<?xml version="1.0"?>

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

  <binding id="toolbar-base">
    <resources>
      <stylesheet src="chrome://global/skin/toolbar.css"/>
    </resources>
  </binding>
  
  <binding id="toolbox" extends="chrome://global/content/bindings/toolbar.xml#toolbar-base">
    <implementation>
      <field name="palette">
        null
      </field>

      <field name="toolbarset">
        null
      </field>
      
      <field name="customToolbarCount">
        0
      </field>
      
      <!-- Set by customizeToolbar.js -->
      <property name="customizing">
        <getter><![CDATA[
          return this.getAttribute("customizing") == "true";
        ]]></getter>
        <setter><![CDATA[
          if (val)
            this.setAttribute("customizing", "true");
          else
            this.removeAttribute("customizing");
          return val;
        ]]></setter>
      </property>

      <constructor>
        <![CDATA[
          // Look to see if there is a toolbarset.
          this.toolbarset = this.firstChild;
          while (this.toolbarset && this.toolbarset.localName != "toolbarset")
            this.toolbarset = toolbarset.nextSibling;
          
          if (this.toolbarset) {
            // Create each toolbar described by the toolbarset.
            var index = 0;
            while (toolbarset.hasAttribute("toolbar"+(++index))) {
              var toolbarInfo = toolbarset.getAttribute("toolbar"+index);
              var infoSplit = toolbarInfo.split(":");
              this.appendCustomToolbar(infoSplit[0], infoSplit[1]);
            }
          }
        ]]>
      </constructor>
      
      <method name="appendCustomToolbar">
        <parameter name="aName"/>
        <parameter name="aCurrentSet"/>
        <body>
          <![CDATA[            
            var toolbar = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                                  "toolbar");
            toolbar.id = "__customToolbar_" + aName.replace(" ", "_");
            toolbar.setAttribute("customizable", "true");
            toolbar.setAttribute("customindex", ++this.customToolbarCount);
            toolbar.setAttribute("toolbarname", aName);
            toolbar.setAttribute("currentset", aCurrentSet);
            toolbar.setAttribute("mode", this.getAttribute("mode"));
            toolbar.setAttribute("iconsize", this.getAttribute("iconsize"));
            toolbar.setAttribute("context", this.toolbarset.getAttribute("context"));
            toolbar.setAttribute("class", "chromeclass-toolbar");
            
            this.insertBefore(toolbar, this.toolbarset);
            return toolbar;
          ]]>
        </body>
      </method>
    </implementation>
  </binding>
  
  <binding id="toolbar" extends="chrome://global/content/bindings/toolbar.xml#toolbar-base">
    <implementation implements="nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          return Components.interfaces.nsIAccessibleProvider.XULToolbar;
        </getter>
      </property>
      <field name="firstPermanentChild">
        null
      </field>
      <field name="lastPermanentChild">
        null
      </field>
      
      <property name="toolbarName"
                onget="return this.getAttribute('toolbarname');"
                onset="this.setAttribute('toolbarname', val); return val;"/>
      
      <constructor>
        <![CDATA[
          if (this.hasAttribute("firstpermanentchild")) {
            // The toolbar is being reconstructed. The original first and last
            // permanent child nodes need to be restored, the rest of the
            // constructor can be skipped.

            this.firstPermanentChild =
              this.childNodes.item(this.getAttribute("firstpermanentchild"));
            this.lastPermanentChild =
              this.childNodes.item(this.getAttribute("lastpermanentchild"));
            return;
          }

          this.firstPermanentChild = this.firstChild;
          this.lastPermanentChild = this.lastChild;
          this.setAttribute("firstpermanentchild", this.firstChild ? 0 : -1);
          this.setAttribute("lastpermanentchild", this.childNodes.length -1);

          // Searching for the toolbox palette in the toolbar binding because
          // toolbars are constructed first.
          var toolbox = this.parentNode;
          
          if (!toolbox.palette) {
            // Look to see if there is a toolbarpalette.
            var node = toolbox.firstChild;
            while (node) {
              if (node.localName == "toolbarpalette")
                break;
              node = node.nextSibling;
            }
            
            if (!node)
              return;

            // Hold on to the palette but remove it from the document.
            toolbox.palette = node;       
            toolbox.removeChild(node);
          }
          
          // Build up our contents from the palette.
          var currentSet = this.getAttribute("currentset");
          if (!currentSet)
            currentSet = this.getAttribute("defaultset");
          if (currentSet)
            this.currentSet = currentSet;
        ]]>
      </constructor>

      <property name="currentSet">
        <getter>
          <![CDATA[
            var currentSet = "";
            for (let i = 0, node; node = this.childNodes.item(i); i++) {
              if (node == this.firstPermanentChild)
                this.setAttribute("firstpermanentchild", i);
              if (node == this.lastPermanentChild)
                this.setAttribute("lastpermanentchild", i);

              if (node.id &&
                  node.localName == "toolbaritem" || 
                  node.localName == "toolbarbutton" ||
                  node.localName == "toolbarseparator" ||
                  node.localName == "toolbarspring" ||
                  node.localName == "toolbarspacer")
              {
                if (currentSet)
                  currentSet += ",";

                if (node.localName == "toolbarseparator")
                  currentSet += "separator";
                else if (node.localName == "toolbarspring")
                  currentSet += "spring";
                else if (node.localName == "toolbarspacer")
                  currentSet += "spacer";
                else
                  currentSet += node.id;
              }
            }

            if ("currentSetChanged" in this)
              this.currentSetChanged(currentSet);

            return currentSet ? currentSet : "__empty";
          ]]>
        </getter>
        
        <setter>
          <![CDATA[
            var palette = this.parentNode.palette;
            // Remove all items before the first permanent child and after the last permanent child.
            while (this.lastChild != this.lastPermanentChild) {
              switch (this.lastChild.localName) {
                case "toolbarseparator":
                case "toolbarspring":
                case "toolbarspacer":
                  this.removeChild(this.lastChild);
                  break;
                default:
                  palette.appendChild(this.lastChild);
              }
            }

            while (this.firstChild != this.firstPermanentChild) {
              switch (this.firstChild.localName) {
                case "toolbarseparator":
                case "toolbarspring":
                case "toolbarspacer":
                  this.removeChild(this.firstChild);
                  break;
                default:
                  palette.appendChild(this.firstChild);
              }
            }

            var firstChildID = this.firstPermanentChild ? this.firstPermanentChild.id : "";
            var lastChildID = this.lastPermanentChild ? this.lastPermanentChild.id : "";

            if (val && val != "__empty") {
              var itemIds = val.split(",");
              var before = true;
              for (var i = 0; i < itemIds.length; i++) {
                if (itemIds[i] == firstChildID || itemIds[i] == lastChildID)
                  before = false;
                else
                  this.insertItem(itemIds[i], null, null, before);
              }
            }

            if (this.firstPermanentChild) {
              this.setAttribute("firstpermanentchild",
                                Array.indexOf(this.childNodes, this.firstPermanentChild));
              this.setAttribute("lastpermanentchild",
                                Array.indexOf(this.childNodes, this.lastPermanentChild));
            }

            if ("currentSetChanged" in this)
              this.currentSetChanged(val);

            return val;
          ]]>
        </setter>
      </property>
      
      <method name="insertItem">
        <parameter name="aId"/>
        <parameter name="aBeforeElt"/>
        <parameter name="aWrapper"/>
        <parameter name="aBeforePermanent"/>
        <body>
          <![CDATA[
            var newItem = null;
            
            // Create special cases of palette items.
            var uniqueId;
            if (aId == "separator") {
              newItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                                 "toolbarseparator");
              uniqueId = (new Date()).getTime()+this.childNodes.length;
              newItem.id = "separator" + uniqueId;
              newItem.className = "chromeclass-toolbar-additional";
            } else if (aId == "spring") {
              newItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                                 "toolbarspring");
              uniqueId = (new Date()).getTime()+this.childNodes.length;
              newItem.flex = 1;
              newItem.id = "spring" + uniqueId;
              newItem.className = "chromeclass-toolbar-additional";
            } else if (aId == "spacer") {
              newItem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                                 "toolbarspacer");
              uniqueId = (new Date()).getTime()+this.childNodes.length;
              newItem.id = "spacer" + uniqueId;
              newItem.className = "chromeclass-toolbar-additional";
            } else if (this.parentNode.localName == "toolbox") {
              // Attempt to locate an item with a matching id within document.
              newItem = document.getElementById(aId);
              if (!newItem || newItem.parentNode.parentNode != this.parentNode) {
                // Attempt to locate an item with a matching id within palette.
                // The palette does not live in the document by default.
                newItem = this.parentNode.palette
                              .getElementsByAttribute("id", aId).item(0);
                if (!newItem || newItem.parentNode != this.parentNode.palette)
                  return false;
              }
            }

            var insertItem = newItem;
            
            // Wrap the item in another node if so inclined.
            if (aWrapper) {
              aWrapper.appendChild(newItem);
              insertItem = aWrapper;
            }
            
            // Insert the palette item into the toolbar.
            if (aBeforeElt)
              this.insertBefore(insertItem, aBeforeElt);
            else if (aBeforePermanent && this.firstPermanentChild)
              this.insertBefore(insertItem, this.firstPermanentChild);
            else
              this.appendChild(insertItem);
               
            return newItem;
          ]]>
        </body>
      </method>

      <method name="hasCustomInteractiveItems">
        <parameter name="aCurrentSet"/>
        <body><![CDATA[
          if (aCurrentSet == "__empty")
            return false;

          var defaultOrNoninteractive = (this.getAttribute("defaultset") || "")
                                          .split(",")
                                          .concat(["separator", "spacer", "spring"]);
          return aCurrentSet.split(",").some(function (item) {
            return defaultOrNoninteractive.indexOf(item) == -1;
          });
        ]]></body>
      </method>
    </implementation>
  </binding>

  <binding id="toolbar-menubar-autohide"
           extends="chrome://global/content/bindings/toolbar.xml#toolbar">
    <implementation>
      <constructor>
        this._setInactive();
      </constructor>

      <field name="_inactiveTimeout">null</field>

      <field name="_contextMenuListener"><![CDATA[({
        toolbar: this,
        contextMenu: null,

        get active () !!this.contextMenu,

        init: function (event) {
          var node = event.target;
          while (node != this.toolbar) {
            if (node.localName == "menupopup")
              return;
            node = node.parentNode;
          }

          var contextMenuId = this.toolbar.getAttribute("context");
          if (!contextMenuId)
            return;

          this.contextMenu = document.getElementById(contextMenuId);
          if (!this.contextMenu)
            return;

          this.contextMenu.addEventListener("popupshown", this, false);
          this.contextMenu.addEventListener("popuphiding", this, false);
          this.toolbar.addEventListener("mousemove", this, false);
        },
        handleEvent: function (event) {
          switch (event.type) {
            case "popupshown":
              this.toolbar.removeEventListener("mousemove", this, false);
              break;
            case "popuphiding":
            case "mousemove":
              this.toolbar._setInactiveAsync();
              this.toolbar.removeEventListener("mousemove", this, false);
              this.contextMenu.removeEventListener("popuphiding", this, false);
              this.contextMenu.removeEventListener("popupshown", this, false);
              this.contextMenu = null;
              break;
          }
        }
      })]]></field>

      <method name="currentSetChanged">
        <parameter name="aCurrentSet"/>
        <body><![CDATA[
          if (this.hasCustomInteractiveItems(aCurrentSet))
            this.setAttribute("customized", "true");
          else
            this.removeAttribute("customized");
        ]]></body>
      </method>

      <method name="_setInactive">
        <body><![CDATA[
          this.setAttribute("inactive", "true");
        ]]></body>
      </method>

      <method name="_setInactiveAsync">
        <body><![CDATA[
          this._inactiveTimeout = setTimeout(function (self) {
            if (self.getAttribute("autohide") == "true") {
              self._inactiveTimeout = null;
              self._setInactive();
            }
          }, 0, this);
        ]]></body>
      </method>

      <method name="_setActive">
        <body><![CDATA[
          if (this._inactiveTimeout) {
            clearTimeout(this._inactiveTimeout);
            this._inactiveTimeout = null;
          }
          this.removeAttribute("inactive");
        ]]></body>
      </method>
    </implementation>

    <handlers>
      <handler event="DOMMenuBarActive"     action="this._setActive();"/>
      <handler event="popupshowing"         action="this._setActive();"/>
      <handler event="mousedown" button="2" action="this._contextMenuListener.init(event);"/>
      <handler event="DOMMenuBarInactive"><![CDATA[
        if (!this._contextMenuListener.active)
          this._setInactiveAsync();
      ]]></handler>
    </handlers>
  </binding>

  <binding id="toolbar-drag"
           extends="chrome://global/content/bindings/toolbar.xml#toolbar">
    <implementation>
      <constructor><![CDATA[
        try {
          Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm");
          let draggableThis = new WindowDraggingElement(this, window);
          draggableThis.mouseDownCheck = function(e) {
            // Don't move while customizing or while in full screen mode.
            return !this.parentNode.customizing && !window.fullScreen;
          }
        } catch (e) {}
      ]]></constructor>
    </implementation>
  </binding>

  <binding id="menubar" extends="chrome://global/content/bindings/toolbar.xml#toolbar-base" display="xul:menubar">
    <implementation implements="nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          <![CDATA[
            return Components.interfaces.nsIAccessibleProvider.XULMenubar;
          ]]>
        </getter>
       </property>
       <field name="_active">false</field>
       <field name="_statusbar">null</field>
       <field name="_originalStatusText">null</field>
       <property name="statusbar" onget="return this.getAttribute('statusbar');"
                                  onset="this.setAttribute('statusbar', val); return val;"/>
       <method name="_updateStatusText">
          <parameter name="itemText"/>
          <body>
           <![CDATA[
            if (!this._active)
                return;
            var newText = itemText ? itemText : this._originalStatusText;
            if (newText != this._statusbar.label)
                this._statusbar.label = newText;
           ]]>
          </body>
        </method>
    </implementation>
    <handlers>
        <handler event="DOMMenuBarActive">
          <![CDATA[
            if (!this.statusbar) return;
            this._statusbar = document.getElementById(this.statusbar);
            if (!this._statusbar)
              return;
            this._active = true;
            this._originalStatusText = this._statusbar.label;
          ]]>
        </handler>
        <handler event="DOMMenuBarInactive">
          <![CDATA[
            if (!this._active)
              return;
            this._active = false;
            this._statusbar.label = this._originalStatusText;
          ]]>
        </handler>
        <handler event="DOMMenuItemActive">this._updateStatusText(event.target.statusText);</handler>
        <handler event="DOMMenuItemInactive">this._updateStatusText("");</handler>
    </handlers>
  </binding>

  <binding id="toolbardecoration" extends="chrome://global/content/bindings/toolbar.xml#toolbar-base">
    <implementation implements="nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          return Components.interfaces.nsIAccessibleProvider.XULToolbarSeparator;
        </getter>
      </property>
    </implementation>
  </binding>

  <binding id="toolbarpaletteitem" extends="chrome://global/content/bindings/toolbar.xml#toolbar-base" display="xul:button">
    <content>
      <xul:hbox class="toolbarpaletteitem-box" flex="1" xbl:inherits="type,place">
        <children/>
      </xul:hbox>
    </content>
  </binding>
    
  <binding id="toolbarpaletteitem-palette" extends="chrome://global/content/bindings/toolbar.xml#toolbarpaletteitem">
    <content>
      <xul:hbox class="toolbarpaletteitem-box" xbl:inherits="type,place">
        <children/>
      </xul:hbox>
      <xul:label xbl:inherits="value=title"/>
    </content>
  </binding>

</bindings>          

