<?xml version="1.0"?>

<bindings id="scaleBindings"
   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="scalethumb" extends="xul:button">
    <resources>
      <stylesheet src="chrome://global/skin/scale.css"/>
    </resources>

    <implementation implements="nsIAccessibleProvider">
      <property name="accessibleType" readonly="true">
        <getter>
          return Components.interfaces.nsIAccessibleProvider.XULThumb;
        </getter>
      </property>
    </implementation>
  </binding>

  <binding id="scaleslider" display="xul:slider"
           extends="chrome://global/content/bindings/general.xml#basecontrol">
    <resources>
      <stylesheet src="chrome://global/skin/scale.css"/>
    </resources>
  </binding>

  <binding id="scale"
           extends="chrome://global/content/bindings/general.xml#basecontrol">
    <resources>
      <stylesheet src="chrome://global/skin/scale.css"/>
    </resources>

    <content align="center" pack="center">
      <xul:slider anonid="slider" class="scale-slider" snap="true" flex="1"
                  xbl:inherits="disabled,orient,dir,curpos=value,minpos=min,maxpos=max,increment,pageincrement,movetoclick">
        <xul:thumb class="scale-thumb" xbl:inherits="disabled,orient"/>
      </xul:slider>
    </content>
    
    <implementation implements="nsIAccessibleProvider, nsISliderListener">
      <property name="accessibleType" readonly="true">
        <getter>
          return Components.interfaces.nsIAccessibleProvider.XULScale;
        </getter>
      </property>

      <property name="value" onget="return this._getIntegerAttribute('curpos', 0);"
                             onset="return this._setIntegerAttribute('curpos', val);"/>
      <property name="min" onget="return this._getIntegerAttribute('minpos', 0);"
                           onset="return this._setIntegerAttribute('minpos', val);"/>
      <property name="max" onget="return this._getIntegerAttribute('maxpos', 100);"
                           onset="return this._setIntegerAttribute('maxpos', val);"/>
      <property name="increment" onget="return this._getIntegerAttribute('increment', 1);"
                                 onset="return this._setIntegerAttribute('increment', val);"/>
      <property name="pageIncrement" onget="return this._getIntegerAttribute('pageincrement', 10);"
                                     onset="return this._setIntegerAttribute('pageincrement', val);"/>

      <field name="_userChanged">false</field>
      <field name="_sliderElement"/>
      <property name="_slider" readonly="true">
        <getter>
          if (!this._sliderElement)
            this._sliderElement = document.getAnonymousElementByAttribute(this, "anonid", "slider");
          return this._sliderElement;
        </getter>
      </property>

      <constructor>
        <![CDATA[
          var value = parseInt(this.getAttribute("value"), 10);
          if (!isNaN(value))
            this.value = value;
          else if (this.min > 0)
            this.value = this.min;
          else if (this.max < 0)
            this.value = this.max;
        ]]>
      </constructor>

      <method name="_getIntegerAttribute">
        <parameter name="aAttr"/>
        <parameter name="aDefaultValue"/>
        <body>
          var value = this._slider.getAttribute(aAttr);
          var intvalue = parseInt(value, 10);
          if (!isNaN(intvalue))
            return intvalue;
          return aDefaultValue;
        </body>
      </method>

      <method name="_setIntegerAttribute">
        <parameter name="aAttr"/>
        <parameter name="aValue"/>
        <body>
        <![CDATA[
          var intvalue = parseInt(aValue, 10);
          if (!isNaN(intvalue)) {
            if (aAttr == "curpos") {
              if (intvalue < this.min)
                intvalue = this.min;
              else if (intvalue > this.max)
                intvalue = this.max;
            }
            this._slider.setAttribute(aAttr, intvalue);
          }
          return aValue;
        ]]>
        </body>
      </method>

      <method name="decrease">
        <body>
        <![CDATA[
          var newpos = this.value - this.increment;
          var startpos = this.min;
          this.value = (newpos > startpos) ? newpos : startpos;
        ]]>
        </body>
      </method>
      <method name="increase">
        <body>
        <![CDATA[
          var newpos = this.value + this.increment;
          var endpos = this.max;
          this.value = (newpos < endpos) ? newpos : endpos;
        ]]>
        </body>
      </method>

      <method name="decreasePage">
        <body>
        <![CDATA[
          var newpos = this.value - this.pageIncrement;
          var startpos = this.min;
          this.value = (newpos > startpos) ? newpos : startpos;
        ]]>
        </body>
      </method>
      <method name="increasePage">
        <body>
        <![CDATA[
          var newpos = this.value + this.pageIncrement;
          var endpos = this.max;
          this.value = (newpos < endpos) ? newpos : endpos;
        ]]>
        </body>
      </method>

      <method name="valueChanged">
        <parameter name="which"/>
        <parameter name="newValue"/>
        <parameter name="userChanged"/>
        <body>
        <![CDATA[
          switch (which) {
            case "curpos":
              this.setAttribute("value", newValue);

              // in the future, only fire the change event when userChanged
              // or _userChanged is true
              var changeEvent = document.createEvent("Events");
              changeEvent.initEvent("change", true, true);
              this.dispatchEvent(changeEvent);
              break;

            case "minpos":
              this.setAttribute("min", newValue);
              break;

            case "maxpos":
              this.setAttribute("max", newValue);
              break;
          }
        ]]>
        </body>
      </method>

      <method name="dragStateChanged">
        <parameter name="isDragging"/>
        <body/>
      </method>
    </implementation>

    <handlers>
      <handler event="keypress" keycode="VK_LEFT" preventdefault="true">
        <![CDATA[
          this._userChanged = true;
          (this.orient != "vertical" && this.dir == "reverse") ? this.increase() : this.decrease();
          this._userChanged = false;
        ]]>
      </handler>
      <handler event="keypress" keycode="VK_RIGHT" preventdefault="true">
        <![CDATA[
          this._userChanged = true;
          (this.orient != "vertical" && this.dir == "reverse") ? this.decrease() : this.increase();
          this._userChanged = false;
        ]]>
      </handler>
      <handler event="keypress" keycode="VK_UP" preventdefault="true">
        <![CDATA[
          this._userChanged = true;
          (this.orient == "vertical" && this.dir != "reverse") ? this.decrease() : this.increase();
          this._userChanged = false;
        ]]>
      </handler>
      <handler event="keypress" keycode="VK_DOWN" preventdefault="true">
        <![CDATA[
          this._userChanged = true;
          (this.orient == "vertical" && this.dir != "reverse") ? this.increase() : this.decrease();
          this._userChanged = false;
        ]]>
      </handler>
      <handler event="keypress" keycode="VK_PAGE_UP" preventdefault="true">
        <![CDATA[
          this._userChanged = true;
          (this.orient == "vertical" && this.dir != "reverse") ? this.decreasePage() : this.increasePage();
          this._userChanged = false;
        ]]>
      </handler>
      <handler event="keypress" keycode="VK_PAGE_DOWN" preventdefault="true">
        <![CDATA[
          this._userChanged = true;
          (this.orient == "vertical" && this.dir != "reverse") ? this.increasePage() : this.decreasePage();
          this._userChanged = false;
        ]]>
      </handler>
      <handler event="keypress" keycode="VK_HOME" preventdefault="true">
        this._userChanged = true;
        this.value = (this.dir == "reverse") ? this.max : this.min;
        this._userChanged = false;
      </handler>
      <handler event="keypress" keycode="VK_END" preventdefault="true">
        this._userChanged = true;
        this.value = (this.dir == "reverse") ? this.min : this.max;
        this._userChanged = false;
      </handler>
    </handlers>

  </binding>
</bindings>
