<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->


<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>

<bindings
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xbl="http://www.mozilla.org/xbl"
    xmlns:svg="http://www.w3.org/2000/svg"
    xmlns:html="http://www.w3.org/1999/xhtml"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <binding id="richlistbox-batch" extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
    <handlers>
      <handler event="scroll">
        <![CDATA[
          // if there no more items to insert, just return early
          if (this._items.length == 0)
            return;

          if (this._contentScrollHeight == -1) {
            let scrollheight = {};
            this.scrollBoxObject.getScrolledSize({}, scrollheight);
            this._contentScrollHeight = scrollheight.value;
          }

          let y = {};
          this.scrollBoxObject.getPosition({}, y);
          let scrollRatio = (y.value + this._childrenHeight) / this._contentScrollHeight;

          // If we're scrolled 80% to the bottom of the list, append the next
          // set of items
          if (scrollRatio > 0.8)
            this._insertItems();
        ]]>
      </handler>
    </handlers>
    <implementation>
      <!-- Number of elements to add to the list initially. If there are more
           than this many elements to display, only add them to the list once
           the user has scrolled towards them. This is a performance
           optimization to avoid locking up while attempting to append hundreds
           of nodes to our richlistbox.
      -->
      <property name="batchSize" readonly="true" onget="return this.getAttribute('batch')"/>

      <field name="_childrenHeight">this.scrollBoxObject.height;</field>
      <field name="_items">[]</field>

      <method name="setItems">
        <parameter name="aItems"/>
        <body><![CDATA[
          this._items = aItems;
          this._insertItems();
        ]]></body>
      </method>

      <method name="_insertItems">
        <body><![CDATA[
          let items = this._items.splice(0, this.batchSize);
          if (!items.length)
            return; // no items to insert

          let count = items.length;
          for (let i = 0; i<count; i++)
            this.appendChild(items[i]);


          // make sure we recalculate the scrollHeight of the content
          this._contentScrollHeight = -1;
        ]]></body>
      </method>
    </implementation>
  </binding>

  <binding id="richlistbox-contextmenu" extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
    <handlers>
      <handler event="click" phase="bubble">
        <![CDATA[
          ContextMenuUI.hide();
        ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
    <handlers>
      <handler event="mousedown" phase="capturing">
        <![CDATA[
          // We'll get this if the user is interacting via the mouse
          let domUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"].
                                           getService(Ci.inIDOMUtils);
          domUtils.setContentState(this, 0x00000001);
        ]]>
      </handler>
      <handler event="click" phase="capturing">
        <![CDATA[
          // allow normal mouse event processing
          if (!InputSourceHelper || InputSourceHelper.isPrecise)
            return;
          // trap this here, we'll rely on tap events
          // event.stopPropagation();
        ]]>
      </handler>
      <handler event="touchstart" phase="capturing">
        <![CDATA[
          // touch input event
          let domUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"].
                                           getService(Ci.inIDOMUtils);
          domUtils.setContentState(this, 0x00000001);
        ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="menulist" display="xul:box" extends="chrome://global/content/bindings/menulist.xml#menulist">
    <handlers>
      <handler event="mousedown" phase="capturing">
        <![CDATA[
          // Stop the normal menupopup from appearing
          event.stopPropagation();
        ]]>
      </handler>

      <handler event="click" button="0">
        <![CDATA[
          if (this.disabled || this.itemCount == 0)
            return;

          this.focus();
          MenuControlUI.show(this);
        ]]>
      </handler>

      <handler event="command" phase="capturing">
        <![CDATA[
          // The dropmark (button) fires a command event too. Don't forward that.
          // Just forward the menuitem command events, which the toolkit version does.
          if (event.target.parentNode.parentNode != this)
            event.stopPropagation();
        ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="chrome-select-option">
    <content orient="horizontal" flex="1">
      <xul:image class="chrome-select-option-image" anonid="check"/>
      <xul:label anonid="label" xbl:inherits="value=label"/>
    </content>

    <implementation>
      <property name="selected">
        <getter>
          <![CDATA[
            return this.hasAttribute("selected");
          ]]>
        </getter>
        <setter>
          <![CDATA[
            if (val)
              this.setAttribute("selected", "true");
            else
              this.removeAttribute("selected");
            return val;
          ]]>
        </setter>
      </property>
    </implementation>
  </binding>

  <binding id="select-button" extends="xul:box">
    <content>
      <svg:svg width="11px" version="1.1" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: -moz-calc(50% - 2px); left: 4px;">
        <svg:polyline points="1 1 5 6 9 1" stroke="#414141" stroke-width="2" stroke-linecap="round" fill="transparent" stroke-linejoin="round"/>
      </svg:svg>
    </content>
  </binding>

  <binding id="textbox" extends="chrome://global/content/bindings/textbox.xml#textbox">
    <handlers>
      <handler event="contextmenu" phase="capturing">
        <![CDATA[
          let box = this.inputField.parentNode;
          box.showContextMenu(this, event, false);
        ]]>
      </handler>
      <handler event="click" phase="capturing">
        <![CDATA[
        if (event.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) {
          if (typeof SelectionHelperUI != 'undefined') {
            SelectionHelperUI.attachEditSession(ChromeSelectionHandler,
                event.clientX, event.clientY, this);
          } else {
            // If we don't have access to SelectionHelperUI then we are using this
            // binding for browser content (e.g. about:config)
            Services.obs.notifyObservers(event, "attach_edit_session_to_content", "");
          }
        }
      ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="search-textbox" extends="chrome://global/content/bindings/textbox.xml#search-textbox">
    <implementation>
      <field name="_searchClear">
        <![CDATA[
          document.getAnonymousElementByAttribute(this, "class", "textbox-search-clear");
        ]]>
      </field>
    </implementation>

    <handlers>
      <handler event="contextmenu" phase="capturing">
        <![CDATA[
          let box = this.inputField.parentNode;
          box.showContextMenu(this, event, false);
        ]]>
      </handler>

      <handler event="text" phase="bubbling"><![CDATA[
        // Listen for composition update, some VKB that does suggestions does not
        // update directly the content of the field but in this case we want to
        // search as soon as something is entered in the field
        let evt = document.createEvent("Event");
        evt.initEvent("input", true, false);
        this.dispatchEvent(evt);
      ]]></handler>

      <handler event="click" phase="bubbling"><![CDATA[
        // bug 629661. To reset the autosuggestions mechanism of Android, the
        // textfield need to reset the IME state
        if (event.originalTarget == this._searchClear) {
          setTimeout(function(self) {
            try {
              let imeEditor = self.inputField.QueryInterface(Ci.nsIDOMNSEditableElement)
                                             .editor
                                             .QueryInterface(Ci.nsIEditorIMESupport);
              if (imeEditor.composing)
                imeEditor.forceCompositionEnd();
            } catch(e) {}
          }, 0, this);
        }
      ]]></handler>
    </handlers>
  </binding>

  <binding id="input-box" extends="xul:box">
    <implementation>
      <method name="showContextMenu">
        <parameter name="aTextbox"/>
        <parameter name="aEvent"/>
        <parameter name="aIgnoreReadOnly"/>
        <body><![CDATA[
          let selectionStart = aTextbox.selectionStart;
          let selectionEnd = aTextbox.selectionEnd;

          let json = { types: ["input-text"], string: "" };
          if (selectionStart != selectionEnd) {
            json.types.push("cut");
            json.types.push("copy");
          }

          if (selectionStart > 0 || selectionEnd < aTextbox.textLength)
            json.types.push("select-all");

          let clipboard = Components.classes["@mozilla.org/widget/clipboard;1"].
                                            getService(Ci.nsIClipboard);
          let flavors = ["text/unicode"];
          let hasData = clipboard.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard);

          if (hasData && (!aTextbox.readOnly || aIgnoreReadOnly)) {
            json.types.push("paste");
            if (aTextbox.type == "url") {
              json.types.push("paste-url");
            }
          }
          json.xPos = aEvent.clientX;
          json.yPos = aEvent.clientY;
          json.source = aEvent.mozInputSource;
          ContextMenuUI.showContextMenu({ target: aTextbox, json: json });
        ]]></body>
      </method>
    </implementation>
    <handlers>
      <handler event="click" phase="capturing">
        <![CDATA[
          if (event.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) {
            if (typeof SelectionHelperUI != 'undefined') {
              SelectionHelperUI.attachEditSession(ChromeSelectionHandler,
                  event.clientX, event.clientY, event.target);
            } else {
              // If we don't have access to SelectionHelperUI then we are using this
              // binding for browser content (e.g. about:config)
              Services.obs.notifyObservers(event, "attach_edit_session_to_content", "");
            }
          }
        ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="line-wrap-label" extends="chrome://global/content/bindings/text.xml#text-label">
    <implementation>
      <constructor>
        <![CDATA[
          if (this.hasAttribute("value")) {
            this.textContent = this.getAttribute("value");
            this.removeAttribute("value");
          }
        ]]>
      </constructor>
      <property name="value">
        <getter>
          <![CDATA[
            return this.textContent;
          ]]>
        </getter>
        <setter>
          <![CDATA[
            return (this.textContent = val);
          ]]>
        </setter>
      </property>
    </implementation>
  </binding>
  <binding id="line-wrap-text-link" extends="chrome://global/content/bindings/text.xml#text-link">
    <implementation>
      <constructor>
        <![CDATA[
          if (this.hasAttribute("value")) {
            this.textContent = this.getAttribute("value");
            this.removeAttribute("value");
          }
        ]]>
      </constructor>
      <property name="value">
        <getter>
          <![CDATA[
            return this.textContent;
          ]]>
        </getter>
        <setter>
          <![CDATA[
            return (this.textContent = val);
          ]]>
        </setter>
      </property>
    </implementation>
  </binding>

</bindings>
