/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Selection List Properties Dialog. * * The Initial Developer of the Original Code is * Neil Rashbrook. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): Neil Rashbrook * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Global variables var hasValue; var oldValue; var insertNew; var globalArray; var selectElement; var currentObject = null; var selectedOption = 0; var selectedOptionCount = 0; // Utility functions function GetObjectForTreeItem(treeItem) { return globalArray[parseInt(treeItem.getAttribute("index"))]; } function InsertBefore(parent, before, after) { var savedObject = currentObject; before.parentObject = parent; parent.element.insertBefore(before.element, after.element); parent.treeChildren.insertBefore(before.treeItem, after.treeItem); gDialog.tree.focus(); gDialog.tree.selectItem(savedObject.treeItem); gDialog.tree.ensureElementIsVisible(savedObject.treeItem); gDialog.previousButton.disabled = !savedObject.canMoveUp(); gDialog.nextButton.disabled = !savedObject.canMoveDown(); } function AppendChild(parent, option) { var savedObject = currentObject; option.parentObject = parent; parent.treeChildren.appendChild(option.treeItem); parent.element.appendChild(option.element); gDialog.tree.focus(); gDialog.tree.selectItem(savedObject.treeItem); gDialog.tree.ensureElementIsVisible(savedObject.treeItem); gDialog.previousButton.disabled = !savedObject.canMoveUp(); gDialog.nextButton.disabled = !savedObject.canMoveDown(); } function UpdateSelectMultiple() { if (selectedOptionCount > 1) { gDialog.selectMultiple.checked = true; gDialog.selectMultiple.disabled = true; } else gDialog.selectMultiple.disabled = false; } function createXUL(localName) { return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", localName); } // OPTION element wrapper object function optionObject(parent, option) { if (option.hasAttribute("selected")) selectedOptionCount++; this.globalIndex = globalArray.length; globalArray[globalArray.length++] = this; this.parentObject = parent; this.element = option; this.treeItem = createXUL("treeitem"); this.treeRow = createXUL("treerow"); this.treeCellText = createXUL("treecell"); this.treeCellValue = createXUL("treecell"); this.treeCellSelected = createXUL("treecell"); this.treeCellCheckbox = createXUL("checkbox"); this.treeItem.setAttribute("index", this.globalIndex); this.treeItem.setAttribute("container", "true"); this.treeItem.setAttribute("empty", "true"); this.treeCellText.setAttribute("class", "treecell-indent"); this.treeCellText.setAttribute("notwisty", "true"); this.treeCellText.setAttribute("label", option.text); if (option.hasAttribute("value")) this.treeCellValue.setAttribute("label", option.value); else this.treeCellValue.setAttribute("label", option.text); this.treeCellCheckbox.setAttribute("allowevents", "true"); this.treeCellCheckbox.setAttribute("checked", option.hasAttribute("selected")); this.treeCellCheckbox.setAttribute("style", "-moz-user-focus: ignore; margin: 1px; "); this.treeCellSelected.appendChild(this.treeCellCheckbox); this.treeRow.appendChild(this.treeCellText); this.treeRow.appendChild(this.treeCellValue); this.treeRow.appendChild(this.treeCellSelected); this.treeItem.appendChild(this.treeRow); parent.treeChildren.appendChild(this.treeItem); gDialog.treeItem.setAttribute("open", "true"); } optionObject.prototype.onSpace = function onSpace() { if (this.element.hasAttribute("selected")) { selectedOptionCount--; this.element.removeAttribute("selected"); this.treeCellCheckbox.setAttribute("checked", "false"); gDialog.optionSelected.setAttribute("checked", "false"); selectedOption = 0; } else { if (gDialog.selectMultiple.checked || !globalArray[selectedOption].element.hasAttribute("selected")) selectedOptionCount++; else { globalArray[selectedOption].element.removeAttribute("selected"); globalArray[selectedOption].treeCellCheckbox.setAttribute("checked", "false"); } this.element.setAttribute("selected", ""); this.treeCellCheckbox.setAttribute("checked", "true"); gDialog.optionSelected.setAttribute("checked", "true"); selectedOption = this.globalIndex; } UpdateSelectMultiple(); }; optionObject.prototype.onFocus = function onFocus() { gDialog.optionText.value = this.element.text; hasValue = this.element.hasAttribute("value"); oldValue = this.element.value; gDialog.optionHasValue.checked = hasValue; gDialog.optionValue.value = hasValue ? this.element.value : this.element.text; gDialog.optionSelected.checked = this.element.hasAttribute("selected"); gDialog.optionDisabled.checked = this.element.hasAttribute("disabled"); gDialog.selectDeck.setAttribute("selectedIndex", "2"); }; optionObject.prototype.onBlur = function onBlur() { this.element.text = gDialog.optionText.value; if (gDialog.optionHasValue.checked) this.element.value = gDialog.optionValue.value; else this.element.removeAttribute("value"); if (gDialog.optionSelected.checked) this.element.setAttribute("selected", ""); else this.element.removeAttribute("selected"); if (gDialog.optionDisabled.checked) this.element.setAttribute("disabled", ""); else this.element.removeAttribute("disabled"); }; optionObject.prototype.canDestroy = function canDestroy(prompt) { return true; /*return !prompt || ConfirmWithTitle(GetString("DeleteOption"), GetString("DeleteOptionMsg"), GetString("DeleteOption"));*/ }; optionObject.prototype.destroy = function destroy() { if (this.element.hasAttribute("selected")) { selectedOptionCount--; UpdateSelectMultiple(); } this.parentObject.removeChild(this); }; optionObject.prototype.canMoveUp = function canMoveUp() { return this.treeItem.previousSibling || this.parentObject != gDialog; } optionObject.prototype.moveUp = function moveUp() { if (this.treeItem.previousSibling) GetObjectForTreeItem(this.treeItem.previousSibling).insertAfter(this); else InsertBefore(gDialog, this, this.parentObject); } optionObject.prototype.insertAfter = function insertAfter(option) { InsertBefore(this.parentObject, option, this); } optionObject.prototype.canMoveDown = function canMoveDown() { return this.treeItem.nextSibling || this.parentObject != gDialog; } optionObject.prototype.moveDown = function moveDown() { if (this.treeItem.nextSibling) GetObjectForTreeItem(this.treeItem.nextSibling).insertBefore(this); else if (this.parentObject.treeItem.nextSibling) InsertBefore(gDialog, this, GetObjectForTreeItem(this.parentObject.treeItem.nextSibling)); else AppendChild(gDialog, this); } optionObject.prototype.insertBefore = function insertBefore(option) { InsertBefore(this.parentObject, this, option); } optionObject.prototype.appendChild = function appendChild(child) { return this.parentObject.appendChild(child); }; // OPTGROUP element wrapper object function optgroupObject(parent, optgroup) { this.globalIndex = globalArray.length; globalArray[globalArray.length++] = this; this.parentObject = parent; this.element = optgroup; this.treeItem = createXUL("treeitem"); this.treeRow = createXUL("treerow"); this.treeCell = createXUL("treecell"); this.treeChildren = createXUL("treechildren"); this.treeItem.setAttribute("index", this.globalIndex); this.treeItem.setAttribute("container", "true"); this.treeItem.setAttribute("empty", "true"); this.treeItem.setAttribute("open", "true"); this.treeCell.setAttribute("class", "treecell-indent"); this.treeCell.setAttribute("notwisty", "true"); this.treeCell.setAttribute("label", optgroup.label); this.treeRow.appendChild(this.treeCell); this.treeItem.appendChild(this.treeRow); this.treeItem.appendChild(this.treeChildren); parent.treeChildren.appendChild(this.treeItem); gDialog.treeItem.setAttribute("open", "true"); for (var child = optgroup.firstChild; child; child = child.nextSibling) if (child.tagName == "OPTION") new optionObject(this, child); } optgroupObject.prototype.onSpace = function onSpace() { }; optgroupObject.prototype.onFocus = function onFocus() { gDialog.optgroupLabel.value = this.element.label; gDialog.optgroupDisabled.checked = this.element.disabled; gDialog.selectDeck.setAttribute("selectedIndex", "1"); }; optgroupObject.prototype.onBlur = function onBlur() { this.element.label = gDialog.optgroupLabel.value; this.element.disabled = gDialog.optgroupDisabled.checked; }; optgroupObject.prototype.canDestroy = function canDestroy(prompt) { return !this.element.firstChild; /*return !this.element.firstChild && (!prompt || ConfirmWithTitle(GetString("DeleteOptGroup"), GetString("DeleteOptGroupMsg"), GetString("DeleteOptGroup"))); */ }; optgroupObject.prototype.destroy = function destroy() { gDialog.removeChild(this); }; optgroupObject.prototype.canMoveUp = function canMoveUp() { return this.treeItem.previousSibling; } optgroupObject.prototype.moveUp = function moveUp() { InsertBefore(this.parentObject, this, GetObjectForTreeItem(this.treeItem.previousSibling)); } optgroupObject.prototype.insertBefore = function insertBefore(option) { if (this.treeChildren.firstChild) InsertBefore(this, option, GetObjectForTreeItem(this.treeChildren.firstChild)); else AppendChild(this, option); } optgroupObject.prototype.canMoveDown = function canMoveDown() { return this.treeItem.nextSibling; } optgroupObject.prototype.moveDown = function moveDown() { InsertBefore(this.parentObject, GetObjectForTreeItem(this.treeItem.nextSibling), this); } optgroupObject.prototype.insertAfter = function insertAfter(option) { AppendChild(this, option); } optgroupObject.prototype.appendChild = function appendChild(child) { this.element.appendChild(child); return new optionObject(this, child); }; optgroupObject.prototype.removeChild = function removeChild(child) { this.element.removeChild(child.element); this.treeChildren.removeChild(child.treeItem); globalArray[child.globalIndex] = null; }; // dialog initialization code function Startup() { if (!InitEditorShell()) return; // Get a single selected select element var tagName = "select"; selectElement = editorShell.GetSelectedElement(tagName); if (selectElement) // We found an element and don't need to insert one insertNew = false; else { insertNew = true; // We don't have an element selected, // so create one with default attributes selectElement = editorShell.CreateElementWithDefaults(tagName); if(!selectElement) { dump("Failed to get selected element or create a new one!\n"); window.close(); return; } } // SELECT element wrapper object gDialog = { accept: document.documentElement.getButton("accept"), selectDeck: document.getElementById("SelectDeck"), selectName: document.getElementById("SelectName"), selectSize: document.getElementById("SelectSize"), selectMultiple: document.getElementById("SelectMultiple"), selectDisabled: document.getElementById("SelectDisabled"), selectTabIndex: document.getElementById("SelectTabIndex"), optgroupLabel: document.getElementById("OptGroupLabel"), optgroupDisabled: document.getElementById("OptGroupDisabled"), optionText: document.getElementById("OptionText"), optionHasValue: document.getElementById("OptionHasValue"), optionValue: document.getElementById("OptionValue"), optionSelected: document.getElementById("OptionSelected"), optionDisabled: document.getElementById("OptionDisabled"), removeButton: document.getElementById("RemoveButton"), previousButton: document.getElementById("PreviousButton"), nextButton: document.getElementById("NextButton"), tree: document.getElementById("SelectTree"), treeCols: document.getElementById("SelectCols"), treeItem: document.getElementById("SelectItem"), treeCell: document.getElementById("SelectCell"), treeChildren: document.getElementById("SelectChildren"), element: selectElement.cloneNode(false), globalIndex: 0, onSpace: function onSpace() {}, onFocus: function onFocus() { gDialog.selectName.value = this.element.getAttribute("name"); gDialog.selectSize.value = this.element.getAttribute("size"); gDialog.selectMultiple.checked = this.element.hasAttribute("multiple"); gDialog.selectDisabled.checked = this.element.hasAttribute("disabled"); gDialog.selectTabIndex.value = this.element.getAttribute("tabindex"); this.selectDeck.setAttribute("selectedIndex", "0"); onNameInput(); }, onBlur: function onBlur() { this.element.setAttribute("name", gDialog.selectName.value); if (gDialog.selectSize.value) this.element.setAttribute("size", gDialog.selectSize.value); else this.element.removeAttribute("size"); if (gDialog.selectMultiple.checked) this.element.setAttribute("multiple", ""); else this.element.removeAttribute("multiple"); if (gDialog.selectDisabled.checked) this.element.setAttribute("disabled", ""); else this.element.removeAttribute("disabled"); if (gDialog.selectTabIndex.value) this.element.setAttribute("tabindex", gDialog.selectTabIndex.value); else this.element.removeAttribute("tabindex"); }, appendChild: function appendChild(child) { if (child.tagName == "OPTION") { this.element.appendChild(child) return new optionObject(this, child); } if (child.tagName == "OPTGROUP") { this.element.appendChild(child) return new optgroupObject(this, child); } return null; }, removeChild: function removeChild(child) { this.treeChildren.removeChild(child.treeItem); globalArray[child.globalIndex] = null; }, canDestroy: function canDestroy(prompt) { return false; }, canMoveUp: function canMoveUp() { return false; }, canMoveDown: function canMoveDown() { return false; } } globalArray = [gDialog]; // Workaround for tree scrollbar bug for (var col = gDialog.treeCols.firstChild; col.nextSibling; col = col.nextSibling) { col.setAttribute("width", col.boxObject.width); col.removeAttribute("flex"); } // We modify the actual option and optgroup elements so clone them first for (var child = selectElement.firstChild; child; child = child.nextSibling) gDialog.appendChild(child.cloneNode(true)); UpdateSelectMultiple(); gDialog.tree.selectItem(gDialog.treeItem); onNameInput(); SetTextboxFocus(gDialog.selectName); SetWindowLocation(); } function InitDialog() { currentObject.onFocus(); } function ValidateData() { currentObject.onBlur(); return true; } function onAccept() { // All values are valid - copy to actual element in doc or // element created to insert ValidateData(); try { // Coalesce into one undo transaction editorShell.BeginBatchChanges(); editorShell.CloneAttributes(selectElement, gDialog.element); if (insertNew) // 'true' means delete the selection before inserting editorShell.InsertElementAtSelection(selectElement, true); while (selectElement.firstChild) editorShell.DeleteElement(selectElement.firstChild); var newNodes = gDialog.element.childNodes; for (var offset = 0; offset < newNodes.length; offset++) editorShell.InsertElement(newNodes[offset], selectElement, offset, true); } finally { editorShell.EndBatchChanges(); } SaveWindowLocation(); return true; } // Button actions function AddOption() { var optionElement = editorShell.CreateElementWithDefaults("option"); var optionObject = currentObject.appendChild(optionElement); gDialog.tree.selectItem(optionObject.treeItem); gDialog.tree.ensureElementIsVisible(optionObject.treeItem); SetTextboxFocus(gDialog.optionText); } function AddOptGroup() { var optgroupElement = editorShell.CreateElementWithDefaults("optgroup"); var optgroupObject = gDialog.appendChild(optgroupElement); gDialog.tree.selectItem(optgroupObject.treeItem); gDialog.tree.ensureElementIsVisible(optgroupObject.treeItem); SetTextboxFocus(gDialog.optgroupLabel); } function RemoveElement() { if (currentObject.canDestroy(true)) { var selection = currentObject.treeItem; selection = selection.nextSibling || selection.previousSibling || selection.parentNode.parentNode; currentObject.destroy(); gDialog.tree.selectItem(selection); gDialog.tree.focus(); } } // Event handlers function onTreeSelect(event) { if (currentObject) currentObject.onBlur(); var selection = gDialog.tree.selectedItems; if (selection.length) { currentObject = globalArray[parseInt(selection[0].getAttribute("index"))]; currentObject.onFocus(); } else currentObject = gDialog; gDialog.removeButton.disabled = !currentObject.canDestroy(false); gDialog.previousButton.disabled = !currentObject.canMoveUp(); gDialog.nextButton.disabled = !currentObject.canMoveDown(); globalElement = currentObject.element; } function onTreeKeyUp(event) { if (event.keyCode == event.DOM_VK_SPACE) currentObject.onSpace(); } function onTreeClicked(event) { if (event.target.localName == "checkbox") { var selection = event.target.parentNode.parentNode.parentNode; gDialog.tree.selectItem(selection); currentObject = globalArray[parseInt(selection.getAttribute("index"))]; currentObject.onSpace(); } gDialog.tree.focus(); } var timeout = 0; function copyValue(textbox, treecell, delay) { if (timeout) clearTimeout(timeout); if (delay) timeout = setTimeout(copyValue, 800, textbox, treecell, false); else { timeout = 0; treecell.setAttribute("label", textbox.value); } } function onNameInput() { var disabled = !gDialog.selectName.value; if (gDialog.accept.disabled != disabled) gDialog.accept.disabled = disabled; copyValue(gDialog.selectName, gDialog.treeCell, true); } function onNameChange() { copyValue(gDialog.selectName, gDialog.treeCell, false); } function onLabelInput() { copyValue(gDialog.optgroupLabel, currentObject.treeCell, true); } function onLabelChange() { copyValue(gDialog.optgroupLabel, currentObject.treeCell, false); } function copyText(delay) { if (timeout) clearTimeout(timeout); if (delay) timeout = setTimeout(copyText, 800, false); else { timeout = 0; currentObject.treeCellText.setAttribute("label", gDialog.optionText.value); if (!hasValue) { gDialog.optionValue.value = gDialog.optionText.value; currentObject.treeCellValue.setAttribute("label", gDialog.optionText.value); } } } function onTextInput() { copyText(true); } function onTextChange() { copyText(false); } function onValueInput() { gDialog.optionHasValue.checked = hasValue = true; oldValue = gDialog.optionValue.value; copyValue(gDialog.optionValue, currentObject.treeCellValue, true); } function onValueChange() { copyValue(gDialog.optionValue, currentObject.treeCellValue, false); } function onHasValueClick() { hasValue = gDialog.optionHasValue.checked; if (hasValue) { gDialog.optionValue.value = oldValue; } else { oldValue = gDialog.optionValue.value; gDialog.optionValue.value = gDialog.optionText.value; } currentObject.treeCellValue.setAttribute("label", gDialog.optionValue.value); } function onSelectMultipleClick() { if (!gDialog.selectMultiple.checked && selectedOptionCount == 1 && !selectedOption) while (!globalArray[selectedOption] || !globalArray[selectedOption].element.hasAttribute("selected")) selectedOption++; }