home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 April / CMCD0404.ISO / Software / Complet / thunderbird / chrome / mail.jar / content / editor / EdSelectProps.js < prev    next >
Encoding:
JavaScript  |  2003-05-27  |  25.2 KB  |  780 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Selection List Properties Dialog.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Neil Rashbrook.
  18.  * Portions created by the Initial Developer are Copyright (C) 2001
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s): Neil Rashbrook <neil@parkwaycc.co.uk>
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  25.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the MPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the MPL, the GPL or the LGPL.
  34.  *
  35.  * ***** END LICENSE BLOCK ***** */
  36.  
  37. // Global variables
  38.  
  39. var atomService = Components.classes["@mozilla.org/atom-service;1"]
  40.                             .getService(Components.interfaces.nsIAtomService);
  41. var checkedAtoms = {
  42.   "false":  atomService.getAtom("checked-false"),
  43.   "true":   atomService.getAtom("checked-true")};
  44.  
  45. var hasValue;
  46. var oldValue;
  47. var insertNew;
  48. var itemArray;
  49. var treeBoxObject;
  50. var treeSelection;
  51. var selectElement;
  52. var currentItem = null;
  53. var selectedOption = null;
  54. var selectedOptionCount = 0;
  55.  
  56. // Utility functions
  57.  
  58. function getParentIndex(index)
  59. {
  60.   switch (itemArray[index].level)
  61.   {
  62.   case 0: return -1;
  63.   case 1: return 0;
  64.   }
  65.   while (itemArray[--index].level > 1);
  66.   return index;
  67. }
  68.  
  69. function UpdateSelectMultiple()
  70. {
  71.   if (selectedOptionCount > 1)
  72.   {
  73.     gDialog.selectMultiple.checked = true;
  74.     gDialog.selectMultiple.disabled = true;
  75.   }
  76.   else
  77.     gDialog.selectMultiple.disabled = false;
  78. }
  79.  
  80. /* wrapper objects:
  81.  * readonly attribute Node element; // DOM node (select/optgroup/option)
  82.  * readonly attribute int level; // tree depth
  83.  * readonly attribute boolean container; // can contain options
  84.  * string getCellText(string col); // tree view helper
  85.  * string cycleCell(int currentIndex); // tree view helper
  86.  * void onFocus(); // load data into deck
  87.  * void onBlur(); // save data from deck
  88.  * boolean canDestroy(boolean prompt); // NB prompt not used
  89.  * void destroy(); // post remove callback
  90.  * void moveUp();
  91.  * boolean canMoveDown();
  92.  * void moveDown();
  93.  * void appendOption(newElement, currentIndex);
  94.  */
  95.  
  96. // OPTION element wrapper object
  97.  
  98. // Create a wrapper for the given element at the given level
  99. function optionObject(option, level)
  100. {
  101.   // select an added option (when loading from document)
  102.   if (option.hasAttribute("selected"))
  103.     selectedOptionCount++;
  104.   this.level = level;
  105.   this.element = option;
  106. }
  107.  
  108. optionObject.prototype.container = false;
  109.  
  110. optionObject.prototype.getCellText = function getCellText(column)
  111. {
  112.   if (column == "SelectSelCol")
  113.     return "";
  114.   if (column == "SelectValCol" && this.element.hasAttribute("value"))
  115.     return this.element.getAttribute("value");
  116.   return this.element.text;
  117. }
  118.  
  119. optionObject.prototype.cycleCell = function cycleCell(index)
  120. {
  121.   if (this.element.hasAttribute("selected"))
  122.   {
  123.     this.element.removeAttribute("selected");
  124.     selectedOptionCount--;
  125.     selectedOption = null;
  126.   }
  127.   else
  128.   {
  129.     // Different handling for multiselect lists
  130.     if (gDialog.selectMultiple.checked || !selectedOption)
  131.       selectedOptionCount++;
  132.     else if (selectedOption)
  133.     {
  134.       selectedOption.removeAttribute("selected");
  135.       treeBoxObject.invalidateColumn("SelectSelCol");
  136.       selectedOption = null;
  137.     }
  138.     this.element.setAttribute("selected", "");
  139.     selectedOption = this.element;
  140.     treeBoxObject.invalidateCell(index, "SelectSelCol");
  141.   }
  142.   if (currentItem == this)
  143.     // Also update the deck
  144.     gDialog.optionSelected.setAttribute("checked", this.element.hasAttribute("selected"));
  145.   UpdateSelectMultiple();
  146. };
  147.  
  148. optionObject.prototype.onFocus = function onFocus()
  149. {
  150.   gDialog.optionText.value = this.element.text;
  151.   hasValue = this.element.hasAttribute("value");
  152.   oldValue = this.element.value;
  153.   gDialog.optionHasValue.checked = hasValue;
  154.   gDialog.optionValue.value = hasValue ? this.element.value : this.element.text;
  155.   gDialog.optionSelected.checked = this.element.hasAttribute("selected");
  156.   gDialog.optionDisabled.checked = this.element.hasAttribute("disabled");
  157.   gDialog.selectDeck.setAttribute("selectedIndex", "2");
  158. };
  159.  
  160. optionObject.prototype.onBlur = function onBlur()
  161. {
  162.   this.element.text = gDialog.optionText.value;
  163.   if (gDialog.optionHasValue.checked)
  164.     this.element.value = gDialog.optionValue.value;
  165.   else
  166.     this.element.removeAttribute("value");
  167.   if (gDialog.optionSelected.checked)
  168.     this.element.setAttribute("selected", "");
  169.   else
  170.     this.element.removeAttribute("selected");
  171.   if (gDialog.optionDisabled.checked)
  172.     this.element.setAttribute("disabled", "");
  173.   else
  174.     this.element.removeAttribute("disabled");
  175. };
  176.  
  177. optionObject.prototype.canDestroy = function canDestroy(prompt)
  178. {
  179.   return true;
  180. /*return !prompt ||
  181.     ConfirmWithTitle(GetString("DeleteOption"),
  182.                      GetString("DeleteOptionMsg"),
  183.                      GetString("DeleteOption"));*/
  184. };
  185.  
  186. optionObject.prototype.destroy = function destroy()
  187. {
  188.   // Deselect a removed option
  189.   if (this.element.hasAttribute("selected"))
  190.   {
  191.     selectedOptionCount--;
  192.     selectedOption = null;
  193.     UpdateSelectMultiple();
  194.   }
  195. };
  196.  
  197. /* 4 cases:
  198.  * a) optgroup -> optgroup
  199.  *      ...         ...
  200.  *    option        option
  201.  * b) optgroup -> option
  202.  *      option    optgroup
  203.  *      ...         ...
  204.  * c) option
  205.  *    option
  206.  * d)   option
  207.  *      option
  208.  */
  209.  
  210. optionObject.prototype.moveUp = function moveUp()
  211. {
  212.   var i;
  213.   var index = treeSelection.currentIndex;
  214.   if (itemArray[index].level < itemArray[index - 1].level + itemArray[index - 1].container)
  215.   {
  216.     // we need to repaint the tree's lines
  217.     treeBoxObject.invalidateRange(getParentIndex(index), index);
  218.     // a) option is just after an optgroup, so it becomes the last child
  219.     itemArray[index].level = 2;
  220.     treeBoxObject.view.selectionChanged();
  221.   }
  222.   else
  223.   {
  224.     // otherwise new option level is now the same as the previous item
  225.     itemArray[index].level = itemArray[index - 1].level;
  226.     // swap the option with the previous item
  227.     itemArray.splice(index, 0, itemArray.splice(--index, 1)[0]);
  228.   }
  229.   selectTreeIndex(index, true);
  230. }
  231.  
  232. optionObject.prototype.canMoveDown = function canMoveDown()
  233. {
  234.   // move down is not allowed on the last option if its level is 1
  235.   return this.level > 1 || itemArray.length - treeSelection.currentIndex > 1;
  236. }
  237.  
  238. optionObject.prototype.moveDown = function moveDown()
  239. {
  240.   var i;
  241.   var index = treeSelection.currentIndex;
  242.   if (index + 1 == itemArray.length || itemArray[index].level > itemArray[index + 1].level)
  243.   {
  244.     // we need to repaint the tree's lines
  245.     treeBoxObject.invalidateRange(getParentIndex(index), index);
  246.     // a) option is last child of an optgroup, so it moves just after
  247.     itemArray[index].level = 1;
  248.     treeBoxObject.view.selectionChanged();
  249.   }
  250.   else
  251.   {
  252.     // level increases if the option was preceding an optgroup
  253.     itemArray[index].level += itemArray[index + 1].container;
  254.     // swap the option with the next item
  255.     itemArray.splice(index, 0, itemArray.splice(++index, 1)[0]);
  256.   }
  257.   selectTreeIndex(index, true);
  258. }
  259.  
  260. optionObject.prototype.appendOption = function appendOption(child, parent)
  261. {
  262.   // special case quick check
  263.   if (this.level == 1)
  264.     return gDialog.appendOption(child, 0);
  265.  
  266.   // append the option to the parent element
  267.   parent = getParentIndex(parent);
  268.   return itemArray[parent].appendOption(child, parent);
  269. };
  270.  
  271. // OPTGROUP element wrapper object
  272.  
  273. function optgroupObject(optgroup)
  274. {
  275.   this.element = optgroup;
  276. }
  277.  
  278. optgroupObject.prototype.level = 1;
  279.  
  280. optgroupObject.prototype.container = true;
  281.  
  282. optgroupObject.prototype.getCellText = function getCellText(column)
  283. {
  284.   return column == "SelectTextCol" ? this.element.label : "";
  285. }
  286.  
  287. optgroupObject.prototype.cycleCell = function cycleCell(index)
  288. {
  289. };
  290.  
  291. optgroupObject.prototype.onFocus = function onFocus()
  292. {
  293.   gDialog.optgroupLabel.value = this.element.label;
  294.   gDialog.optgroupDisabled.checked = this.element.disabled;
  295.   gDialog.selectDeck.setAttribute("selectedIndex", "1");
  296. };
  297.  
  298. optgroupObject.prototype.onBlur = function onBlur()
  299. {
  300.   this.element.label = gDialog.optgroupLabel.value;
  301.   this.element.disabled = gDialog.optgroupDisabled.checked;
  302. };
  303.  
  304. optgroupObject.prototype.canDestroy = function canDestroy(prompt)
  305. {
  306.   // Only removing empty option groups for now
  307.   return gDialog.nextChild(treeSelection.currentIndex) - treeSelection.currentIndex == 1;
  308. /*&& (!prompt ||
  309.     ConfirmWithTitle(GetString("DeleteOptGroup"),
  310.                      GetString("DeleteOptGroupMsg"),
  311.                      GetString("DeleteOptGroup")));
  312. */
  313. };
  314.  
  315. optgroupObject.prototype.destroy = function destroy()
  316. {
  317. };
  318.  
  319. optgroupObject.prototype.moveUp = function moveUp()
  320. {
  321.   // Find the index of the previous and next elements at the same level
  322.   var index = treeSelection.currentIndex;
  323.   var i = index;
  324.   while (itemArray[--index].level > 1);
  325.   var j = gDialog.nextChild(i);
  326.   // Cut out the element, cut the array in two, then join together
  327.   var movedItems = itemArray.splice(i, j - i);
  328.   var endItems = itemArray.splice(index);
  329.   itemArray = itemArray.concat(movedItems).concat(endItems);
  330.   // Repaint the lot
  331.   treeBoxObject.invalidateRange(index, j);
  332.   selectTreeIndex(index, true);
  333. }
  334.  
  335. optgroupObject.prototype.canMoveDown = function canMoveDown()
  336. {
  337.   return gDialog.lastChild() > treeSelection.currentIndex;
  338. }
  339.  
  340. optgroupObject.prototype.moveDown = function moveDown()
  341. {
  342.   // Find the index of the next two elements at the same level
  343.   var index = treeSelection.currentIndex;
  344.   var i = gDialog.nextChild(index);
  345.   var j = gDialog.nextChild(i);
  346.   // Cut out the element, cut the array in two, then join together
  347.   var movedItems = itemArray.splice(i, j - 1);
  348.   var endItems = itemArray.splice(index);
  349.   itemArray = itemArray.concat(movedItems).concat(endItems);
  350.   // Repaint the lot
  351.   treeBoxObject.invalidateRange(index, j);
  352.   index += j - i;
  353.   selectTreeIndex(index, true);
  354. }
  355.  
  356. optgroupObject.prototype.appendOption = function appendOption(child, parent)
  357. {
  358.   var index = gDialog.nextChild(parent);
  359.   // XXX need to repaint the lines, tree won't do this
  360.   treeBoxObject.invalidatePrimaryCell(index - 1);
  361.   // insert the wrapped object as the last child
  362.   itemArray.splice(index, 0, new optionObject(child, 2));
  363.   treeBoxObject.rowCountChanged(index, 1);
  364.   selectTreeIndex(index, false);
  365. };
  366.  
  367. // dialog initialization code
  368.  
  369. function Startup()
  370. {
  371.   var editor = GetCurrentEditor();
  372.   if (!editor)
  373.   {
  374.     dump("Failed to get active editor!\n");
  375.     window.close();
  376.     return;
  377.   }
  378.  
  379.   // Get a single selected select element
  380.   const kTagName = "select";
  381.   try {
  382.     selectElement = editor.getSelectedElement(kTagName);
  383.   } catch (e) {}
  384.  
  385.   if (selectElement)
  386.     // We found an element and don't need to insert one
  387.     insertNew = false;
  388.   else
  389.   {
  390.     insertNew = true;
  391.  
  392.     // We don't have an element selected,
  393.     //  so create one with default attributes
  394.     try {
  395.       selectElement = editor.createElementWithDefaults(kTagName);
  396.     } catch (e) {}
  397.  
  398.     if(!selectElement)
  399.     {
  400.       dump("Failed to get selected element or create a new one!\n");
  401.       window.close();
  402.       return;
  403.     }
  404.   }
  405.  
  406.   // SELECT element wrapper object
  407.   gDialog = {
  408.     // useful elements
  409.     accept:           document.documentElement.getButton("accept"),
  410.     selectDeck:       document.getElementById("SelectDeck"),
  411.     selectName:       document.getElementById("SelectName"),
  412.     selectSize:       document.getElementById("SelectSize"),
  413.     selectMultiple:   document.getElementById("SelectMultiple"),
  414.     selectDisabled:   document.getElementById("SelectDisabled"),
  415.     selectTabIndex:   document.getElementById("SelectTabIndex"),
  416.     optgroupLabel:    document.getElementById("OptGroupLabel"),
  417.     optgroupDisabled: document.getElementById("OptGroupDisabled"),
  418.     optionText:       document.getElementById("OptionText"),
  419.     optionHasValue:   document.getElementById("OptionHasValue"),
  420.     optionValue:      document.getElementById("OptionValue"),
  421.     optionSelected:   document.getElementById("OptionSelected"),
  422.     optionDisabled:   document.getElementById("OptionDisabled"),
  423.     removeButton:     document.getElementById("RemoveButton"),
  424.     previousButton:   document.getElementById("PreviousButton"),
  425.     nextButton:       document.getElementById("NextButton"),
  426.     tree:             document.getElementById("SelectTree"),
  427.     // wrapper methods (except MoveUp and MoveDown)
  428.     element:          selectElement.cloneNode(false),
  429.     level:            0,
  430.     container:        true,
  431.     getCellText:      function getCellText(column)
  432.     {
  433.       return column == "SelectTextCol" ? this.element.getAttribute("name") : "";
  434.     },
  435.     cycleCell:        function cycleCell(index) {},
  436.     onFocus:          function onFocus()
  437.     {
  438.       gDialog.selectName.value = this.element.getAttribute("name");
  439.       gDialog.selectSize.value = this.element.getAttribute("size");
  440.       gDialog.selectMultiple.checked = this.element.hasAttribute("multiple");
  441.       gDialog.selectDisabled.checked = this.element.hasAttribute("disabled");
  442.       gDialog.selectTabIndex.value = this.element.getAttribute("tabindex");
  443.       this.selectDeck.setAttribute("selectedIndex", "0");
  444.       onNameInput();
  445.     },
  446.     onBlur:           function onBlur()
  447.     {
  448.       this.element.setAttribute("name", gDialog.selectName.value);
  449.       if (gDialog.selectSize.value)
  450.         this.element.setAttribute("size", gDialog.selectSize.value);
  451.       else
  452.         this.element.removeAttribute("size");
  453.       if (gDialog.selectMultiple.checked)
  454.         this.element.setAttribute("multiple", "");
  455.       else
  456.         this.element.removeAttribute("multiple");
  457.       if (gDialog.selectDisabled.checked)
  458.         this.element.setAttribute("disabled", "");
  459.       else
  460.         this.element.removeAttribute("disabled");
  461.       if (gDialog.selectTabIndex.value)
  462.         this.element.setAttribute("tabindex", gDialog.selectTabIndex.value);
  463.       else
  464.         this.element.removeAttribute("tabindex");
  465.     },
  466.     appendOption:     function appendOption(child, parent)
  467.     {
  468.       var index = itemArray.length;
  469.       // XXX need to repaint the lines, tree won't do this
  470.       treeBoxObject.invalidateRange(this.lastChild(), index);
  471.       // append the wrapped object
  472.       itemArray.push(new optionObject(child, 1));
  473.       treeBoxObject.rowCountChanged(index, 1);
  474.       selectTreeIndex(index, false);
  475.     },
  476.     canDestroy:       function canDestroy(prompt)
  477.     {
  478.       return false;
  479.     },
  480.     canMoveDown:      function canMoveDown()
  481.     {
  482.       return false;
  483.     },
  484.     // helper methods
  485.     // Find the index of the next immediate child of the select
  486.     nextChild:        function nextChild(index)
  487.     {
  488.       while (++index < itemArray.length && itemArray[index].level > 1);
  489.       return index;
  490.     },
  491.     // Find the index of the last immediate child of the select
  492.     lastChild:        function lastChild()
  493.     {
  494.       var index = itemArray.length;
  495.       while (itemArray[--index].level > 1);
  496.       return index;
  497.     }
  498.   }
  499.   // Start with the <select> wrapper
  500.   itemArray = [gDialog];
  501.  
  502.   // We modify the actual option and optgroup elements so clone them first
  503.   for (var child = selectElement.firstChild; child; child = child.nextSibling)
  504.   {
  505.     if (child.tagName == "OPTION")
  506.       itemArray.push(new optionObject(child.cloneNode(true), 1));
  507.     else if (child.tagName == "OPTGROUP")
  508.     {
  509.       itemArray.push(new optgroupObject(child.cloneNode(false)));
  510.       for (var grandchild = child.firstChild; grandchild; grandchild = grandchild.nextSibling)
  511.         if (grandchild.tagName == "OPTION")
  512.           itemArray.push(new optionObject(grandchild.cloneNode(true), 2));
  513.     }
  514.   }
  515.  
  516.   UpdateSelectMultiple();
  517.  
  518.   // Define a custom view for the tree
  519.   treeBoxObject = gDialog.tree.treeBoxObject;
  520.   treeBoxObject.view = {
  521.     QueryInterface : function QueryInterface(aIID)
  522.     {
  523.       if (aIID.equals(Components.interfaces.nsITreeView) ||
  524.           aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
  525.           aIID.equals(Components.interfaces.nsISupports))
  526.         return this;
  527.       throw Components.results.NS_NOINTERFACE;
  528.     },
  529.     // useful for debugging
  530.     get wrappedJSObject() { return this; },
  531.     get rowCount() { return itemArray.length; },
  532.     get selection() { return treeSelection; },
  533.     set selection(selection) { return treeSelection = selection; },
  534.     getRowProperties: function getRowProperties(index, column, prop) { },
  535.     // could have used a wrapper for this
  536.     getCellProperties: function getCellProperties(index, column, prop)
  537.     {
  538.       if (column == "SelectSelCol" && !itemArray[index].container)
  539.         prop.AppendElement(checkedAtoms[itemArray[index].element.hasAttribute("selected")]);
  540.     },
  541.     getColumnProperties: function getColumnProperties(column, elem, prop) { },
  542.     // get info from wrapper
  543.     isContainer: function isContainer(index) { return itemArray[index].container; },
  544.     isContainerOpen: function isContainerOpen(index) { return true; },
  545.     isContainerEmpty: function isContainerEmpty(index) { return true; },
  546.     isSeparator: function isSeparator(index) { return false; },
  547.     isSorted: function isSorted() { return false; },
  548.     // d&d not implemented yet!
  549.     canDropOn: function canDropOn(index) { return false; },
  550.     canDropBeforeAfter: function canDropBeforeAfter(index, before) { return index >= before; },
  551.     drop: function drop(index, orientation) { alert('drop:' + index + ',' + orientation); },
  552.     // same as the global helper
  553.     getParentIndex: getParentIndex,
  554.     // tree needs to know when to paint lines
  555.     hasNextSibling: function hasNextSibling(index, after)
  556.     {
  557.       if (!index)
  558.         return false;
  559.       var level = itemArray[index].level;
  560.       while (++after < itemArray.length)
  561.         switch (level - itemArray[after].level)
  562.         {
  563.         case 1: return false;
  564.         case 0: return true;
  565.         }
  566.       return false;
  567.     },
  568.     getLevel: function getLevel(index) { return itemArray[index].level; },
  569.     getImageSrc: function getImageSrc(index, column) { },
  570.     getProgressMode : function getProgressMode(index,column) { },
  571.     getCellValue: function getCellValue(index, column) { },
  572.     getCellText: function getCellText(index, column) { return itemArray[index].getCellText(column); },
  573.     setTree: function setTree(tree) { this.tree = tree; },
  574.     toggleOpenState: function toggleOpenState(index) { },
  575.     cycleHeader: function cycleHeader(col, elem) { },
  576.     selectionChanged: function selectionChanged()
  577.     {
  578.       // Save current values and update buttons and deck
  579.       if (currentItem)
  580.         currentItem.onBlur();
  581.       var currentIndex = treeSelection.currentIndex;
  582.       currentItem = itemArray[currentIndex];
  583.       gDialog.removeButton.disabled = !currentItem.canDestroy();
  584.       gDialog.previousButton.disabled = currentIndex < 2;
  585.       gDialog.nextButton.disabled = !currentItem.canMoveDown();
  586.       // For Advanced Edit
  587.       globalElement = currentItem.element;
  588.       currentItem.onFocus();
  589.     },
  590.     cycleCell: function cycleCell(index, column) { itemArray[index].cycleCell(index); },
  591.     isEditable: function isEditable(index, column) { return false; },
  592.     performAction: function performAction(action) { },
  593.     performActionOnCell: function performActionOnCell(action, index, column) { }
  594.   };
  595.   treeSelection.select(0);
  596.   currentItem = gDialog;
  597.   //onNameInput();
  598.  
  599.   SetTextboxFocus(gDialog.selectName);
  600.  
  601.   SetWindowLocation();
  602. }
  603.  
  604. // Called from Advanced Edit
  605. function InitDialog()
  606. {
  607.   currentItem.onFocus();
  608. }
  609.  
  610. // Called from Advanced Edit
  611. function ValidateData()
  612. {
  613.   currentItem.onBlur();
  614.   return true;
  615. }
  616.  
  617. function onAccept()
  618. {
  619.   // All values are valid - copy to actual element in doc or
  620.   //   element created to insert
  621.   ValidateData();
  622.  
  623.   var editor = GetCurrentEditor();
  624.  
  625.   // Coalesce into one undo transaction
  626.   editor.beginTransaction();
  627.  
  628.   try
  629.   {
  630.     editor.cloneAttributes(selectElement, gDialog.element);
  631.  
  632.     if (insertNew)
  633.       // 'true' means delete the selection before inserting
  634.       editor.insertElementAtSelection(selectElement, true);
  635.  
  636.     editor.setShouldTxnSetSelection(false);
  637.  
  638.     while (selectElement.lastChild)
  639.       editor.deleteNode(selectElement.lastChild);
  640.  
  641.     var offset = 0;
  642.     for (var i = 1; i < itemArray.length; i++)
  643.       if (itemArray[i].level > 1)
  644.         selectElement.lastChild.appendChild(itemArray[i].element);
  645.       else
  646.         editor.insertNode(itemArray[i].element, selectElement, offset++, true);
  647.  
  648.     editor.setShouldTxnSetSelection(true);
  649.   }
  650.   finally
  651.   {
  652.     editor.endTransaction();
  653.   }
  654.  
  655.   SaveWindowLocation();
  656.  
  657.   return true;
  658. }
  659.  
  660. // Button actions
  661. function AddOption()
  662. {
  663.   currentItem.appendOption(GetCurrentEditor().createElementWithDefaults("option"), treeSelection.currentIndex);
  664.   SetTextboxFocus(gDialog.optionText);
  665. }
  666.  
  667. function AddOptGroup()
  668. {
  669.   var optgroupElement = GetCurrentEditor().createElementWithDefaults("optgroup");
  670.   var index = itemArray.length;
  671.   // XXX need to repaint the lines, tree won't do this
  672.   treeBoxObject.invalidateRange(gDialog.lastChild(), index);
  673.   // append the wrapped object
  674.   itemArray.push(new optgroupObject(optgroupElement));
  675.   treeBoxObject.rowCountChanged(index, 1);
  676.   selectTreeIndex(index, false);
  677.   SetTextboxFocus(gDialog.optgroupLabel);
  678. }
  679.  
  680. function RemoveElement()
  681. {
  682.   if (currentItem.canDestroy(true))
  683.   {
  684.     // Only removing empty option groups for now
  685.     var index = treeSelection.currentIndex;
  686.     var level = itemArray[index].level;
  687.     // Perform necessary cleanup and remove the wrapper
  688.     itemArray[index].destroy();
  689.     itemArray.splice(index, 1);
  690.     --index;
  691.     // XXX need to repaint the lines, tree won't do this
  692.     if (level == 1) {
  693.       var last = gDialog.lastChild();
  694.       if (index > last)
  695.         treeBoxObject.invalidateRange(last, index);
  696.     }
  697.     selectTreeIndex(index, true);
  698.     treeBoxObject.rowCountChanged(++index, -1);
  699.   }
  700. }
  701.  
  702. // Event handler
  703. function onTreeKeyUp(event)
  704. {
  705.   if (event.keyCode == event.DOM_VK_SPACE)
  706.     currentItem.cycleCell();
  707. }
  708.  
  709. function onNameInput()
  710. {
  711.   var disabled = !gDialog.selectName.value;
  712.   if (gDialog.accept.disabled != disabled)
  713.     gDialog.accept.disabled = disabled;
  714.   gDialog.element.setAttribute("name", gDialog.selectName.value);
  715.   // repaint the tree
  716.   treeBoxObject.invalidatePrimaryCell(treeSelection.currentIndex);
  717. }
  718.  
  719. function onLabelInput()
  720. {
  721.   currentItem.element.setAttribute("label", gDialog.optgroupLabel.value);
  722.   // repaint the tree
  723.   treeBoxObject.invalidatePrimaryCell(treeSelection.currentIndex);
  724. }
  725.  
  726. function onTextInput()
  727. {
  728.   currentItem.element.text = gDialog.optionText.value;
  729.   // repaint the tree
  730.   if (hasValue)
  731.     treeBoxObject.invalidatePrimaryCell(treeSelection.currentIndex);
  732.   else
  733.   {
  734.     gDialog.optionValue.value = gDialog.optionText.value;
  735.     treeBoxObject.invalidateRow(treeSelection.currentIndex);
  736.   }
  737. }
  738.  
  739. function onValueInput()
  740. {
  741.   gDialog.optionHasValue.checked = hasValue = true;
  742.   oldValue = gDialog.optionValue.value;
  743.   currentItem.element.setAttribute("value", oldValue);
  744.   // repaint the tree
  745.   treeBoxObject.invalidateCell(treeSelection.currentIndex, "SelectValCol");
  746. }
  747.  
  748. function onHasValueClick()
  749. {
  750.   hasValue = gDialog.optionHasValue.checked;
  751.   if (hasValue)
  752.   {
  753.     gDialog.optionValue.value = oldValue;
  754.     currentItem.element.setAttribute("value", oldValue);
  755.   }
  756.   else
  757.   {
  758.     oldValue = gDialog.optionValue.value;
  759.     gDialog.optionValue.value = gDialog.optionText.value;
  760.     currentItem.element.removeAttribute("value");
  761.   }
  762.   // repaint the tree
  763.   treeBoxObject.invalidateCell(treeSelection.currentIndex, "SelectValCol");
  764. }
  765.  
  766. function onSelectMultipleClick()
  767. {
  768.   // Recalculate the unique selected option if we need it and have lost it
  769.   if (!gDialog.selectMultiple.checked && selectedOptionCount == 1 && !selectedOption)
  770.     for (var i = 1; !(selectedOption = itemArray[i].element).hasAttribute("selected"); i++);
  771. }
  772.  
  773. function selectTreeIndex(index, focus)
  774. {
  775.   treeSelection.select(index);
  776.   treeBoxObject.ensureRowIsVisible(index);
  777.   if (focus)
  778.     gDialog.tree.focus();
  779. }
  780.