<?xml version="1.0"?>

<bindings id="buttonBindings"
   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="button-base" extends="chrome://global/content/bindings/general.xml#basetext">
    <implementation implements="nsIDOMXULButtonElement, nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          <![CDATA[
            return Components.interfaces.nsIAccessibleProvider.XULButton;
          ]]>
        </getter>
      </property>

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

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

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

      <property name="open" onget="return this.hasAttribute('open');">
        <setter><![CDATA[
          if (this.boxObject instanceof
              Components.interfaces.nsIMenuBoxObject) {
            this.boxObject.openMenu(val);
          } else {
            // Fall back to just setting the attribute
            if (val) {
              this.setAttribute('open', 'true');
            } else {
              this.removeAttribute('open');
            }
          }
          return val;
        ]]></setter>
      </property>

      <property name="checked" onget="return this.hasAttribute('checked');">
        <setter><![CDATA[
          if (this.type == "checkbox") {
            this.checkState = val ? 1 : 0;
          } else if (this.type == "radio" && val) {
            var sibs = this.parentNode.getElementsByAttribute("group", this.group);
            for (var i = 0; i < sibs.length; ++i)
              sibs[i].removeAttribute("checked");
          }

          if (val)
            this.setAttribute("checked", "true");
          else
            this.removeAttribute("checked");
          
          return val;
        ]]></setter>
      </property>
      
      <property name="checkState">
        <getter><![CDATA[
          var state = this.getAttribute("checkState");
          if (state == "")
            return this.checked ? 1 : 0;
          else
            return state == "0" ? 0 : (state == "2" ? 2 : 1);
        ]]></getter>
        <setter><![CDATA[
          this.setAttribute("checkState", val);
          return val;
        ]]></setter>
      </property>
      
      <property name="autoCheck"
                onget="return this.getAttribute('autoCheck') == 'true';"
                onset="this.setAttribute('autoCheck', val); return val;"/>

      <method name ="filterButtons">
        <parameter name="node"/>
        <body>
        <![CDATA[
          if (node.localName == "button" && node.accessKey &&
            !node.disabled && !node.collapsed && !node.hidden)
            return NodeFilter.FILTER_ACCEPT;
          return NodeFilter.FILTER_SKIP;
        ]]>
        </body>
      </method>

      <method name="fireAccessKeyButton">
        <parameter name="aSubtree"/>
        <parameter name="aAccessKeyLower"/>
        <body>
        <![CDATA[
          var iterator = aSubtree.ownerDocument.createTreeWalker(aSubtree, 
                                                                 NodeFilter.SHOW_ELEMENT, 
                                                                 this.filterButtons, false);
          while (iterator.nextNode()) {
            var test = iterator.currentNode;
            if (test.accessKey.toLowerCase() == aAccessKeyLower && 
                !test.disabled && !test.collapsed && !test.hidden) {
              test.focus();
              test.click();
              return true;
            }
          }
          return false;
        ]]>
        </body>
      </method>

      <method name="_handleClick">
        <body>
        <![CDATA[
          if (!this.disabled &&
              (this.autoCheck || !this.hasAttribute("autoCheck"))) {

            if (this.type == "checkbox") {
              this.checked = !this.checked;
            } else if (this.type == "radio") {
              this.checked = true;
            }
          }
        ]]>
        </body>
      </method>
    </implementation>

    <handlers>
      <!-- While it would seem we could do this by handling oncommand, we can't
           because any external oncommand handlers might get called before ours,
           and then they would see the incorrect value of checked. Additionally
           a command attribute would redirect the command events anyway.-->
      <handler event="click" button="0" action="this._handleClick();"/>
      <handler event="keypress" key=" " action="this._handleClick();"/>

      <handler event="keypress">
      <![CDATA[
        if (this.boxObject instanceof Components.interfaces.nsIMenuBoxObject) {
          if (this.open)
            return;
        } else {
          if (event.keyCode == KeyEvent.DOM_VK_UP ||
              (event.keyCode == KeyEvent.DOM_VK_LEFT &&
                document.defaultView.getComputedStyle(this.parentNode, "")
                        .direction == "ltr") ||
              (event.keyCode == KeyEvent.DOM_VK_RIGHT &&
                document.defaultView.getComputedStyle(this.parentNode, "")
                        .direction == "rtl")) {
            event.preventDefault();
            window.document.commandDispatcher.rewindFocus();
            return;
          }

          if (event.keyCode == KeyEvent.DOM_VK_DOWN ||
              (event.keyCode == KeyEvent.DOM_VK_RIGHT &&
                document.defaultView.getComputedStyle(this.parentNode, "")
                        .direction == "ltr") ||
              (event.keyCode == KeyEvent.DOM_VK_LEFT &&
                document.defaultView.getComputedStyle(this.parentNode, "")
                        .direction == "rtl")) {
            event.preventDefault();
            window.document.commandDispatcher.advanceFocus();
            return;
          }
        }

        if (event.keyCode || event.charCode <= 32 || event.altKey || 
            event.ctrlKey || event.metaKey)
          return;  // No printable char pressed, not a potential accesskey

        // Possible accesskey pressed
        var charPressedLower = String.fromCharCode(event.charCode).toLowerCase();

        // If the accesskey of the current button is pressed, just activate it
        if (this.accessKey.toLowerCase() == charPressedLower) {
          this.click();
          return;
        }

        // Search for accesskey in the list of buttons for this doc and each subdoc
        // Get the buttons for the main document and all sub-frames
        for (var frameCount = -1; frameCount < window.top.frames.length; frameCount++) {
          var doc = (frameCount == -1)? window.top.document: 
            window.top.frames[frameCount].document
          if (this.fireAccessKeyButton(doc.documentElement, charPressedLower))
            return;
        }

        // Test anonymous buttons
        var dlg = window.top.document;
        var buttonBox = dlg.getAnonymousElementByAttribute(dlg.documentElement,
                                                         "anonid", "buttons");
        if (buttonBox)
          this.fireAccessKeyButton(buttonBox, charPressedLower);
      ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="button" display="xul:button"
           extends="chrome://global/content/bindings/button.xml#button-base">
    <resources>
      <stylesheet src="chrome://global/skin/button.css"/>
    </resources>

    <content>
      <children includes="observes|template|menupopup|panel|tooltip"/>
      <xul:hbox class="box-inherit button-box" xbl:inherits="align,dir,pack,orient"
                align="center" pack="center" flex="1" anonid="button-box">
        <children>
          <xul:image class="button-icon" xbl:inherits="src=image"/>
          <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop"/>
        </children>
      </xul:hbox>
    </content>
  </binding>

  <binding id="button-periodic-redraw"
           extends="chrome://global/content/bindings/button.xml#button">
    <implementation>
      <field name="_alive">true</field>
      <method name="_init">
        <body><![CDATA[
          var step = 0;
          var self = this;
          var hbox = document.getAnonymousElementByAttribute(this, "anonid", "button-box");
          var interval = setInterval(function nextStep() {
            try {
              // Only bother redrawing when we're visible
              if (!hbox.getBoundingClientRect().width) {
                // Maybe we've been removed from the document.
                if (!self._alive)
                  clearInterval(interval);
                return;
              }
              // Trigger redraw by changing the step attribute
              step = 1 - step;
              self.setAttribute('step', step);
            } catch (e) {
              clearInterval(interval);
            }
          }, 100);
        ]]></body>
      </method>
      <constructor>this._init();</constructor>
    </implementation>
  </binding>

  <binding id="menu" display="xul:menu"
           extends="chrome://global/content/bindings/button.xml#button">
    <content>
      <children includes="observes|template|menupopup|panel|tooltip"/>
      <xul:hbox class="box-inherit button-box" xbl:inherits="align,dir,pack,orient"
                align="center" pack="center" flex="1">
        <children>
          <xul:hbox class="box-inherit" xbl:inherits="align,dir,pack,orient"
                    align="center" pack="center" flex="1">
            <xul:image class="button-icon" xbl:inherits="src=image"/>
            <xul:label class="button-text" xbl:inherits="value=label,accesskey,crop"/>
          </xul:hbox>
          <xul:dropmarker class="button-menu-dropmarker" xbl:inherits="open,disabled,label"/>
        </children>
      </xul:hbox>
    </content>

    <handlers>
      <handler event="keypress" keycode="VK_RETURN" action="this.open = true;"/>
      <handler event="keypress" key=" " action="this.open = true;"/>
    </handlers>
  </binding>

  <binding id="menu-button-base"
           extends="chrome://global/content/bindings/button.xml#button-base">
    <implementation implements="nsIDOMEventListener">
      <constructor>
        this.init();
      </constructor>
      
      <method name="init">
        <body>
        <![CDATA[
          var btn = document.getAnonymousElementByAttribute(this, "anonid", "button");
          if (!btn)
            throw "XBL binding for <button type=\"menu-button\"/> binding must contain an element with anonid=\"button\"";
          
          var menubuttonParent = this;
          btn.addEventListener("mouseover", function() { 
            if (!this.disabled)
              menubuttonParent.buttonover = true;
          }, true);
          btn.addEventListener("mouseout", function() {
            menubuttonParent.buttonover = false;
          }, true);
          btn.addEventListener("mousedown", function() {
            if (!this.disabled) {
              menubuttonParent.buttondown = true;
              document.addEventListener("mouseup", menubuttonParent, true);
            }
          }, true);
        ]]>
        </body>
      </method>
      
      <property name="buttonover" onget="return this.getAttribute('buttonover');">
        <setter>
        <![CDATA[
          var v = val || val == "true";
          if (!v && this.buttondown) { 
            this.buttondown = false;
            this._pendingActive = true;
          } 
          else {
            if (this._pendingActive) {
              this.buttondown = true;
              this._pendingActive = false;
            }
          }

          if (v)
            this.setAttribute("buttonover", "true");
          else
            this.removeAttribute("buttonover");
          return val;
        ]]>
        </setter>
      </property>
      
      <property name="buttondown" onget="return this.getAttribute('buttondown') == 'true';">
        <setter>
        <![CDATA[
          if (val || val == "true")
            this.setAttribute("buttondown", "true");
          else
            this.removeAttribute("buttondown");
          return val;
        ]]>
        </setter>
      </property>
      
      <field name="_pendingActive">false</field>

      <method name="handleEvent">
        <parameter name="aEvent"/>
        <body>
        <![CDATA[
          this._pendingActive = false;
          this.buttondown = false;
          document.removeEventListener("mouseup", this, true);
        ]]>
        </body>
      </method>

    </implementation>
  </binding>

  <binding id="menu-button" display="xul:menu"
           extends="chrome://global/content/bindings/button.xml#menu-button-base">
    <resources>
      <stylesheet src="chrome://global/skin/button.css"/>
    </resources>

    <content>
      <children includes="observes|template|menupopup|panel|tooltip"/>
      <xul:button class="box-inherit button-menubutton-button"
                  anonid="button" flex="1" allowevents="true"
                  xbl:inherits="disabled,crop,image,label,accessKey,command,
                                buttonover,buttondown,align,dir,pack,orient">
        <children/>
      </xul:button>
      <xul:dropmarker class="button-menubutton-dropmarker" xbl:inherits="open,disabled,label"/>
    </content>
  </binding>
  
  <binding id="button-image" display="xul:button"
           extends="chrome://global/content/bindings/button.xml#button">
    <content>
      <xul:image class="button-image-icon" xbl:inherits="src=image"/>
    </content>
  </binding>

  <binding id="button-repeat" display="xul:autorepeatbutton"
           extends="chrome://global/content/bindings/button.xml#button"/>
  
</bindings>
