/* ==================================================================

FILE:   System.js

DESCR:  System class for Netscape Help implementation.

NOTES:  

================================================================== */

// Dev. switches.

var TRACE           = false

var ASSERT          = true

var ERR_DLGS        = true

var ERRS_TO_CONSOLE = false



// "Constants."

var VERSION         = "2.0-970514"

var LAYERED_TOPICS  = false

var JAVA_DEPENDENT  = false

var STAMP_CONSOLE   = false

var OK_NAV_VERSIONS = ".4."

var CHECK_HREFS     = false

var CONTENTS        = "CntTool.htm"

var INDEX           = "IdxTool.htm"

var SEARCH          = "SrhTool.htm"

var NAVUI           = "NavUI.htm"

var TOOLUI          = "ToolUI.htm"

var STATUS          = "Status.htm"

var MESSAGE_BGCOLOR = "#fafad2"

var TOOL_BGCOLOR    = "#99ccff"



//trace( "System.js" )



// Runtime error handling.

// window.onerror = errHandler



// Version stamp.

if ( STAMP_CONSOLE && navigator.javaEnabled() ) {

   java.lang.System.out.println( "System.js, version " + VERSION )

   java.lang.System.out.println( "Client: " + navigator.appVersion )

}



/*

DESCR:   Handles the onhelp event.

PARAMS:  URL  The URL to load.

RETURNS: 

NOTES:   

*/

function onHelpHandler( URL )

{

   //trace( "onHelpHandler(" + URL + ")" )

   //alert( "onHelpHandler(" + URL + ")" )



   systemObj.loadTopic( URL, false )

}



/*

DESCR:   Traces execution to the console.

PARAMS:  msg  The trace text.

RETURNS: 

NOTES:   

*/

function trace( msg )

{

   if ( TRACE ) java.lang.System.out.println( "System: " + msg )

}



// Create system object.

var systemObj = new system( JAVA_DEPENDENT, OK_NAV_VERSIONS )

assert( ( typeof( systemObj ) == "object" ), SYS_CONSTRUCT )



/*

DESCR:   Help implementation system class.

PARAMS:  bJavaDependent  Pass true if the system is Java dependent,

                         false if not.

         OKNavVersions   String representing valid versions of Navigator.

RETURNS: 

NOTES:   

*/

function system( bJavaDependent, OKNavVersions )

{

   //trace( "system constructor" )



   this.backStack    = new stack()

   this.forwardStack = new stack()



   this.navVersion

   this.topic

   this.contents



   //this.aSysSecWnds = new Array()  // Sys-owned secondary windows.



   this.currentTool       = CONTENTS  // Set default tool.

   this.bIdxTitlesPageUp  = false

   this.currentSubsystem  = ""

   this.visibleLayerIdx   = ""



   this.bToolUIloaded   = false

   this.bNavUIloaded    = false

   this.bContentsLoaded = false

   this.bIndexLoaded    = false

   this.bStarted        = false



   this.bNewSubsystem = false



   this.preproc           = preproc

   this.componentCallback = componentCallback

   this.idxTitlesUp       = idxTitlesUp

   this.loadTopic         = loadTopic

   this.newSubsystem      = newSubsystem

   this.exit              = exit

   this.print             = print

   this.find              = find

   this.back              = back

   this.forward           = forward

   //this.openSecWnd        = openSecWnd

   this.loadTool          = loadTool

   this.setToolButton     = setToolButton

   this.msg               = msg

   this.manageLayers      = manageLayers

   this.badURLerror       = badURLerror



   this.componentsReadyEvt = componentsReadyEvt

   this.loadEvt            = loadEvt

   this.unloadEvt          = unloadEvt



   this.stashPersistentData  = stashPersistentData

   this.snatchPersistentData = snatchPersistentData



   // Preprocessing.

   if ( !this.preproc( bJavaDependent, OKNavVersions ) ) {

      return

   }



   // Load components.

   top.StatusFrame.location.replace( STATUS )

   top.ToolUIFrame.location.replace( TOOLUI )

   top.NavFrame.location.replace( NAVUI )

   top.ToolFrame.location.replace( this.currentTool )

}



   /*

   DESCR:   Pre-Help processing.

   PARAMS:  bJavaDependent  Pass true if the system is Java dependent,

                            false if not.

            OKNavVersions   String representing valid versions of Navigator.

   RETURNS: 

   NOTES:   

   */

   function preproc( bJavaDependent, OKNavVersions )

   {

      //trace( "preproc()" )

      

      // Make sure Java is enabled if needed. Otherwise, abort.

      if ( bJavaDependent && !navigator.javaEnabled() ) {

         this.msg( NO_JAVA_ERR_MSG, "" )

         return false

      }



      // Validate version of navigator. Render in ".integer." form.

      var version = navigator.appVersion

      this.navVersion =

         "." + version.substring( 0, ( version.indexOf( "." ) + 1 ) )

      if ( OKNavVersions.indexOf( this.navVersion ) == -1 ) {

         this.msg( WRONG_NAV_VER_ERR_MSG, "" )

         return false

      }



      return true

   }



   /*

   DESCR:   Tracks component loading.

   PARAMS:  componentName  The document-name of the component.

   RETURNS: 

   NOTES:   Components call this method on load. Triggers ready

            event when all loaded.

   */

   function componentCallback( componentName )

   {

      //trace( "componentCallback(" + componentName + ")" )

      //alert( "componentCallback(" + componentName + ")" )



      // Track component loading.



      // Note: for some reason, can't write to status frame until Contents

      // is loaded, so we do this later.

      //if ( componentName == STATUS ) top.StatusFrame.setStatusWait()



      if ( componentName == TOOLUI ) this.bToolUIloaded = true



      if ( componentName == NAVUI ) this.bNavUIloaded = true



      if ( componentName == CONTENTS ) {



         // Keep house.

         top.StatusFrame.setStatusWait()

         top.ToolUIFrame.bDisableBtnBar = false

         this.bContentsLoaded = true

         this.bIndexLoaded = false



         // Create a Contents object.

         this.contents = new top.ToolFrame.contents( TOOL_BGCOLOR )

         assert( ( typeof( this.contents ) == "object" ), CNT_CONSTRUCT )



         // If this is not a system load and we're just changing to the

         // Contents tool, set the Content's dataset and update. Otherwise,

         // this will happen when a topic is loaded.

         if ( this.bStarted ) {

           this.contents.setDataset( this.currentSubsystem )

           this.contents.updateTree( this.topic.URL )



           top.StatusFrame.setStatusClear()

         }

      }



      if ( componentName == INDEX ) {

         top.ToolUIFrame.bDisableBtnBar = false

         this.bIndexLoaded = true

         this.bContentsLoaded = false



         if ( this.bStarted ) top.StatusFrame.setStatusClear()

      }



      // Set the blender button down for the current tool.

      if ( componentName == TOOLUI ) {

         this.setToolButton( ( this.currentTool == CONTENTS ? 0 : 1 ), 1 )

      }



      // Get moving if we're ready, but not already underway.

      if ( !this.bStarted &&

           this.bToolUIloaded && this.bNavUIloaded &&

           ( this.bContentsLoaded || this.bIndexLoaded ) ) {

         this.bStarted = true

         this.componentsReadyEvt()   

      }

   }



   /*

   DESCR:   Handles components ready "event."

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function componentsReadyEvt()

   {

      //trace( "componentsReadyEvt()" )



      // Set onhelp handler.

      top.setOnhelpHandler( "onHelpHandler" )

   }



   /*

   DESCR:   Enables external state-setting of the tool UI button bar.

   PARAMS:  buttonIdx  Identifies the button on the bar.

            newState   The state to set.

   RETURNS: 

   NOTES:   

   */

   function setToolButton( buttonIdx, newState )

   {

      //trace( "setToolButton" )



      assert( defined( top.ToolUIFrame.btnBar ), TOOLUI )

      if ( defined( top.ToolUIFrame.btnBar ) ) {



         // Set.

         with ( top.ToolUIFrame.btnBar.aButtons[ buttonIdx ] ) {

            setState( newState )

            bOn = true

         }

      }

   }



   /*

   DESCR:   onload handler.

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function loadEvt()

   {

      //trace( "loadEvt()" )

      //alert( "loadEvt()" )



      // Snatch important data for persistence on reload due to window resizing.

      this.snatchPersistentData()

   }



   /*

   DESCR:   onunload handler.

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function unloadEvt()

   {

      //trace( "unloadEvt()" )

      //alert( "unloadEvt()" )



      // Stash important data for persistence on reload due to window resizing.

      this.stashPersistentData()



      // Clean up topic (topic-owned secondary windows, etc.).

      if ( defined( this.topic ) ) this.topic.destruct()



      // Close any system-owned secondary windows.

      //for ( var i = 0; i < this.aSysSecWnds.length; i++ ) {

      //   if ( defined( this.aSysSecWnds[ i ] ) ) this.aSysSecWnds[ i ].close()

      //}

   }



   /*

   DESCR:   Stores data needed to maintain state on reload due to resize.

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function stashPersistentData()

   {

      //trace( "stashPersistentData" )



      if ( defined( top.aPersistentData ) ) {

         var i = 0

         top.aPersistentData[ i++ ] = this.currentTool

         top.aPersistentData[ i++ ] = this.forwardStack

         top.aPersistentData[ i++ ] = this.backStack

         //this.topic

         //this.visibleLayerIdx

      }

   }



   /*

   DESCR:   Retrieves data needed to maintain state on reload due to resize.

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function snatchPersistentData()

   {

      //trace( "snatchPersistentData" )



      if ( defined( top.aPersistentData[ 0 ] ) ) {

         var i = 0

         this.currentTool  = top.aPersistentData[ i++ ]

         this.forwardStack = top.aPersistentData[ i++ ] 

         this.backStack    = top.aPersistentData[ i++ ]

      }

   }



   /*

   DESCR:   Loads the Contents, Index, or Find tool.

   PARAMS:  toolName  The document-name of the tool.

   RETURNS: 

   NOTES:   

   */

   function loadTool( toolName )

   {

      //trace( "loadTool(" + toolName + ")" )



      assert( ( toolName == CONTENTS ||

                toolName == INDEX ||

                toolName == SEARCH ), TOOLNAME )



      this.currentTool = toolName



      // Contents tool.

      if ( toolName == CONTENTS && !this.bContentsLoaded ) {



         // Keep house.

         top.StatusFrame.setStatusWait()

         top.ToolUIFrame.bDisableBtnBar = true



         top.ToolFrame.location.replace( toolName )

      }



      // Index tool.

      else if ( toolName == INDEX && !this.bIndexLoaded ) {



         // Keep house.

         top.StatusFrame.setStatusWait()

         top.ToolUIFrame.bDisableBtnBar = true



         top.ToolFrame.location.replace( toolName )

      }



      // Search tool.

      else if ( toolName == SEARCH ) {



         // We are using native find functionality in lieu of Search tool.

         // Be sure to invoke Find against the topic frame.

         top.TopicFrame.find()

      }

   }



   /*

   DESCR:   Closes the Help window.

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function exit()

   {

      //trace( "exit()" )



      // Close frameset.

      top.close()

   }



   /*

   DESCR:   Moves back in Help history.

   PARAMS:  

   RETURNS: 

   NOTES:   Presumes button is disabled on empty stack.

   */

   function back()

   {

      //trace( "back()" )



      // Since the Index's titles page may be in the topic frame, but not

      // part of history, we don't really wan't to go Back. Just restore

      // the current topic without adding it to history.

      if ( this.bIdxTitlesPageUp ) {

         this.loadTopic( this.topic.URL, false )

         return

      }



      assert( !this.backStack.isEmpty(), BACK_STACK )

      this.forwardStack.push( this.topic.URL )

      this.loadTopic( this.backStack.pop(), true )

   }



   /*

   DESCR:   Moves forward in Help history.

   PARAMS:  

   RETURNS: 

   NOTES:   Presumes button is disabled on empty stack.

   */

   function forward()

   {

      //trace( "forward()" )



      assert( !this.forwardStack.isEmpty(), FORWARD_STACK )

      this.backStack.push( this.topic.URL )

      this.loadTopic( this.forwardStack.pop(), true )

   }



   /*

   DESCR:   Tracks the Index's topic titles page in the topic window.

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function idxTitlesUp()

   {

      //trace ( "idxTitlesUp()" )

      

      this.bIdxTitlesPageUp = true



      // The Index titles page is not part of the topics history, so make

      // sure that Forward is disabled and Back is enabled.

      assert( defined( top.NavFrame.btnBar ), NAV_BAR )

      if ( defined( top.NavFrame.btnBar ) ) {

         top.NavFrame.btnBar.aButtons[ 0 ].enable( true )

         top.NavFrame.btnBar.aButtons[ 1 ].enable( false )

      }

   }



   /*

   DESCR:   Handles tasks when the Help subsystem changes.

   PARAMS:  newSubsystem  A simple filename representing the subsystem

                          (the topic filename).

   RETURNS: 

   NOTES:   

   */

   function newSubsystem( newSubsystem )

   {

      //trace( "newSubsystem('" + newSubsystem + "')" )

      //alert( "newSubsystem('" + newSubsystem + "')" )



      assert( ( newSubsystem.toLowerCase().indexOf( ".htm" ) > -1 ),

              SUBSYS_VALUE + newSubsystem )



      // Track the current subsystem.

      this.currentSubsystem = newSubsystem



      // Reinitialize the visible layer index, since nothing is visible yet.

      this.visibleLayerIdx = ""



      // Load the new header.

      top.HeaderFrame.location.replace( this.topic.headerURL )



      // Set the Contents tool's dataset.

      if ( this.currentTool == CONTENTS ) {

         this.contents.setDataset( newSubsystem )

      }

   }



   /*

   DESCR:   Loads a topic.

   PARAMS:  URL           The non-nethelp version of the URL.

            bHistoryLoad  true if this is a load due to Back or Forward;

                          false, otherwise.

   RETURNS: 

   NOTES:   

   */

   function loadTopic( URL, bHistoryLoad )

   {

      //trace( "loadTopic(" + URL + ")" )



      assert( ( URL.toLowerCase().indexOf( ".htm" ) > -1 ), URL_VALUE + URL )



      // Update Index titles page flag. If we are loading a topic, it can't

      // be the URL in the topic window.

      this.bIdxTitlesPageUp = false



      // Keep house if there is an existing topic...

      if ( defined( this.topic ) ) {



         // Track as history, if this is _not_ a history load, and if we are

         // not reloading the same URL for some (good) reason. Note that

         // forward stack should be cleared.

         if ( !bHistoryLoad && ( this.topic.URL != URL ) ) {

            this.backStack.push( this.topic.URL )

            this.forwardStack.clear()

         }



         // "Destruct" the current topic.

         this.topic.destruct()

      } 



      // See if we need to change subsystems.

      var bNewSubsystem =

         ( makeSimpleFilename( URL ) != this.currentSubsystem ? true : false )



      // If we're loading a new topic file, set the wait indicator.

      if ( bNewSubsystem ) top.StatusFrame.setStatusWait()



      // Create new topic object. Must happen before updating subsystem.

      this.topic = new helpTopic( URL )

      assert( ( typeof( this.topic ) == "object" ), TOPIC_CONSTRUCT )



      // Update current subsystem. Must happen before updating Contents tree

      // or managing layers.

      if ( bNewSubsystem ) this.newSubsystem( makeSimpleFilename( URL ) )



      // Enable/disable history buttons.

      assert( defined( top.NavFrame.btnBar ), NAV_BAR )

      if ( defined( top.NavFrame.btnBar ) ) {

         var bEnable

         bEnable = ( this.backStack.isEmpty() ? false : true )

         top.NavFrame.btnBar.aButtons[ 0 ].enable( bEnable )

         bEnable = ( this.forwardStack.isEmpty() ? false : true )

         top.NavFrame.btnBar.aButtons[ 1 ].enable( bEnable )

      }



      // Update Contents tree.

      if ( this.currentTool == CONTENTS ) this.contents.updateTree( URL )



      // Layers are managed on topic file load (to ensure the layers exist),

      // but since an anchor "load" in the current URL generates no load

      // event, we need to call manageLayers() here under such conditions.

      if ( LAYERED_TOPICS && !this.bNewSubsystem ) this.manageLayers()

   }



   /*

   DESCR:   Manages layers if layered topics is turned on.

   PARAMS:  

   RETURNS: 

   NOTES:   This is called from the topic onload handler, to be sure the

            document is loaded, or it is called from loadTopic() if the

            subsystem has not changed, since such a load is via a named

            anchor and does not raise an onload event. 

   */

   function manageLayers()

   {

      //trace( "manageLayers()" )



      if ( !LAYERED_TOPICS ) return



      // Bail if there are no layered topics.

      if ( !defined( top.TopicFrame.document.layers[ 0 ] ) ) return



      // Hide any currently visible topic.

      if ( this.visibleLayerIdx != "" ) {

         top.TopicFrame.document.layers[ this.visibleLayerIdx ].hidden = true

      }



      // Unhide the topic layer.

      top.TopicFrame.document.layers[ this.topic.fragmentSpec ].hidden = false



      // Stash this index.

      this.visibleLayerIdx = this.topic.fragmentSpec

   }



   /*

   DESCR:   Creates an always-on-top message box with an OK button.

   PARAMS:  msgText     The message text.

            msgCaption  The window caption.

   RETURNS: 

   NOTES:   The message box is asynchronous, except that the OK button's

            window.close() will not execute until the JS in the calling

            window executes. Note that parser can't hack whitespace in

            third parm of open().

   */

   function msg( msgText, msgCaption )

   {

      //trace( "msg(" + msgText + ")" )



      if ( msgCaption == "" ) msgCaption = DEFAULT_MESSAGE_CAPTION



      var msgDlg =

         window.open( "", "",

                      "SCROLLBARS=no,WIDTH=350,HEIGHT=200,SCROLLBARS=yes,ALWAYSRAISED=yes,DEPENDENT=yes,TITLEBAR=no,MENUBAR=no,HOTKEYS=no" )



      var html

      html = "<HEAD><TITLE>" + msgCaption +

             "</TITLE></HEAD><BODY BGCOLOR = " + MESSAGE_BGCOLOR + ">"

      html += msgText

      html += "<BR><BR><BR><BR>"

      html += "<CENTER><FORM METHOD = POST>"

      html += "<INPUT TYPE = button NAME = 'OK' VALUE = '   " +

              OK_BTN_LABEL +

              "   ' ONCLICK = 'window.close()'></FORM></CENTER></BODY>"



      msgDlg.document.write( html )

      msgDlg.document.close()

   }



   /*

   DESCR:   Creates a secondary content window.

   PARAMS:  URL         The content URL.

            name        The window name.

            features    The feature string.

            bSystem     true if the window should be system-owned (i.e.,

                        window will not be closed when parent topic

                        closes, only when the system shuts down);

                        false, if topic-owned.

            bReturnObj  true if the window object should be returned.

   RETURNS: Optionally, the window object.

   NOTES:   

   */

   /*

   function openSecWnd( URL, name, features, bSystem, bReturnObj )

   {

      //trace( "openSecWnd(" + URL + ")" )



      // Create a system- or topic-owned seconadary window.

      var newWnd

      var bAlreadyExists = false

      if ( bSystem ) {

         newWnd = window.open( URL, name, features )



         // Add window to list only if it is a new window (user may create

         // a secondary more than once).

         for ( var i = 0; i < this.aSysSecWnds.length; i++ ) {

            if ( this.aSysSecWnds[ i ] == newWnd ) {

               if ( bReturnObj ) return newWnd

               else bAlreadyExists = true

            }

         }

         if ( !bAlreadyExists ) { 

            this.aSysSecWnds[ this.aSysSecWnds.length ] = newWnd

            if ( bReturnObj ) return newWnd

         }

      }

      else {

         newWnd = this.topic.addSecondary( URL, name, features )

         if ( bReturnObj ) return newWnd

      }

   }

   */



   /*

   DESCR:   Creates a bad URL type error.

   PARAMS:  href  The offending href.

   RETURNS: 

   NOTES:   

   */

   function badURLerror( href )

   {

      this.msg( BAD_HREF_ERR_MSG + "<P>" + href, "" )

   }



   /*

   DESCR:   Prints the content in the topic frame.

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function print()

   {

      //trace( "print()" )



      top.TopicFrame.print()

   }



// End class definition: system.



/*

DESCR:   Help topic class.

PARAMS:  URL  The non-nethelp form of the URL.

RETURNS: 

NOTES:   

*/

function helpTopic( URL )

{

   //trace( "helpTopic constructor" )



   assert( ( URL.toLowerCase().indexOf( ".htm" ) > -1 ),

           TOPIC_URL_VALUE + URL )



   this.headerURL



   this.URL = URL



   this.fragmentSpec = URL.substring( URL.lastIndexOf( "#" ) + 1 )

   assert( this.fragmentSpec != "", FRAG_SPEC )



   //this.aSecWnds = new Array()  // Secondary windows.



   this.destruct         = destruct

   //this.addSecondary     = addSecondary

   //this.closeSecondaries = closeSecondaries



   // Determine header URL by naming convention.

   var extPos

   extentionPos = this.URL.indexOf( ".htm" )

   if ( extentionPos == -1 ) extentionPos = this.URL.indexOf( ".HTM" )

   assert( ( extentionPos != -1 ), TOPIC_FILENAME )

   this.headerURL = this.URL.substring( 0, extentionPos ) + "Hdr.htm"



   // Load URL in the topic frame.

   top.TopicFrame.location.replace( this.URL )

}



   /*

   DESCR:   Adds a secondary window.

   PARAMS:  URL       The content URL.

            name      The window name.

            features  The feature string.

   RETURNS: 

   NOTES:   

   */

   /*

   function addSecondary( URL, name, features )

   {

      //trace( "addSecondary()" )



      var newWnd = window.open( URL, name, features )

      

      // Add window to list only if it is a new window (user may

      // create a secondary more than once).

      for ( var i = 0; i < this.aSecWnds.length; i++ ) {

         if ( this.aSecWnds[ i ] == newWnd ) return newWnd

      }

      this.aSecWnds[ this.aSecWnds.length ] = newWnd



      return newWnd

   }

   */



   /*

   DESCR:   Closes all secondary windows.

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   /*

   function closeSecondaries()

   {

      //trace( "closeSecondaries()" )



      for ( var i = 0; i < this.aSecWnds.length; i++ ) {

         if ( defined( this.aSecWnds[ i ] ) ) this.aSecWnds[ i ].close()

      }

   }

   */

   

   /*

   DESCR:   "Destructor."

   PARAMS:  

   RETURNS: 

   NOTES:   

   */

   function destruct()

   {

      //trace( "helpTopic destruct()" )



      // Close any secondary windows owned by current topic.

      //this.closeSecondaries()

   }



// End class definition: helpTopic.



/*

DESCR:   Popup class.

PARAMS:  

RETURNS: 

NOTES:   

*/



// End class definition: popup.

