/* 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/. */
"use strict";

var SelectHelper = {
  _uiBusy: false,

  handleEvent: function(aEvent) {
    this.handleClick(aEvent.target);
  },

  handleClick: function(aTarget) {
    // if we're busy looking at a select we want to eat any clicks that
    // come to us, but not to process them
    if (this._uiBusy || !this._isMenu(aTarget) || this._isDisabledElement(aTarget))
        return;

    this._uiBusy = true;
    this.show(aTarget);
    this._uiBusy = false;
  },

  show: function(aElement) {
    let list = this.getListForElement(aElement);

    let p = new Prompt({
      window: aElement.contentDocument
    });

    if (aElement.multiple) {
      p.addButton({
        label: Strings.browser.GetStringFromName("selectHelper.closeMultipleSelectDialog")
      }).setMultiChoiceItems(list);
    } else {
      p.setSingleChoiceItems(list);
    }

    p.show((function(data) {
      let selected = data.list;

      if (aElement instanceof Ci.nsIDOMXULMenuListElement) {
        if (aElement.selectedIndex != selected[0]) {
          aElement.selectedIndex = selected[0];
          this.fireOnCommand(aElement);
        }
      } else if (aElement instanceof HTMLSelectElement) {
        let changed = false;
        let i = 0;
        this.forOptions(aElement, function(aNode) {
          if (aNode.selected && selected.indexOf(i) == -1) {
            changed = true;
            aNode.selected = false;
          } else if (!aNode.selected && selected.indexOf(i) != -1) {
            changed = true;
            aNode.selected = true;
          }
          i++;
        });

        if (changed)
          this.fireOnChange(aElement);
      }
    }).bind(this));
  },

  _isMenu: function(aElement) {
    return (aElement instanceof HTMLSelectElement ||
            aElement instanceof Ci.nsIDOMXULMenuListElement);
  },

  getListForElement: function(aElement) {
    let index = 0;
    let items = [];
    this.forOptions(aElement, function(aNode, aOptions, aParent) {
      let item = {
        label: aNode.text || aNode.label,
        header: aOptions.isGroup,
        disabled: aNode.disabled,
        id: index,
        selected: aNode.selected
      }

      if (aParent) {
        item.child = true;
        item.disabled = item.disabled || aParent.disabled;
      }
      items.push(item);

      index++;
    });
    return items;
  },

  forOptions: function(aElement, aFunction, aParent = null) {
    let element = aElement;
    if (aElement instanceof Ci.nsIDOMXULMenuListElement)
      element = aElement.menupopup;
    let children = element.children;
    let numChildren = children.length;

    // if there are no children in this select, we add a dummy row so that at least something appears
    if (numChildren == 0)
      aFunction.call(this, { label: "" }, { isGroup: false }, aParent);

    for (let i = 0; i < numChildren; i++) {
      let child = children[i];
      if (child instanceof HTMLOptionElement ||
          child instanceof Ci.nsIDOMXULSelectControlItemElement) {
        aFunction.call(this, child, { isGroup: false }, aParent);
      } else if (child instanceof HTMLOptGroupElement) {
        aFunction.call(this, child, { isGroup: true });
        this.forOptions(child, aFunction, child);

      }
    }
  },

  fireOnChange: function(aElement) {
    let evt = aElement.ownerDocument.createEvent("Events");
    evt.initEvent("change", true, true, aElement.defaultView, 0,
                  false, false,
                  false, false, null);
    setTimeout(function() {
      aElement.dispatchEvent(evt);
    }, 0);
  },

  fireOnCommand: function(aElement) {
    let evt = aElement.ownerDocument.createEvent("XULCommandEvent");
    evt.initCommandEvent("command", true, true, aElement.defaultView, 0,
                  false, false,
                  false, false, null);
    setTimeout(function() {
      aElement.dispatchEvent(evt);
    }, 0);
  },

  _isDisabledElement : function(aElement) {
    let currentElement = aElement;
    while (currentElement) {
      if (currentElement.disabled)
	return true;

      currentElement = currentElement.parentElement;
    }
    return false;
  }
};
