<?xml version="1.0"?>

<bindings id="ViewerPaneBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xbl="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<!-- ***************************************************************
     * Inspector 
     *  Interface for a set of viewer panels.
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * REQUIRED IMPORTS:
     *   chrome://inspector/content/ViewerRegistry.js
     *   chrome://inspector/content/jsutil/events/ObserverManager.js
     **************************************************************** -->

  <binding id="panelset" extends="chrome://inspector/content/inspector.xml#base">
    <content>
      <xul:stringbundle src="chrome://inspector/locale/inspector.properties"/>
      <children/>
    </content>

    <implementation>
      <field name="mFocusedDocument">null</field>
      <field name="mFocusedPanel">null</field>
      <field name="mCommandStack">[]</field>
      <field name="mCommandPtr">-1</field>
      <field name="mStringBundle">null</field>

      <property name="registry" readonly="true"
                onget="return this.mRegistry"/>
      <property name="initialized" readonly="true"
                onget="return this.mInitialized"/>
      <property name="focusedPanel" readonly="true" 
                onget="return this.mFocusedPanel;"/>
      <property name="stringBundle" readonly="true"
                onget="return this.mStringBundle;"/>
      
      <method name="initialize">
        <body><![CDATA[
          this.mRegistry = new ViewerRegistry();
          this.mRegistry.load(kViewerRegURL, this);
        ]]></body>
      </method>

      <method name="onEvent">
        <parameter name="aEvent"/>
        <body><![CDATA[
          if (aEvent.type == "viewerChange")
            this.onViewerChange(aEvent);

          // bubble the event up
          this.mObsMan.dispatchEvent(aEvent.type, aEvent);
        ]]></body>
      </method>

      <method name="onViewerRegistryLoad">
        <body><![CDATA[
          var panels = this.panels;
          for (var i = 0; i < panels.length; ++i) {
            panels[i].initialize();
            panels[i].addObserver("viewerChange", this);
            panels[i].addObserver("subjectChange", this);
          }
      
          this.setViewerCmdAttributeOnAll("disabled", "true");
          this.updateAllCommands();
          
          this.mInitialized = true;
          this.mObsMan.dispatchEvent("panelsetready", {});
        ]]></body>
      </method>

      <method name="onViewerRegistryLoadError">
        <body><![CDATA[
          throw "Unable to load the Viewer Registry";
        ]]></body>
      </method>

      <method name="onViewerChange">
        <parameter name="aEvent"/>
        <body><![CDATA[
          if (aEvent.oldViewer)
            // disable all commands for for the old viewer
            this.setViewerCmdAttribute(aEvent.oldViewer.uid,
                                       "disabled", "true");

          if (aEvent.newViewer)
            // enable all commands for for the new viewer
            this.setViewerCmdAttribute(aEvent.newViewer.uid,
                                       "disabled", "false");
        ]]></body>
      </method>

      <property name="panels">
        <getter>
          return this.getElementsByTagName("domi-panel");
        </getter>
      </property>
      
      <property name="panelCount">
        <getter>
          return this.panels.length;
        </getter>
      </property>
      
      <method name="getPanel">
        <parameter name="aIndex"/>
        <body><![CDATA[
          return this.panels[aIndex];
        ]]></body>
      </method>
      
      <!-- ////////////// GLOBAL COMMANDS /////////////////// -->
      
      <method name="updateAllCommands">
        <body><![CDATA[
          var cmds = document.getElementById(this.getAttribute("viewercommandset"));
          if (!cmds) return;
          
          var els = cmds.getElementsByAttribute("global", "true");
          for (var i = 0; i < els.length; i++)
            this.updateCommand(els[i].id);
        ]]></body>
      </method>

      <method name="updateCommand">
        <parameter name="aCommand"/>
        <body><![CDATA[
          var enabled;
          if (aCommand == "cmdEditUndo")
            enabled = this.mCommandPtr >= 0;
          else if (aCommand == "cmdEditRedo") {
            enabled = this.mCommandPtr+1 < this.mCommandStack.length;
            if (!enabled && (this.mCommandPtr > -1)) {
              var lastCmd = this.mCommandStack[this.mCommandPtr];
              enabled = ((lastCmd instanceof nsITransactionManager) && (lastCmd.numberOfRedoItems > 0));
            }
          } else {
            if (this.focusedPanel && this.focusedPanel.viewer) {
              enabled = this.focusedPanel.viewer.isCommandEnabled(aCommand);
            }
          }
          this.setCommandAttribute(aCommand, "disabled", !enabled);
        ]]></body>
      </method>

      <method name="getCommandAttribute">
        <parameter name="aCommand"/>
        <parameter name="aAttribute"/>
        <body><![CDATA[
          var cmd = document.getElementById(aCommand);
          return cmd ? cmd.getAttribute(aAttribute) : null;
        ]]></body>
      </method>

      <method name="setCommandAttribute">
        <parameter name="aCommand"/>
        <parameter name="aAttribute"/>
        <parameter name="aValue"/>
        <body><![CDATA[
          // set attribute on command in our document
          var cmd = document.getElementById(aCommand);
          if (cmd) {
            if (aValue == false || aValue == "false")
              cmd.removeAttribute(aAttribute);
            else
              cmd.setAttribute(aAttribute, aValue);
          }
          
          // set attribute on command in each viewer document
          var panels = this.panels;
          for (var i = 0; i < panels.length; ++i) {
            var doc = panels[i].viewerDocument;
            cmd = doc.getElementById(aCommand);
            if (cmd)
              if (aValue == false || aValue == "false")
                cmd.removeAttribute(aAttribute);
              else
                cmd.setAttribute(aAttribute, aValue);
          }
        ]]></body>
      </method>

      <method name="createTransactionManager">
        <body><![CDATA[
          var txmgr = Components.classes["@mozilla.org/transactionmanager;1"].createInstance(nsITransactionManager); 
          txmgr = txmgr.QueryInterface(nsITransactionManager);
          return txmgr;
        ]]></body>
      </method>

      <!-- ////////////// VIEWER-SPECIFIC COMMANDS /////////////////// -->

      <method name="setViewerCmdAttribute">
        <parameter name="aEntryUid"/>
        <parameter name="aAttr"/>
        <parameter name="aValue"/>
        <body><![CDATA[
          var cmds = document.getElementById(this.getAttribute("viewercommandset"));
          if (!cmds) return;
          
          var els = cmds.getElementsByAttribute("viewer", aEntryUid);
          for (var i = 0; i < els.length; i++) {
            if (els[i].hasAttribute("exclusive"))
              if (aValue == false || aValue == "false")
                els[i].removeAttribute(aAttr);
              else
                els[i].setAttribute(aAttr, aValue);
          }
        ]]></body>
      </method>

      <method name="setViewerCmdAttributeOnAll">
        <parameter name="aAttr"/>
        <parameter name="aValue"/>
        <body><![CDATA[
          var count = this.mRegistry.getEntryCount();
          for (var i = 0; i < count; i++) {
            this.setViewerCmdAttribute(this.mRegistry.getEntryProperty(i, "uid"), aAttr, aValue);
          }
        ]]></body>
      </method>

      <!-- ////////////// COMMAND EXECUTION /////////////////// -->

      <method name="execCommand">
        <parameter name="aCommand"/>
        <body><![CDATA[
          if (aCommand == "cmdEditUndo")
            this.undo();
          else if (aCommand == "cmdEditRedo")
            this.redo();
          else {
            if (!this.focusedPanel)
              return;
            
            // if the command is enabled, execute it
            var viewer = this.focusedPanel.viewer;
            if (!viewer.isCommandEnabled(aCommand)) 
              return;
            var command = viewer.getCommand(aCommand);
            if (!command)
              return;
              
            if (typeof command.txnType == "undefined") {
              var noPush = false;
              //try {
              noPush = command.doCommand();
              //} catch (ex) {
              //  dump("Unable to successfully complete command " + aCommand + "\n");
              //  return;
              //}
      
              if (!noPush) {
                this.addCommandToStack(command);
              }
              return;
            }

            // command.txnType != "undefined"; we have an nsITransaction.
            if (command.isTransient) {
              // command cannot be undone
              command.doTransaction();
              return;
            }

            if (command.txnType == "dialog") {
              // transaction executed from dialog box onAcceptDialog event; open dialog.
              command.openDialog();
              return;
            }

            // otherwise, command is a standard transaction.
            // Pass control to addCommandToStack function; it will handle TxMgr.
            this.addCommandToStack(command);
          }
        ]]></body>
      </method>

      <method name="undo">
        <body><![CDATA[
          if (this.mCommandPtr >= 0) {
            var command = this.mCommandStack[this.mCommandPtr];
            if ((command instanceof nsITransactionManager) && (command.numberOfUndoItems > 0)) {
              command.undoTransaction();
              if (command.numberOfUndoItems == 0) {
                --this.mCommandPtr;
              }
            } else {
              --this.mCommandPtr;
              //try {
                command.undoCommand();
              /*} catch (ex) {
                dump("Unable to successfully undo command.\n");
                return;
              }*/
            }
            
            this.updateCommand("cmdEditUndo");
            this.updateCommand("cmdEditRedo");
          }
        ]]></body>
      </method>

      <method name="redo">
        <body><![CDATA[
          if (this.mCommandPtr == -1) {
            var command = null;
          } else {
            command = this.mCommandStack[this.mCommandPtr];
          }

          if (this.mCommandPtr >= -1) {
            if (!((command instanceof nsITransactionManager)&&(command.numberOfRedoItems > 0))) {
              ++this.mCommandPtr;
              command = this.mCommandStack[this.mCommandPtr];
            }

            if ((command instanceof nsITransactionManager)&&(command.numberOfRedoItems > 0)) {
              command.redoTransaction();
            } else {
              try {
                command.doCommand();
              } catch (ex) {
                dump("Unable to successfully redo command.\n");
                return;
              }
            }
            this.updateCommand("cmdEditUndo");
            this.updateCommand("cmdEditRedo");
          }
        ]]></body>
      </method>

      <method name="addCommandToStack">
        <parameter name="command"/>
        <body><![CDATA[
          if (typeof command.txnType == "undefined") {
            // original Inspector-compatible command, not nsITransaction.
            // add new command to the stack
            ++this.mCommandPtr;
            this.mCommandStack[this.mCommandPtr] = command;

            // get rid of stack entries ahead of the command pointer (if there were any);
            this.mCommandStack.splice(this.mCommandPtr+1, this.mCommandStack.length - (this.mCommandPtr+1));

            this.updateCommand("cmdEditUndo");
            this.updateCommand("cmdEditRedo");
            return;
          }

          // command should be treated as a nsITransaction object.
          if (this.mCommandPtr + 1 < this.mCommandStack.length) {
            var nextCmd = this.mCommandStack[this.mCommandPtr + 1];
            if (nextCmd instanceof nsITransactionManager) {
              var mgr = nextCmd;
              mgr.clear();
              // No two Transaction Manager objects are ever adjacent to each other.
              // We simply use the Transaction Manager immediately following, and clear all transactions from it.
              // This gives us a fresh Transaction Manager.
            }
          }

          var currentCmd = this.mCommandStack[this.mCommandPtr];
          if (currentCmd instanceof nsITransactionManager) {
            mgr = currentCmd;
          }
          // we need a Transaction Manager, even if we create one.
          if (typeof mgr == "undefined") {
            mgr = this.createTransactionManager();
          } 

          mgr.doTransaction(command);

          if (currentCmd != mgr) {
            // insert Transaction Manager into stack
            ++this.mCommandPtr;
            this.mCommandStack[this.mCommandPtr] = mgr;
          }

          // get rid of stack entries ahead of the command pointer (if there were any);
          this.mCommandStack.splice(this.mCommandPtr + 1, this.mCommandStack.length - (this.mCommandPtr + 1));

          this.updateCommand("cmdEditUndo");
          this.updateCommand("cmdEditRedo");
        ]]></body>
      </method>

      <!-- ////////////// CLIPBOARD /////////////////// -->

      <field name="mClipboard">null</field>
      <field name="mClipboardData">null</field>
      <field name="mClipboardFlavor">null</field>
      <field name="mClipboardHelper">null</field>

      <property name="clipboardFlavor" readonly="true"
                onget="return this.mClipboardFlavor"/>

      <property name="clipboard" readonly="true">
        <getter>
        <![CDATA[
          if (!this.mClipboard) {
            var iid = Components.interfaces.nsIClipboard;
            var cid = "@mozilla.org/widget/clipboard;1";
            this.mClipboard = Components.classes[cid].createInstance(iid);
          }
          return this.mClipboard;
        ]]></getter>
      </property>

      <property name="clipboardHelper" readonly="true">
        <getter>
        <![CDATA[
          if (!this.mClipboardHelper) {
            this.mClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
                                              .getService(Components.interfaces.nsIClipboardHelper);
          }
          return this.mClipboardHelper;
        ]]></getter>
      </property>
      
      <method name="setClipboardData">
        <parameter name="aObject"/>
        <parameter name="aFlavor"/>
        <parameter name="aTextRepresentation"/>
        <body><![CDATA[
          // put the text representation on the system clipboard
          if (aTextRepresentation) {            
            this.clipboardHelper.copyString(aTextRepresentation);
          }
          
          // store the real object internally
          this.mClipboardData = aObject;
          this.mClipboardFlavor = aFlavor;
          
          this.updateCommand("cmdEditPaste");
        ]]></body>
      </method>

      <method name="getClipboardData">
        <body><![CDATA[
          return this.mClipboardData;
        ]]></body>
      </method>

      <constructor><![CDATA[
        this.mStringBundle = document.getAnonymousNodes(this)[0];
      ]]></constructor>
    </implementation>
    
    <handlers>
      <handler event="focus" phase="capturing">
      <![CDATA[
        if (!(event.originalTarget instanceof Element))
          return;

        var targetDoc = event.originalTarget.ownerDocument;
        if (targetDoc == this.mFocusedDocument)
          return;
        
        this.mFocusedDocument = targetDoc;
        
        if (targetDoc != this.ownerDocument) {
          var panels = this.panels;
          for (var i = 0; i < panels.length; ++i) {
            if (targetDoc == panels[i].viewerDocument) {
              this.mFocusedPanel = panels[i];
              this.updateAllCommands();
              return;
            }
          }
        }
        
        this.mFocusedPanel = null; // if all else fails
      ]]>
      </handler>
    </handlers>

  </binding>

<!-- ***************************************************************
     * ViewerPane 
     *  Interface for a panel accepts a node and displays all eligible
     *  viewers in a list and activates the selected viewer.
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
     * REQUIRED IMPORTS:
     *   chrome://inspector/content/jsutil/xpcom/XPCU.js
     *   chrome://inspector/content/jsutil/events/ObserverManager.js
     **************************************************************** -->

  <binding id="panel" extends="chrome://inspector/content/inspector.xml#base">
    <content orient="vertical">
      <xul:toolbox class="viewer-pane-toolbox" xbl:inherits="disabled">
        <xul:toolbar class="viewer-pane-header" align="center" xbl:inherits="disabled" xpfe="false">
          <xul:toolbarbutton type="menu" anonid="viewer-list" class="viewer-list" disabled="true"/>
          <xul:label anonid="viewer-title" class="viewer-title label toolbar" flex="1"/>
          <xul:toolbarbutton type="menu" anonid="viewer-menu" class="viewer-menu" disabled="true"/>
        </xul:toolbar>
      </xul:toolbox>
      <xul:box class="viewer-pane-box-1" flex="1">
        <xul:box class="viewer-pane-box-2" flex="1">
          <xul:browser anonid="viewer-iframe" class="viewer-iframe"
                       disablehistory="true" disablesecurity="true" flex="1"/>
        </xul:box>
      </xul:box>
    </content>

    <resources>
      <stylesheet src="chrome://inspector/skin/panelset.css"/>
    </resources>
    
    <implementation>
      <field name="mCurrentEntry">null</field>
      <field name="mCurrentViewer">null</field>
      <field name="mSubject">null</field>
      <field name="mParams">null</field>
      
      <constructor><![CDATA[
        // look for panelset parent
        var p = this.parentNode;
        while (p && p.localName != "domi-panelset")
          p = p.parentNode;
          
        this.mPanelSet = p;
        
        this.setAttribute("disabled", "true");

        this.mIFrameEl = document.getAnonymousElementByAttribute(this, "anonid", "viewer-iframe");
      ]]></constructor>
    
      <destructor><![CDATA[
        if (this.mCurrentViewer)
          this.destroyViewer();
      ]]></destructor>
      
      <property name="panelset"
                onget="return this.mPanelSet;"/>

      <property name="viewer"
                onget="return this.mCurrentViewer;"/>

      <property name="subject"
                onget="return this.mSubject;"
                onset="this.setSubject(val);"/>

      <property name="params"
                onget="return this.mParams;"
                onset="return this.mParams = val;"/>

      <property name="title"
                onget="return this.getAttribute('title');"
                onset="return this.setAttribute('title', val);"/>
                            
      <property name="registry" 
                onget="return this.panelset.registry;"/>

      <property name="viewerDocument" 
                onget="return this.mIFrameEl.contentDocument;"/>

      <property name="linkedPanel">
        <getter>return this.mLinkedPanel;</getter>
        <setter>
          if (this.mLinkedPanel)
            this.mLinkedPanel.removeObserver("viewerChange", this);
            
          this.mLinkedPanel = val;

          if (val)
            val.addObserver("viewerChange", this);
        </setter>
      </property>

      <field name="mLinkedPanel">null</field>
      <field name="mLinkedViewer">null</field>
      <field name="mCurrentViewer">null</field>
      <field name="mCurrentEntry">null</field>
      
      <method name="initialize">
        <body><![CDATA[
          this.mListEl = document.getAnonymousElementByAttribute(this, "anonid", "viewer-list");
          this.mTitleEl = document.getAnonymousElementByAttribute(this, "anonid", "viewer-title");
          this.mMenuEl = document.getAnonymousElementByAttribute(this, "anonid", "viewer-menu");
          
          if (this.hasAttribute("linkedpanel"))
            this.linkedPanel = document.getElementById(this.getAttribute("linkedpanel"));
      
          this.fillViewerList();
        ]]></body>
      </method>

      <method name="notifyViewerReady">
        <parameter name="aViewer"/>
        <body><![CDATA[
          var old = this.mCurrentViewer;
          this.mCurrentViewer = aViewer;
          var oldEntry = this.mCurrentEntry;
          this.mCurrentEntry = this.mPendingEntry;

          if (aViewer) {
            this.registry.cacheViewer(aViewer, this.mCurrentEntry);
            var title = this.registry.getEntryProperty(this.mCurrentEntry, "description");
            this.setTitle(title);
          }  else {
            this.setTitle(null);
          }

          this.rebuildViewerContextMenu();
          
          // listen for subjectChange so we can bubble it
          if (old)
            old.removeObserver("subjectChange", this);
          
          if (aViewer)
            aViewer.addObserver("subjectChange", this);

          this.mObsMan.dispatchEvent("viewerChange", 
            { viewerPane: this, oldViewer: old, newViewer: aViewer });
              
          if (aViewer) {
            aViewer.subject = this.mSubject;
            // only give initial focus to panes that are not on the right side of a link
            if (!aViewer.pane.hasAttribute("linkedpanel")) {
              var doc = aViewer.pane.viewerDocument;
              doc.commandDispatcher.advanceFocusIntoSubtree(doc.documentElement);
            }
          }
        ]]></body>
      </method>

      <method name="destroyViewer">
        <body><![CDATA[
          this.mCurrentViewer.destroy();
          this.registry.uncacheViewer(this.mCurrentViewer);
        ]]></body>
      </method>
      
      <!-- ////////////// UI Commands /////////////////-->

  <!-- ////////////////////////////////////////////////////////////////////////////
      // Sets the new view to the item just selected from the "viewer list"
      //////////////////////////////////////////////////////////////////////// -->

      <method name="onViewerListCommand">
        <parameter name="aItem"/>
        <body><![CDATA[
          this.switchViewer(parseInt(aItem.getAttribute("viewerListEntry")));
        ]]></body>
      </method>

  <!-- ////////////////////////////////////////////////////////////////////////////
      // Prepares the list of viewers for a node, rebuilds the menulist to display
      // them, and load the default viewer for the node.
      //
      // @param Object aObject - the object to begin viewing
      //////////////////////////////////////////////////////////////////////// -->

      <method name="setSubject">
        <parameter name="aObject"/>
        <body><![CDATA[
          this.mSubject = aObject;
          this.mParams = null;
          
          // get the list of viewers which match the node
          var entries = this.registry.findViewersForObject(aObject, this.id,
                                                           this.mLinkedViewer);
          this.rebuildViewerList(entries);

          if (entries.length == 0) {
            this.switchViewer(-1);
            this.setAttribute("disabled", "true");
          } else if (!this.entryInList(this.mCurrentEntry, entries)) {
            this.switchViewer(entries[0]);
            this.removeAttribute("disabled");
          } else {
            this.mCurrentViewer.subject = aObject;
            this.removeAttribute("disabled");
          }
  
        ]]></body>
      </method>

  <!-- ////////////////////////////////////////////////////////////////////////////
      // Clear out and rebuild the menulist full of the available views
      // for the currently selected node.
      //
      // @param Array aEntries - an array of entries from the viewer registry
      //////////////////////////////////////////////////////////////////////// -->

      <method name="rebuildViewerList">
        <parameter name="aEntries"/>
        <body><![CDATA[
          var mpp = this.mListElPopup;
          
          if (aEntries.length <= 0)
            this.mListEl.setAttribute("disabled", true);
          else
            this.mListEl.removeAttribute("disabled");
      
          // empty the list
          while (mpp.childNodes.length)
            mpp.removeChild(mpp.childNodes[0]);
          
          for (var i = 0; i < aEntries.length; i++) {
            var entry = aEntries[i];
            var menuitem = document.createElement("menuitem");
            menuitem.setAttribute("label", this.registry.getEntryProperty(entry, "description"));
            menuitem.setAttribute("viewerListEntry", entry);
            mpp.appendChild(menuitem);
          }
        ]]></body>
      </method>
  
  <!-- ////////////////////////////////////////////////////////////////////////////
      // Loads the viewer described by an entry in the viewer registry.
      //
      // @param nsIRDFNode aEntry - entry in the viewer registry
      //////////////////////////////////////////////////////////////////////// -->

      <method name="switchViewer">
        <parameter name="aEntry"/>
        <body><![CDATA[
          if (aEntry < 0) { // -1 is for null viewer
            this.mPendingEntry = -1;
            this.notifyViewerReady(null);
            return;
          }

          var url = this.registry.getEntryURL(aEntry);

          var loadNew = true;
          if (this.mCurrentViewer) {
            var oldURL = this.registry.getEntryURL(this.mCurrentEntry);
            if (oldURL == url) {
              loadNew = false;
            } 
          }
          
          if (loadNew) {
            this.mPendingEntry = aEntry;
            this.loadViewerURL(url);
          }
        ]]></body>
      </method>
  
  <!-- ////////////////////////////////////////////////////////////////////////////
       // Begin loading a new viewer from a given url.
       //
       // @param String aURL - the url of the viewer document
      //////////////////////////////////////////////////////////////////////// -->

      <method name="loadViewerURL">
        <parameter name="aURL"/>
        <body><![CDATA[
          if (this.mCurrentViewer) {
            // tell the old viewer it's about to go away
            this.destroyViewer();
          }
          
          // load the new document
          FrameExchange.loadURL(this.mIFrameEl, aURL, this);
        ]]></body>
      </method>
  
  <!-- ////////////////////////////////////////////////////////////////////////////
       // Rebuild the viewer context menu
      //////////////////////////////////////////////////////////////////////// -->

      <method name="rebuildViewerContextMenu">
        <body><![CDATA[
          if (!this.mSubject) {
            this.mMenuEl.setAttribute("disabled", "true");
            return;
          } else {
            this.mMenuEl.removeAttribute("disabled");
          }
          
          // remove old context menu
          if (this.mContextMenu) {
            this.mMenuEl.removeChild(this.mContextMenu);
            this.mFormerContextParent.appendChild(this.mContextMenu);
          }
      
          var uid = this.registry.getEntryProperty(this.mCurrentEntry, "uid");
          var ppId = "ppViewerContext-" + uid;
          var pp = document.getElementById(ppId);
          if (pp) {
            this.mMenuEl.removeAttribute("disabled");
            var parent = pp.parentNode;
            parent.removeChild(pp);
            this.mMenuEl.appendChild(pp);
      
           this.mFormerContextParent = parent;
          } else {
            this.mMenuEl.setAttribute("disabled", "true");
          }
          this.mContextMenu = pp;
        ]]></body>
      </method>

  <!-- ////////////////////////////////////////////////////////////////////////////
      // Check to see if an entry exists in an arry of entries
      //
      // @param nsIRDFResource aEntry - the entry being searched for
      // @param Array aList - array of entries
      //////////////////////////////////////////////////////////////////////// -->

      <method name="entryInList">
        <parameter name="aEntry"/>
        <parameter name="aList"/>
        <body><![CDATA[
          for (var i in aList) {
            if (aList[i] == aEntry) return true;
          }
      
          return false;
        ]]></body>
      </method>

  <!-- ////////////////////////////////////////////////////////////////////////////
      // Set the text in the viewer title bar
      //
      // @param String title - the text to use
      //////////////////////////////////////////////////////////////////////// -->

      <method name="setTitle">
        <parameter name="aTitle"/>
        <body><![CDATA[
          this.mTitleEl.setAttribute("value", this.title && aTitle ? this.title + " - " + aTitle : "");
        ]]></body>
      </method>
      
  <!-- ////////////////////////////////////////////////////////////////////////////
      // Fill out the content of the "viewer list" menu
      //////////////////////////////////////////////////////////////////////// -->

      <method name="fillViewerList">
        <body><![CDATA[
          this.mListEl.pViewer = this;
          this.mListEl.setAttribute("oncommand", "this.pViewer.onViewerListCommand(event.originalTarget)");
      
          var mpp = document.createElement("menupopup");
          this.mListEl.appendChild(mpp);
          this.mListElPopup = mpp;
        ]]></body>
      </method>
      
  <!-- ////////////////////////////////////////////////////////////////////////////
      // Listen for selection changes on the viewer in the linked pane
      //////////////////////////////////////////////////////////////////////// -->

      <method name="observerLinkedViewer">
        <body><![CDATA[
          if (this.mLinkedViewer)
            this.mLinkedViewer.removeObserver("selectionChange", this);
            
          this.mLinkedViewer = this.mLinkedPanel.viewer;
          this.mLinkedViewer.addObserver("selectionChange", this);
        ]]></body>
      </method>
      
  <!-- ////////////////////////////////////////////////////////////////////////////
      // Update this pane's subject to be the selection of it's linked pane
      //////////////////////////////////////////////////////////////////////// -->

      <method name="updateLinkedSubject">
        <body><![CDATA[
          this.subject = this.mLinkedViewer.selection;
        ]]></body>
      </method>
      
  <!-- ////////////////////////////////////////////////////////////////////////////
      // global event handler
      //////////////////////////////////////////////////////////////////////// -->

      <method name="onEvent">
        <parameter name="aEvent"/>
        <body><![CDATA[
          switch (aEvent.type) {
            case "viewerChange":
              this.observerLinkedViewer();
              break;
            case "selectionChange":
              this.updateLinkedSubject();
              break;
            case "subjectChange":
              this.mObsMan.dispatchEvent("subjectChange", aEvent);
          };
        ]]></body>
      </method>

    </implementation>
    
  </binding>

  <binding id="base">
    <implementation>
      <constructor><![CDATA[
        this.mObsMan = new ObserverManager(this);
      ]]></constructor>
      
      <!-- ////////////// Event Handling /////////////////-->

      <method name="addObserver">
        <parameter name="aEvent"/>
        <parameter name="aObserver"/>
        <body><![CDATA[
          this.mObsMan.addObserver(aEvent, aObserver);
        ]]></body>
      </method>
      
      <method name="removeObserver">
        <parameter name="aEvent"/>
        <parameter name="aObserver"/>
        <body><![CDATA[
          this.mObsMan.removeObserver(aEvent, aObserver);
        ]]></body>
      </method>
      
    </implementation>
    
  </binding>

</bindings>
