<?xml version="1.0"?>

<bindings id="generalBindings"
   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="basecontrol">
    <implementation implements="nsIDOMXULControlElement">
      <!-- public implementation -->
      <property name="disabled" onset="if (val) this.setAttribute('disabled', 'true');
                                       else this.removeAttribute('disabled');
                                       return val;"
                                onget="return this.getAttribute('disabled') == 'true';"/>
      <property name="tabIndex" onget="return parseInt(this.getAttribute('tabindex')) || 0"
                                onset="if (val) this.setAttribute('tabindex', val);
                                       else this.removeAttribute('tabindex'); return val;"/>
    </implementation>
  </binding>

  <binding id="basetext" extends="chrome://global/content/bindings/general.xml#basecontrol">
    <implementation implements="nsIDOMXULLabeledControlElement">
      <!-- public implementation -->
      <property name="label"      onset="this.setAttribute('label',val); return val;"
                                  onget="return this.getAttribute('label');"/>
      <property name="crop"       onset="this.setAttribute('crop',val); return val;"
                                  onget="return this.getAttribute('crop');"/>
      <property name="image"      onset="this.setAttribute('image',val); return val;"
                                  onget="return this.getAttribute('image');"/>
      <property name="command"    onset="this.setAttribute('command',val); return val;"
                                  onget="return this.getAttribute('command');"/>
      <property name="accessKey">
        <getter>
          <![CDATA[
            return this.labelElement ? this.labelElement.accessKey : this.getAttribute('accesskey');
          ]]>
        </getter>
        <setter>
          <![CDATA[
            // Always store on the control
            this.setAttribute('accesskey', val);
            // If there is a label, change the accesskey on the labelElement
            // if it's also set there
            if (this.labelElement) {
              this.labelElement.accessKey = val;
            }
            return val;
          ]]>
        </setter>
      </property>

      <field name="labelElement"/>
    </implementation>      
  </binding>

  <binding id="control-item" extends="chrome://global/content/bindings/general.xml#basetext">
    <implementation>
      <property name="value"      onset="this.setAttribute('value', val); return val;"
                                  onget="return this.getAttribute('value');"/>
    </implementation>
  </binding>

  <binding id="root-element">
    <implementation>
      <field name="_lightweightTheme">null</field>
      <constructor><![CDATA[
        if (this.hasAttribute("lightweightthemes")) {
          let temp = {};
          Components.utils.import("resource://gre/modules/LightweightThemeConsumer.jsm", temp);
          this._lightweightTheme = new temp.LightweightThemeConsumer(this.ownerDocument);
        }
      ]]></constructor>
      <destructor><![CDATA[
        if (this._lightweightTheme) {
          this._lightweightTheme.destroy();
          this._lightweightTheme = null;
        }
      ]]></destructor>
    </implementation>
  </binding>

  <!--
    Inline Editable UI Element
    - This binding forms the basis of the inline edit treecell and the inline edit
    - buttons. 
    - TODO: investigate creating extensions to the wrapper widgets (tree, toolbar)
    -       to make them provide some object implementing an interface similar to
    -       tree's so we can build in some of the ILE behavior (such as going
    -       in and out of the mode, asking isEditable etc) so as to remove some of
    -       the burden from the implementor. 
    -
    - Note that this widget will be no longer used in the bookmarks window once 
    - tree is extended to have this functionality built in. 
    -->
  <binding id="inline-edit-base" extends="chrome://global/content/bindings/general.xml#basetext">
    <implementation>
      <field name="_mode">0</field>
      <method name="setMode">
        <parameter name="val"/>
        <body>
        <![CDATA[
          var ctr = document.getAnonymousElementByAttribute(this, "ileattr", "text-container");
          var txt = document.getAnonymousElementByAttribute(this, "ileattr", "text");
          this.setAttribute("mode", val);
          if (val == "edit") {
            var nodes = document.getAnonymousNodes(this);

            if (txt.getAttribute("hidden") != "true") {
              ctr.setAttribute("mode", "edit");
              var width = ctr.boxObject.width;
              txt.setAttribute("hidden", "true");
              const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
              var field = document.createElementNS(kXULNS, "textbox");
              field.className = "textbox-inline-edit";
              field.setAttribute("flex", "1");
              field.setAttribute("value", txt.getAttribute("value"));
              field.setAttribute("ileattr", "field");
              field.setAttribute("rootcontent", txt.getAttribute("rootcontent"));
              field.setAttribute("style", "width: " + width + "px");
              ctr.appendChild(field);
              field.addEventListener("keydown", this.fieldKeyDown, false);
              field.addEventListener("change", this.fieldChange, false);
              field.select();
            }
          }
          else {
            nodes = document.getAnonymousNodes(this);
            var fld = document.getAnonymousElementByAttribute(this, "ileattr", "field");
            if (fld && txt.getAttribute("hidden") == "true") {
              ctr.removeAttribute("mode");
              fld.blur();
              ctr.removeChild(fld);
              txt.removeAttribute("hidden");
            }
          }
        ]]>
        </body>
      </method>
      <field name="_observers">
      <![CDATA[
        ({
          reject: [], 
          accept: []
        })
      ]]>
      </field>
      <field name="valueIsRejected">false</field>
      <method name="addObserver">
        <parameter name="aObserver"/>
        <parameter name="aTopic"/>
        <parameter name="aParams"/>
        <body>
          this._observers[aTopic].push({ callback: aObserver, params: aParams });
        </body>
      </method>
      <method name="fieldKeyDown">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          var rootLocalName = aEvent.target.getAttribute("rootcontent");
          if (rootLocalName) {
            // Root content is the bound element. 
            var rootContent = aEvent.target;
            while (rootContent && rootContent.localName != rootLocalName) 
              rootContent = rootContent.parentNode;
            
            if (rootContent) {
              var ctr = document.getAnonymousElementByAttribute(rootContent, "ileattr", "text-container");
              if (aEvent.keyCode == 13) {
                rootContent.valueIsRejected = false;
                rootContent.fieldChange(aEvent);
              }
              if (aEvent.keyCode == 27) {
                rootContent.valueIsRejected = true;
                var fld = document.getAnonymousElementByAttribute(rootContent, "ileattr", "field");
                for (i = 0; i < rootContent._observers["reject"].length; ++i) 
                  rootContent._observers["reject"][i].callback(rootContent._observers["reject"][i].params.concat(fld.value), "reject");
                if ("setMode" in rootContent) 
                  rootContent.setMode("normal");
              }
            }
          }
          aEvent.stopPropagation();
        ]]>
        </body>
      </method>
      <field name="valueIsAccepted">false</field>
      <method name="fieldChange">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          var rootLocalName = this.getAttribute("rootcontent");
          if (rootLocalName) {
            // Root content is the bound element. 
            var rootContent = this;
            while (rootContent && rootContent.localName != rootLocalName) 
              rootContent = rootContent.parentNode;

            if (rootContent) {
              var ctr = document.getAnonymousElementByAttribute(rootContent, "ileattr", "text-container");
              if (!rootContent.valueIsRejected) {
                var fld = document.getAnonymousElementByAttribute(rootContent, "ileattr", "field");
                for (var i = 0; i < rootContent._observers["accept"].length; ++i)
                  rootContent._observers["accept"][i].callback(rootContent._observers["accept"][i].params.concat(fld.value), "accept");
                if ("setMode" in rootContent)
                  rootContent.setMode("normal");
              }
            }
          }
        ]]>
        </body>
      </method>
    </implementation>
  </binding>

  <!-- inline editable buttons -->
  <binding id="buttonleft-ile" extends="chrome://global/content/bindings/general.xml#inline-edit-base">
    <content>
      <xul:hbox class="button-internal-box" align="center" flex="1">
        <xul:image class="button-icon" xbl:inherits="src"/>
        <xul:hbox class="button-text-container" flex="1" ileattr="text-container">
          <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop,dragover-top" ileattr="text" rootcontent="button" flex="1"/>
        </xul:hbox>
      </xul:hbox>
      <children includes="menupopup"/>
    </content>
  </binding>

  <binding id="iframe">
    <implementation implements="nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          <![CDATA[
            return Components.interfaces.nsIAccessibleProvider.OuterDoc;
          ]]>
        </getter>
      </property>
      <property name="docShell"
                readonly="true"
                onget="return this.boxObject.QueryInterface(Components.interfaces.nsIContainerBoxObject).docShell"/>
      <property name="contentWindow"
                readonly="true"
                onget="return this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindow);"/>
      <property name="webNavigation"
                onget="return this.docShell.QueryInterface(Components.interfaces.nsIWebNavigation);"
                readonly="true"/>
      <property name="contentDocument" readonly="true"
                onget="return this.webNavigation.document;"/>
    </implementation>
  </binding>

  <binding id="statusbarpanel" display="xul:button">
    <content>
      <children>
        <xul:label class="statusbarpanel-text" xbl:inherits="value=label,crop" crop="right" flex="1"/>
      </children>
    </content>

    <implementation implements="nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          <![CDATA[
            return Components.interfaces.nsIAccessibleProvider.XULButton;
          ]]>
        </getter>
      </property>

      <property name="label"
                onget="return this.getAttribute('label');"
                onset="this.setAttribute('label',val); return val;"/>
      <property name="image"
                onget="return this.getAttribute('image');"
                onset="this.setAttribute('image',val); return val;"/>
      <property name="src"
                onget="return this.getAttribute('src');"
                onset="this.setAttribute('src',val); return val;"/>
    </implementation>
  </binding>

  <binding id="statusbarpanel-menu-iconic" display="xul:menu"
           extends="chrome://global/content/bindings/general.xml#statusbarpanel">
    <content>
      <xul:image class="statusbarpanel-icon" xbl:inherits="src,src=image"/>
      <children/>
    </content>
  </binding>

  <binding id="statusbar">
    <content>
      <children/>
      <xul:statusbarpanel class="statusbar-resizerpanel">
        <xul:resizer dir="bottomend"/>
      </xul:statusbarpanel>
    </content>

    <implementation implements="nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          <![CDATA[
            return Components.interfaces.nsIAccessibleProvider.XULStatusBar;
          ]]>
        </getter>
      </property>
    </implementation>
  </binding>
  
  <binding id="statusbar-drag"
           extends="chrome://global/content/bindings/general.xml#statusbar">
    <implementation>
      <constructor>
        try {
          Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm");
          new WindowDraggingElement(this, window);
        } catch (e) {}
      </constructor>
    </implementation>
  </binding>

  <binding id="statusbarpanel-iconic" display="xul:button"
           extends="chrome://global/content/bindings/general.xml#statusbarpanel">
    <content>
      <xul:image class="statusbarpanel-icon" xbl:inherits="src,src=image"/>
    </content>
  </binding>

  <binding id="statusbarpanel-iconic-text" display="xul:button"
           extends="chrome://global/content/bindings/general.xml#statusbarpanel">
    <content>
      <xul:image class="statusbarpanel-icon" xbl:inherits="src,src=image"/>
      <xul:label class="statusbarpanel-text" xbl:inherits="value=label,crop"/>
    </content>
  </binding>

  <binding id="image">
    <implementation implements="nsIDOMXULImageElement, nsIAccessibleProvider">
      <property name="src"
                onget="return this.getAttribute('src');"
                onset="this.setAttribute('src',val); return val;"/>
      <property name="accessibleType" readonly="true">
        <getter>
          <![CDATA[
            // Expose XUL images with an onclick as a toolbarbutton
            return this.hasAttribute("onclick") ?
              Components.interfaces.nsIAccessibleProvider.XULToolbarButton :
              Components.interfaces.nsIAccessibleProvider.XULImage;
          ]]>
        </getter>
      </property>
    </implementation>
  </binding>

  <binding id="deck">
    <implementation>
      <property name="selectedIndex"
                onget="return this.getAttribute('selectedIndex') || '0'">
        <setter>
        <![CDATA[
          if (this.selectedIndex == val)
            return val;
          this.setAttribute("selectedIndex", val);
          var event = document.createEvent('Events');
          event.initEvent('select', true, true);
          this.dispatchEvent(event);
          return val;
        ]]>
        </setter>
      </property>

      <property name="selectedPanel">
        <getter>
          <![CDATA[
            return this.childNodes[this.selectedIndex];
          ]]>
        </getter>

        <setter>
          <![CDATA[
            var selectedIndex = -1;
            for (var panel = val; panel != null; panel = panel.previousSibling)
              ++selectedIndex;
            this.selectedIndex = selectedIndex;
            return val;
          ]]>
        </setter>
      </property>
    </implementation>
  </binding>

  <binding id="dropmarker" extends="xul:button">
    <resources>
      <stylesheet src="chrome://global/skin/dropmarker.css"/>
    </resources>

    <content>
      <xul:image class="dropmarker-icon"/>
    </content>

    <implementation implements="nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          <![CDATA[
            return Components.interfaces.nsIAccessibleProvider.XULDropmarker;
          ]]>
        </getter>
      </property>
    </implementation>
  </binding>

  <binding id="windowdragbox">
    <implementation>
      <constructor>
        try {
          Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm");
          new WindowDraggingElement(this, window);
        } catch (e) {}
      </constructor>
    </implementation>
  </binding>

</bindings>
