michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: /** michael@0: * SelectHelperUI: Provides an interface for making a choice in a list. michael@0: * Supports simultaneous selection of choices and group headers. michael@0: */ michael@0: var SelectHelperUI = { michael@0: _list: null, michael@0: michael@0: get _container() { michael@0: delete this._container; michael@0: return this._container = document.getElementById("select-container"); michael@0: }, michael@0: michael@0: get _listbox() { michael@0: delete this._listbox; michael@0: return this._listbox = document.getElementById("select-commands"); michael@0: }, michael@0: michael@0: get _menuPopup() { michael@0: let popup = document.getElementById("select-popup"); michael@0: delete this._menuPopup; michael@0: return this._menuPopup = new MenuPopup(this._container, popup); michael@0: }, michael@0: michael@0: show: function selectHelperShow(aList, aTitle, aRect) { michael@0: if (this._list) { michael@0: this.reset(); michael@0: } michael@0: michael@0: this._list = aList; michael@0: michael@0: this._listbox.setAttribute("seltype", aList.multiple ? "multiple" : "single"); michael@0: michael@0: let firstSelected = null; michael@0: michael@0: // Using a fragment prevent us to hang on huge list michael@0: let fragment = document.createDocumentFragment(); michael@0: let choices = aList.choices; michael@0: let selectedItems = []; michael@0: for (let i = 0; i < choices.length; i++) { michael@0: let choice = choices[i]; michael@0: let item = document.createElement("richlistitem"); michael@0: let label = document.createElement("label"); michael@0: michael@0: item.setAttribute("class", "option-command listitem-iconic"); michael@0: item.setAttribute("flex", "1"); michael@0: item.setAttribute("crop", "center"); michael@0: label.setAttribute("value", choice.text); michael@0: item.appendChild(label); michael@0: item.setAttribute("oldstate", "false"); michael@0: choice.disabled ? item.setAttribute("disabled", "true") michael@0: : item.removeAttribute("disabled"); michael@0: michael@0: fragment.appendChild(item); michael@0: michael@0: if (choice.group) { michael@0: item.classList.add("optgroup"); michael@0: continue; michael@0: } michael@0: michael@0: item.optionIndex = choice.optionIndex; michael@0: item.choiceIndex = i; michael@0: michael@0: if (choice.inGroup) { michael@0: item.classList.add("in-optgroup"); michael@0: } michael@0: michael@0: if (choice.selected) { michael@0: firstSelected = firstSelected || item; michael@0: selectedItems.push(item); michael@0: } michael@0: } michael@0: this._listbox.appendChild(fragment); michael@0: michael@0: this._container.addEventListener("click", this, false); michael@0: window.addEventListener("MozPrecisePointer", this, false); michael@0: this._menuPopup.show(this._positionOptions(aRect)); michael@0: michael@0: // Setup pre-selected items. Note, this has to happen after show. michael@0: this._listbox.clearSelection(); michael@0: for (let item of selectedItems) { michael@0: this._listbox.addItemToSelection(item); michael@0: item.setAttribute("oldstate", "true"); michael@0: } michael@0: this._listbox.ensureElementIsVisible(firstSelected); michael@0: }, michael@0: michael@0: reset: function selectHelperReset() { michael@0: this._updateControl(); michael@0: while (this._listbox.hasChildNodes()) michael@0: this._listbox.removeChild(this._listbox.lastChild); michael@0: this._list = null; michael@0: }, michael@0: michael@0: hide: function selectHelperHide() { michael@0: if (!this._list) michael@0: return; michael@0: michael@0: this._container.removeEventListener("click", this, false); michael@0: window.removeEventListener("MozPrecisePointer", this, false); michael@0: this._menuPopup.hide(); michael@0: this.reset(); michael@0: }, michael@0: michael@0: _positionOptions: function _positionOptions(aRect) { michael@0: let browser = Browser.selectedBrowser; michael@0: let p0 = browser.ptBrowserToClient(aRect.left, aRect.top); michael@0: let p1 = browser.ptBrowserToClient(aRect.right, aRect.bottom); michael@0: michael@0: return { michael@0: xPos: p0.x, michael@0: yPos: p1.y, michael@0: bottomAligned: false, michael@0: leftAligned: true michael@0: }; michael@0: }, michael@0: michael@0: _updateControl: function _selectHelperUpdateControl() { michael@0: Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { }); michael@0: }, michael@0: michael@0: handleEvent: function selectHelperHandleEvent(aEvent) { michael@0: switch (aEvent.type) { michael@0: case "MozPrecisePointer": michael@0: this.hide(); michael@0: break; michael@0: case "click": michael@0: let item = aEvent.target; michael@0: if (item && item.hasOwnProperty("optionIndex")) { michael@0: if (this._list.multiple) { michael@0: // item.selected is always true here since that's how richlistbox handles michael@0: // mouse click events. We track our own state so that we can toggle here michael@0: // on click events. Iniial 'oldstate' values are setup in show above. michael@0: if (item.getAttribute("oldstate") == "true") { michael@0: item.setAttribute("oldstate", "false"); michael@0: } else { michael@0: item.setAttribute("oldstate", "true"); michael@0: } michael@0: // Fix up selected items - richlistbox will clear selection on click events michael@0: // so we need to set selection on the items the user has previously chosen. michael@0: this._listbox.clearSelection(); michael@0: for (let node of this._listbox.childNodes) { michael@0: if (node.getAttribute("oldstate") == "true") { michael@0: this._listbox.addItemToSelection(node); michael@0: } michael@0: } michael@0: } michael@0: // Let the form element know we've added or removed a selected item. michael@0: this.onSelect(item.optionIndex, item.selected); michael@0: } michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: onSelect: function selectHelperOnSelect(aIndex, aSelected) { michael@0: Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceSelect", { michael@0: index: aIndex, michael@0: selected: aSelected michael@0: }); michael@0: if (!this._list.multiple) { michael@0: this.hide(); michael@0: } michael@0: } michael@0: };