]> git.mjollnir.org Git - moodle.git/commitdiff
table-operations file that seems to only working...
authorjulmis <julmis>
Tue, 16 Mar 2004 23:22:50 +0000 (23:22 +0000)
committerjulmis <julmis>
Tue, 16 Mar 2004 23:22:50 +0000 (23:22 +0000)
lib/editor/plugins/TableOperations/table-operations.js

index 53b0bc23e1bd33dac7535838b7d3326267da1136..a376f3a488d40069b0373f598e6a88a259fbf304 100644 (file)
-// Table Operations Plugin for HTMLArea-3.0\r
-// Implementation by Mihai Bazon.  Sponsored by http://www.bloki.com\r
-//\r
-// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.\r
-// This notice MUST stay intact for use (see license.txt).\r
-//\r
-// A free WYSIWYG editor replacement for <textarea> fields.\r
-// For full source code and docs, visit http://www.interactivetools.com/\r
-//\r
-// Version 3.0 developed by Mihai Bazon for InteractiveTools.\r
-//          http://students.infoiasi.ro/~mishoo\r
-//\r
-// $Id$\r
-\r
-// Object that will encapsulate all the table operations provided by\r
-// HTMLArea-3.0 (except "insert table" which is included in the main file)\r
-function TableOperations(editor) {\r
-       this.editor = editor;\r
-\r
-       var cfg = editor.config;\r
-       var tt = TableOperations.I18N;\r
-       var bl = TableOperations.btnList;\r
-       var self = this;\r
-\r
-       // register the toolbar buttons provided by this plugin\r
-       var toolbar = ["linebreak"];\r
-       for (var i in bl) {\r
-               var btn = bl[i];\r
-               if (!btn) {\r
-                       toolbar.push("separator");\r
-               } else {\r
-                       var id = "TO-" + btn[0];\r
-                       cfg.registerButton(id, tt[id], "plugins/TableOperations/img/" + btn[0] + ".gif", false,\r
-                                          function(editor, id) {\r
-                                                  // dispatch button press event\r
-                                                  self.buttonPress(editor, id);\r
-                                          }, btn[1]);\r
-                       toolbar.push(id);\r
-               }\r
-       }\r
-\r
-       // add a new line in the toolbar\r
-       cfg.toolbar.push(toolbar);\r
-};\r
-\r
-/************************\r
- * UTILITIES\r
- ************************/\r
-\r
-// retrieves the closest element having the specified tagName in the list of\r
-// ancestors of the current selection/caret.\r
-TableOperations.prototype.getClosest = function(tagName) {\r
-       var editor = this.editor;\r
-       var ancestors = editor.getAllAncestors();\r
-       var ret = null;\r
-       tagName = ("" + tagName).toLowerCase();\r
-       for (var i in ancestors) {\r
-               var el = ancestors[i];\r
-               if (el.tagName.toLowerCase() == tagName) {\r
-                       ret = el;\r
-                       break;\r
-               }\r
-       }\r
-       return ret;\r
-};\r
-\r
-// this function requires the file PopupDiv/PopupWin to be loaded from browser\r
-TableOperations.prototype.dialogTableProperties = function() {\r
-       var i18n = TableOperations.I18N;\r
-       // retrieve existing values\r
-       var table = this.getClosest("table");\r
-       // this.editor.selectNodeContents(table);\r
-       // this.editor.updateToolbar();\r
-\r
-       var dialog = new PopupWin(this.editor, i18n["Table Properties"], function(dialog, params) {\r
-               TableOperations.processStyle(params, table);\r
-               for (var i in params) {\r
-                       var val = params[i];\r
-                       switch (i) {\r
-                           case "f_caption":\r
-                               if (/\S/.test(val)) {\r
-                                       // contains non white-space characters\r
-                                       var caption = table.getElementsByTagName("caption")[0];\r
-                                       if (!caption) {\r
-                                               caption = dialog.editor._doc.createElement("caption");\r
-                                               table.insertBefore(caption, table.firstChild);\r
-                                       }\r
-                                       caption.innerHTML = val;\r
-                               } else {\r
-                                       // search for caption and delete it if found\r
-                                       var caption = table.getElementsByTagName("caption")[0];\r
-                                       if (caption) {\r
-                                               caption.parentNode.removeChild(caption);\r
-                                       }\r
-                               }\r
-                               break;\r
-                           case "f_summary":\r
-                               table.summary = val;\r
-                               break;\r
-                           case "f_width":\r
-                               table.style.width = ("" + val) + params.f_unit;\r
-                               break;\r
-                           case "f_align":\r
-                               table.align = val;\r
-                               break;\r
-                           case "f_spacing":\r
-                               table.cellSpacing = val;\r
-                               break;\r
-                           case "f_padding":\r
-                               table.cellPadding = val;\r
-                               break;\r
-                           case "f_borders":\r
-                               table.border = val;\r
-                               break;\r
-                           case "f_frames":\r
-                               table.frame = val;\r
-                               break;\r
-                           case "f_rules":\r
-                               table.rules = val;\r
-                               break;\r
-                       }\r
-               }\r
-               // various workarounds to refresh the table display (Gecko,\r
-               // what's going on?! do not disappoint me!)\r
-               dialog.editor.forceRedraw();\r
-               dialog.editor.focusEditor();\r
-               dialog.editor.updateToolbar();\r
-               var save_collapse = table.style.borderCollapse;\r
-               table.style.borderCollapse = "collapse";\r
-               table.style.borderCollapse = "separate";\r
-               table.style.borderCollapse = save_collapse;\r
-       },\r
-\r
-       // this function gets called when the dialog needs to be initialized\r
-       function (dialog) {\r
-\r
-               var f_caption = "";\r
-               var capel = table.getElementsByTagName("caption")[0];\r
-               if (capel) {\r
-                       f_caption = capel.innerHTML;\r
-               }\r
-               var f_summary = table.summary;\r
-               var f_width = parseInt(table.style.width);\r
-               isNaN(f_width) && (f_width = "");\r
-               var f_unit = /%/.test(table.style.width) ? 'percent' : 'pixels';\r
-               var f_align = table.align;\r
-               var f_spacing = table.cellSpacing;\r
-               var f_padding = table.cellPadding;\r
-               var f_borders = table.border;\r
-               var f_frames = table.frame;\r
-               var f_rules = table.rules;\r
-\r
-               function selected(val) {\r
-                       return val ? " selected" : "";\r
-               };\r
-\r
-               // dialog contents\r
-               dialog.content.style.width = "400px";\r
-               dialog.content.innerHTML = " \\r
-<div class='title'\\r
- style='background: url(" + dialog.baseURL + dialog.editor.imgURL("table-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n["Table Properties"] + "\\r
-</div> \\r
-<table style='width:100%'> \\r
-  <tr> \\r
-    <td> \\r
-      <fieldset><legend>" + i18n["Description"] + "</legend> \\r
-       <table style='width:100%'> \\r
-        <tr> \\r
-          <td class='label'>" + i18n["Caption"] + ":</td> \\r
-          <td class='value'><input type='text' name='f_caption' value='" + f_caption + "'/></td> \\r
-        </tr><tr> \\r
-          <td class='label'>" + i18n["Summary"] + ":</td> \\r
-          <td class='value'><input type='text' name='f_summary' value='" + f_summary + "'/></td> \\r
-        </tr> \\r
-       </table> \\r
-      </fieldset> \\r
-    </td> \\r
-  </tr> \\r
-  <tr><td id='--HA-layout'></td></tr> \\r
-  <tr> \\r
-    <td> \\r
-      <fieldset><legend>" + i18n["Spacing and padding"] + "</legend> \\r
-       <table style='width:100%'> \\r
-"+//        <tr> \\r
-//           <td class='label'>" + i18n["Width"] + ":</td> \\r
-//           <td><input type='text' name='f_width' value='" + f_width + "' size='5' /> \\r
-//             <select name='f_unit'> \\r
-//               <option value='%'" + selected(f_unit == "percent") + ">" + i18n["percent"] + "</option> \\r
-//               <option value='px'" + selected(f_unit == "pixels") + ">" + i18n["pixels"] + "</option> \\r
-//             </select> &nbsp;&nbsp;" + i18n["Align"] + ": \\r
-//             <select name='f_align'> \\r
-//               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \\r
-//               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \\r
-//               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \\r
-//             </select> \\r
-//           </td> \\r
-//         </tr> \\r
-"        <tr> \\r
-          <td class='label'>" + i18n["Spacing"] + ":</td> \\r
-          <td><input type='text' name='f_spacing' size='5' value='" + f_spacing + "' /> &nbsp;" + i18n["Padding"] + ":\\r
-            <input type='text' name='f_padding' size='5' value='" + f_padding + "' /> &nbsp;&nbsp;" + i18n["pixels"] + "\\r
-          </td> \\r
-        </tr> \\r
-       </table> \\r
-      </fieldset> \\r
-    </td> \\r
-  </tr> \\r
-  <tr> \\r
-    <td> \\r
-      <fieldset><legend>Frame and borders</legend> \\r
-        <table width='100%'> \\r
-          <tr> \\r
-            <td class='label'>" + i18n["Borders"] + ":</td> \\r
-            <td><input name='f_borders' type='text' size='5' value='" + f_borders + "' /> &nbsp;&nbsp;" + i18n["pixels"] + "</td> \\r
-          </tr> \\r
-          <tr> \\r
-            <td class='label'>" + i18n["Frames"] + ":</td> \\r
-            <td> \\r
-              <select name='f_frames'> \\r
-                <option value='void'" + selected(f_frames == "void") + ">" + i18n["No sides"] + "</option> \\r
-                <option value='above'" + selected(f_frames == "above") + ">" + i18n["The top side only"] + "</option> \\r
-                <option value='below'" + selected(f_frames == "below") + ">" + i18n["The bottom side only"] + "</option> \\r
-                <option value='hsides'" + selected(f_frames == "hsides") + ">" + i18n["The top and bottom sides only"] + "</option> \\r
-                <option value='vsides'" + selected(f_frames == "vsides") + ">" + i18n["The right and left sides only"] + "</option> \\r
-                <option value='lhs'" + selected(f_frames == "lhs") + ">" + i18n["The left-hand side only"] + "</option> \\r
-                <option value='rhs'" + selected(f_frames == "rhs") + ">" + i18n["The right-hand side only"] + "</option> \\r
-                <option value='box'" + selected(f_frames == "box") + ">" + i18n["All four sides"] + "</option> \\r
-              </select> \\r
-            </td> \\r
-          </tr> \\r
-          <tr> \\r
-            <td class='label'>" + i18n["Rules"] + ":</td> \\r
-            <td> \\r
-              <select name='f_rules'> \\r
-                <option value='none'" + selected(f_rules == "none") + ">" + i18n["No rules"] + "</option> \\r
-                <option value='rows'" + selected(f_rules == "rows") + ">" + i18n["Rules will appear between rows only"] + "</option> \\r
-                <option value='cols'" + selected(f_rules == "cols") + ">" + i18n["Rules will appear between columns only"] + "</option> \\r
-                <option value='all'" + selected(f_rules == "all") + ">" + i18n["Rules will appear between all rows and columns"] + "</option> \\r
-              </select> \\r
-            </td> \\r
-          </tr> \\r
-        </table> \\r
-      </fieldset> \\r
-    </td> \\r
-  </tr> \\r
-  <tr> \\r
-    <td id='--HA-style'></td> \\r
-  </tr> \\r
-</table> \\r
-";\r
-               var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, table);\r
-               var p = dialog.doc.getElementById("--HA-style");\r
-               p.appendChild(st_prop);\r
-               var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, table);\r
-               p = dialog.doc.getElementById("--HA-layout");\r
-               p.appendChild(st_layout);\r
-               dialog.modal = true;\r
-               dialog.addButtons("ok", "cancel");\r
-               dialog.showAtElement(dialog.editor._iframe, "c");\r
-       });\r
-};\r
-\r
-// this function requires the file PopupDiv/PopupWin to be loaded from browser\r
-TableOperations.prototype.dialogRowCellProperties = function(cell) {\r
-       var i18n = TableOperations.I18N;\r
-       // retrieve existing values\r
-       var element = this.getClosest(cell ? "td" : "tr");\r
-       var table = this.getClosest("table");\r
-       // this.editor.selectNodeContents(element);\r
-       // this.editor.updateToolbar();\r
-\r
-       var dialog = new PopupWin(this.editor, i18n[cell ? "Cell Properties" : "Row Properties"], function(dialog, params) {\r
-               TableOperations.processStyle(params, element);\r
-               for (var i in params) {\r
-                       var val = params[i];\r
-                       switch (i) {\r
-                           case "f_align":\r
-                               element.align = val;\r
-                               break;\r
-                           case "f_char":\r
-                               element.ch = val;\r
-                               break;\r
-                           case "f_valign":\r
-                               element.vAlign = val;\r
-                               break;\r
-                       }\r
-               }\r
-               // various workarounds to refresh the table display (Gecko,\r
-               // what's going on?! do not disappoint me!)\r
-               dialog.editor.forceRedraw();\r
-               dialog.editor.focusEditor();\r
-               dialog.editor.updateToolbar();\r
-               var save_collapse = table.style.borderCollapse;\r
-               table.style.borderCollapse = "collapse";\r
-               table.style.borderCollapse = "separate";\r
-               table.style.borderCollapse = save_collapse;\r
-       },\r
-\r
-       // this function gets called when the dialog needs to be initialized\r
-       function (dialog) {\r
-\r
-               var f_align = element.align;\r
-               var f_valign = element.vAlign;\r
-               var f_char = element.ch;\r
-\r
-               function selected(val) {\r
-                       return val ? " selected" : "";\r
-               };\r
-\r
-               // dialog contents\r
-               dialog.content.style.width = "400px";\r
-               dialog.content.innerHTML = " \\r
-<div class='title'\\r
- style='background: url(" + dialog.baseURL + dialog.editor.imgURL(cell ? "cell-prop.gif" : "row-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n[cell ? "Cell Properties" : "Row Properties"] + "</div> \\r
-<table style='width:100%'> \\r
-  <tr> \\r
-    <td id='--HA-layout'> \\r
-"+//      <fieldset><legend>" + i18n["Layout"] + "</legend> \\r
-//        <table style='width:100%'> \\r
-//         <tr> \\r
-//           <td class='label'>" + i18n["Align"] + ":</td> \\r
-//           <td> \\r
-//             <select name='f_align'> \\r
-//               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \\r
-//               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \\r
-//               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \\r
-//               <option value='char'" + selected(f_align == "char") + ">" + i18n["Char"] + "</option> \\r
-//             </select> \\r
-//             &nbsp;&nbsp;" + i18n["Char"] + ": \\r
-//             <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \\r
-//           </td> \\r
-//         </tr><tr> \\r
-//           <td class='label'>" + i18n["Vertical align"] + ":</td> \\r
-//           <td> \\r
-//             <select name='f_valign'> \\r
-//               <option value='top'" + selected(f_valign == "top") + ">" + i18n["Top"] + "</option> \\r
-//               <option value='middle'" + selected(f_valign == "middle") + ">" + i18n["Middle"] + "</option> \\r
-//               <option value='bottom'" + selected(f_valign == "bottom") + ">" + i18n["Bottom"] + "</option> \\r
-//               <option value='baseline'" + selected(f_valign == "baseline") + ">" + i18n["Baseline"] + "</option> \\r
-//             </select> \\r
-//           </td> \\r
-//         </tr> \\r
-//        </table> \\r
-//       </fieldset> \\r
-"    </td> \\r
-  </tr> \\r
-  <tr> \\r
-    <td id='--HA-style'></td> \\r
-  </tr> \\r
-</table> \\r
-";\r
-               var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element);\r
-               var p = dialog.doc.getElementById("--HA-style");\r
-               p.appendChild(st_prop);\r
-               var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element);\r
-               p = dialog.doc.getElementById("--HA-layout");\r
-               p.appendChild(st_layout);\r
-               dialog.modal = true;\r
-               dialog.addButtons("ok", "cancel");\r
-               dialog.showAtElement(dialog.editor._iframe, "c");\r
-       });\r
-};\r
-\r
-// this function gets called when some button from the TableOperations toolbar\r
-// was pressed.\r
-TableOperations.prototype.buttonPress = function(editor, button_id) {\r
-       this.editor = editor;\r
-       var mozbr = HTMLArea.is_gecko ? "<br />" : "";\r
-       var i18n = TableOperations.I18N;\r
-\r
-       // helper function that clears the content in a table row\r
-       function clearRow(tr) {\r
-               var tds = tr.getElementsByTagName("td");\r
-               for (var i = tds.length; --i >= 0;) {\r
-                       var td = tds[i];\r
-                       td.rowSpan = 1;\r
-                       td.innerHTML = mozbr;\r
-               }\r
-       };\r
-\r
-       function splitRow(td) {\r
-               var n = parseInt("" + td.rowSpan);\r
-               var nc = parseInt("" + td.colSpan);\r
-               td.rowSpan = 1;\r
-               tr = td.parentNode;\r
-               var itr = tr.rowIndex;\r
-               var trs = tr.parentNode.rows;\r
-               var index = td.cellIndex;\r
-               while (--n > 0) {\r
-                       tr = trs[++itr];\r
-                       var otd = editor._doc.createElement("td");\r
-                       otd.colSpan = td.colSpan;\r
-                       otd.innerHTML = mozbr;\r
-                       tr.insertBefore(otd, tr.cells[index]);\r
-               }\r
-               editor.forceRedraw();\r
-               editor.updateToolbar();\r
-       };\r
-\r
-       function splitCol(td) {\r
-               var nc = parseInt("" + td.colSpan);\r
-               td.colSpan = 1;\r
-               tr = td.parentNode;\r
-               var ref = td.nextSibling;\r
-               while (--nc > 0) {\r
-                       var otd = editor._doc.createElement("td");\r
-                       otd.rowSpan = td.rowSpan;\r
-                       otd.innerHTML = mozbr;\r
-                       tr.insertBefore(otd, ref);\r
-               }\r
-               editor.forceRedraw();\r
-               editor.updateToolbar();\r
-       };\r
-\r
-       function splitCell(td) {\r
-               var nc = parseInt("" + td.colSpan);\r
-               splitCol(td);\r
-               var items = td.parentNode.cells;\r
-               var index = td.cellIndex;\r
-               while (nc-- > 0) {\r
-                       splitRow(items[index++]);\r
-               }\r
-       };\r
-\r
-       function selectNextNode(el) {\r
-               var node = el.nextSibling;\r
-               while (node && node.nodeType != 1) {\r
-                       node = node.nextSibling;\r
-               }\r
-               if (!node) {\r
-                       node = el.previousSibling;\r
-                       while (node && node.nodeType != 1) {\r
-                               node = node.previousSibling;\r
-                       }\r
-               }\r
-               if (!node) {\r
-                       node = el.parentNode;\r
-               }\r
-               editor.selectNodeContents(node);\r
-       };\r
-\r
-       switch (button_id) {\r
-               // ROWS\r
-\r
-           case "TO-row-insert-above":\r
-           case "TO-row-insert-under":\r
-               var tr = this.getClosest("tr");\r
-               if (!tr) {\r
-                       break;\r
-               }\r
-               var otr = tr.cloneNode(true);\r
-               clearRow(otr);\r
-               tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr);\r
-               editor.forceRedraw();\r
-               editor.focusEditor();\r
-               break;\r
-           case "TO-row-delete":\r
-               var tr = this.getClosest("tr");\r
-               if (!tr) {\r
-                       break;\r
-               }\r
-               var par = tr.parentNode;\r
-               if (par.rows.length == 1) {\r
-                       alert(i18n["not-del-last-row"]);\r
-                       break;\r
-               }\r
-               // set the caret first to a position that doesn't\r
-               // disappear.\r
-               selectNextNode(tr);\r
-               par.removeChild(tr);\r
-               editor.forceRedraw();\r
-               editor.focusEditor();\r
-               editor.updateToolbar();\r
-               break;\r
-           case "TO-row-split":\r
-               var td = this.getClosest("td");\r
-               if (!td) {\r
-                       break;\r
-               }\r
-               splitRow(td);\r
-               break;\r
-\r
-               // COLUMNS\r
-\r
-           case "TO-col-insert-before":\r
-           case "TO-col-insert-after":\r
-               var td = this.getClosest("td");\r
-               if (!td) {\r
-                       break;\r
-               }\r
-               var rows = td.parentNode.parentNode.rows;\r
-               var index = td.cellIndex;\r
-               for (var i = rows.length; --i >= 0;) {\r
-                       var tr = rows[i];\r
-                       var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)];\r
-                       var otd = editor._doc.createElement("td");\r
-                       otd.innerHTML = mozbr;\r
-                       tr.insertBefore(otd, ref);\r
-               }\r
-               editor.focusEditor();\r
-               break;\r
-           case "TO-col-split":\r
-               var td = this.getClosest("td");\r
-               if (!td) {\r
-                       break;\r
-               }\r
-               splitCol(td);\r
-               break;\r
-           case "TO-col-delete":\r
-               var td = this.getClosest("td");\r
-               if (!td) {\r
-                       break;\r
-               }\r
-               var index = td.cellIndex;\r
-               if (td.parentNode.cells.length == 1) {\r
-                       alert(i18n["not-del-last-col"]);\r
-                       break;\r
-               }\r
-               // set the caret first to a position that doesn't disappear\r
-               selectNextNode(td);\r
-               var rows = td.parentNode.parentNode.rows;\r
-               for (var i = rows.length; --i >= 0;) {\r
-                       var tr = rows[i];\r
-                       tr.removeChild(tr.cells[index]);\r
-               }\r
-               editor.forceRedraw();\r
-               editor.focusEditor();\r
-               editor.updateToolbar();\r
-               break;\r
-\r
-               // CELLS\r
-\r
-           case "TO-cell-split":\r
-               var td = this.getClosest("td");\r
-               if (!td) {\r
-                       break;\r
-               }\r
-               splitCell(td);\r
-               break;\r
-           case "TO-cell-insert-before":\r
-           case "TO-cell-insert-after":\r
-               var td = this.getClosest("td");\r
-               if (!td) {\r
-                       break;\r
-               }\r
-               var tr = td.parentNode;\r
-               var otd = editor._doc.createElement("td");\r
-               otd.innerHTML = mozbr;\r
-               tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td);\r
-               editor.forceRedraw();\r
-               editor.focusEditor();\r
-               break;\r
-           case "TO-cell-delete":\r
-               var td = this.getClosest("td");\r
-               if (!td) {\r
-                       break;\r
-               }\r
-               if (td.parentNode.cells.length == 1) {\r
-                       alert(i18n["not-del-last-cell"]);\r
-                       break;\r
-               }\r
-               // set the caret first to a position that doesn't disappear\r
-               selectNextNode(td);\r
-               td.parentNode.removeChild(td);\r
-               editor.forceRedraw();\r
-               editor.updateToolbar();\r
-               break;\r
-           case "TO-cell-merge":\r
-               // !! FIXME: Mozilla specific !!\r
-               var sel = editor._getSelection();\r
-               var range, i = 0;\r
-               var rows = [];\r
-               var row = null;\r
-               var cells = null;\r
-               if (!HTMLArea.is_ie) {\r
-                       try {\r
-                               while (range = sel.getRangeAt(i++)) {\r
-                                       var td = range.startContainer.childNodes[range.startOffset];\r
-                                       if (td.parentNode != row) {\r
-                                               row = td.parentNode;\r
-                                               (cells) && rows.push(cells);\r
-                                               cells = [];\r
-                                       }\r
-                                       cells.push(td);\r
-                               }\r
-                       } catch(e) {/* finished walking through selection */}\r
-                       rows.push(cells);\r
-               } else {\r
-                       // Internet Explorer "browser"\r
-                       var td = this.getClosest("td");\r
-                       if (!td) {\r
-                               alert(i18n["Please click into some cell"]);\r
-                               break;\r
-                       }\r
-                       var tr = td.parentElement;\r
-                       var no_cols = prompt(i18n["How many columns would you like to merge?"], 2);\r
-                       if (!no_cols) {\r
-                               // cancelled\r
-                               break;\r
-                       }\r
-                       var no_rows = prompt(i18n["How many rows would you like to merge?"], 2);\r
-                       if (!no_rows) {\r
-                               // cancelled\r
-                               break;\r
-                       }\r
-                       var cell_index = td.cellIndex;\r
-                       while (no_rows-- > 0) {\r
-                               td = tr.cells[cell_index];\r
-                               cells = [td];\r
-                               for (var i = 1; i < no_cols; ++i) {\r
-                                       td = td.nextSibling;\r
-                                       if (!td) {\r
-                                               break;\r
-                                       }\r
-                                       cells.push(td);\r
-                               }\r
-                               rows.push(cells);\r
-                               tr = tr.nextSibling;\r
-                               if (!tr) {\r
-                                       break;\r
-                               }\r
-                       }\r
-               }\r
-               var HTML = "";\r
-               for (i = 0; i < rows.length; ++i) {\r
-                       // i && (HTML += "<br />");\r
-                       var cells = rows[i];\r
-                       for (var j = 0; j < cells.length; ++j) {\r
-                               // j && (HTML += "&nbsp;");\r
-                               var cell = cells[j];\r
-                               HTML += cell.innerHTML;\r
-                               (i || j) && (cell.parentNode.removeChild(cell));\r
-                       }\r
-               }\r
-               var td = rows[0][0];\r
-               td.innerHTML = HTML;\r
-               td.rowSpan = rows.length;\r
-               td.colSpan = rows[0].length;\r
-               editor.selectNodeContents(td);\r
-               editor.forceRedraw();\r
-               editor.focusEditor();\r
-               break;\r
-\r
-               // PROPERTIES\r
-\r
-           case "TO-table-prop":\r
-               this.dialogTableProperties();\r
-               break;\r
-\r
-           case "TO-row-prop":\r
-               this.dialogRowCellProperties(false);\r
-               break;\r
-\r
-           case "TO-cell-prop":\r
-               this.dialogRowCellProperties(true);\r
-               break;\r
-\r
-           default:\r
-               alert("Button [" + button_id + "] not yet implemented");\r
-       }\r
-};\r
-\r
-// the list of buttons added by this plugin\r
-TableOperations.btnList = [\r
-       // table properties button\r
-       ["table-prop",       "table"],\r
-       null,                   // separator\r
-\r
-       // ROWS\r
-       ["row-prop",         "tr"],\r
-       ["row-insert-above", "tr"],\r
-       ["row-insert-under", "tr"],\r
-       ["row-delete",       "tr"],\r
-       ["row-split",        "td[rowSpan!=1]"],\r
-       null,\r
-\r
-       // COLS\r
-       ["col-insert-before", "td"],\r
-       ["col-insert-after",  "td"],\r
-       ["col-delete",        "td"],\r
-       ["col-split",         "td[colSpan!=1]"],\r
-       null,\r
-\r
-       // CELLS\r
-       ["cell-prop",          "td"],\r
-       ["cell-insert-before", "td"],\r
-       ["cell-insert-after",  "td"],\r
-       ["cell-delete",        "td"],\r
-       ["cell-merge",         "tr"],\r
-       ["cell-split",         "td[colSpan!=1,rowSpan!=1]"]\r
-       ];\r
-\r
-\r
-\r
-//// GENERIC CODE [style of any element; this should be moved into a separate\r
-//// file as it'll be very useful]\r
-//// BEGIN GENERIC CODE -----------------------------------------------------\r
-\r
-TableOperations.getLength = function(value) {\r
-       var len = parseInt(value);\r
-       if (isNaN(len)) {\r
-               len = "";\r
-       }\r
-       return len;\r
-};\r
-\r
-// Applies the style found in "params" to the given element.\r
-TableOperations.processStyle = function(params, element) {\r
-       var style = element.style;\r
-       for (var i in params) {\r
-               var val = params[i];\r
-               switch (i) {\r
-                   case "f_st_backgroundColor":\r
-                       style.backgroundColor = val;\r
-                       break;\r
-                   case "f_st_color":\r
-                       style.color = val;\r
-                       break;\r
-                   case "f_st_backgroundImage":\r
-                       if (/\S/.test(val)) {\r
-                               style.backgroundImage = "url(" + val + ")";\r
-                       } else {\r
-                               style.backgroundImage = "none";\r
-                       }\r
-                       break;\r
-                   case "f_st_borderWidth":\r
-                       style.borderWidth = val;\r
-                       break;\r
-                   case "f_st_borderStyle":\r
-                       style.borderStyle = val;\r
-                       break;\r
-                   case "f_st_borderColor":\r
-                       style.borderColor = val;\r
-                       break;\r
-                   case "f_st_borderCollapse":\r
-                       style.borderCollapse = val ? "collapse" : "";\r
-                       break;\r
-                   case "f_st_width":\r
-                       if (/\S/.test(val)) {\r
-                               style.width = val + params["f_st_widthUnit"];\r
-                       } else {\r
-                               style.width = "";\r
-                       }\r
-                       break;\r
-                   case "f_st_height":\r
-                       if (/\S/.test(val)) {\r
-                               style.height = val + params["f_st_heightUnit"];\r
-                       } else {\r
-                               style.height = "";\r
-                       }\r
-                       break;\r
-                   case "f_st_textAlign":\r
-                       if (val == "char") {\r
-                               var ch = params["f_st_textAlignChar"];\r
-                               if (ch == '"') {\r
-                                       ch = '\\"';\r
-                               }\r
-                               style.textAlign = '"' + ch + '"';\r
-                       } else {\r
-                               style.textAlign = val;\r
-                       }\r
-                       break;\r
-                   case "f_st_verticalAlign":\r
-                       style.verticalAlign = val;\r
-                       break;\r
-                   case "f_st_float":\r
-                       style.cssFloat = val;\r
-                       break;\r
-//                 case "f_st_margin":\r
-//                     style.margin = val + "px";\r
-//                     break;\r
-//                 case "f_st_padding":\r
-//                     style.padding = val + "px";\r
-//                     break;\r
-               }\r
-       }\r
-};\r
-\r
-// Returns an HTML element for a widget that allows color selection.  That is,\r
-// a button that contains the given color, if any, and when pressed will popup\r
-// the sooner-or-later-to-be-rewritten select_color.html dialog allowing user\r
-// to select some color.  If a color is selected, an input field with the name\r
-// "f_st_"+name will be updated with the color value in #123456 format.\r
-TableOperations.createColorButton = function(doc, editor, color, name) {\r
-       if (!color) {\r
-               color = "";\r
-       } else if (!/#/.test(color)) {\r
-               color = HTMLArea._colorToRgb(color);\r
-       }\r
-\r
-       var df = doc.createElement("span");\r
-       var field = doc.createElement("input");\r
-       field.type = "hidden";\r
-       df.appendChild(field);\r
-       field.name = "f_st_" + name;\r
-       field.value = color;\r
-       var button = doc.createElement("span");\r
-       button.className = "buttonColor";\r
-       df.appendChild(button);\r
-       var span = doc.createElement("span");\r
-       span.className = "chooser";\r
-       // span.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";\r
-       span.style.backgroundColor = color;\r
-       button.appendChild(span);\r
-       button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }};\r
-       button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }};\r
-       span.onclick = function() {\r
-               if (this.parentNode.disabled) {\r
-                       return false;\r
-               }\r
-               editor._popupDialog("select_color.html", function(color) {\r
-                       if (color) {\r
-                               span.style.backgroundColor = "#" + color;\r
-                               field.value = "#" + color;\r
-                       }\r
-               }, color);\r
-       };\r
-       var span2 = doc.createElement("span");\r
-       span2.innerHTML = "&#x00d7;";\r
-       span2.className = "nocolor";\r
-       span2.title = TableOperations.I18N["Unset color"];\r
-       button.appendChild(span2);\r
-       span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }};\r
-       span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }};\r
-       span2.onclick = function() {\r
-               span.style.backgroundColor = "";\r
-               field.value = "";\r
-       };\r
-       return df;\r
-};\r
-\r
-TableOperations.createStyleLayoutFieldset = function(doc, editor, el) {\r
-       var i18n = TableOperations.I18N;\r
-       var fieldset = doc.createElement("fieldset");\r
-       var legend = doc.createElement("legend");\r
-       fieldset.appendChild(legend);\r
-       legend.innerHTML = i18n["Layout"];\r
-       var table = doc.createElement("table");\r
-       fieldset.appendChild(table);\r
-       table.style.width = "100%";\r
-       var tbody = doc.createElement("tbody");\r
-       table.appendChild(tbody);\r
-\r
-       var tagname = el.tagName.toLowerCase();\r
-       var tr, td, input, select, option, options, i;\r
-\r
-       if (tagname != "td" && tagname != "tr" && tagname != "th") {\r
-               tr = doc.createElement("tr");\r
-               tbody.appendChild(tr);\r
-               td = doc.createElement("td");\r
-               td.className = "label";\r
-               tr.appendChild(td);\r
-               td.innerHTML = i18n["Float"] + ":";\r
-               td = doc.createElement("td");\r
-               tr.appendChild(td);\r
-               select = doc.createElement("select");\r
-               td.appendChild(select);\r
-               select.name = "f_st_float";\r
-               options = ["None", "Left", "Right"];\r
-               for (i in options) {\r
-                       var Val = options[i];\r
-                       var val = options[i].toLowerCase();\r
-                       option = doc.createElement("option");\r
-                       option.innerHTML = i18n[Val];\r
-                       option.value = val;\r
-                       option.selected = (("" + el.style.cssFloat).toLowerCase() == val);\r
-                       select.appendChild(option);\r
-               }\r
-       }\r
-\r
-       tr = doc.createElement("tr");\r
-       tbody.appendChild(tr);\r
-       td = doc.createElement("td");\r
-       td.className = "label";\r
-       tr.appendChild(td);\r
-       td.innerHTML = i18n["Width"] + ":";\r
-       td = doc.createElement("td");\r
-       tr.appendChild(td);\r
-       input = doc.createElement("input");\r
-       input.type = "text";\r
-       input.value = TableOperations.getLength(el.style.width);\r
-       input.size = "5";\r
-       input.name = "f_st_width";\r
-       input.style.marginRight = "0.5em";\r
-       td.appendChild(input);\r
-       select = doc.createElement("select");\r
-       select.name = "f_st_widthUnit";\r
-       option = doc.createElement("option");\r
-       option.innerHTML = i18n["percent"];\r
-       option.value = "%";\r
-       option.selected = /%/.test(el.style.width);\r
-       select.appendChild(option);\r
-       option = doc.createElement("option");\r
-       option.innerHTML = i18n["pixels"];\r
-       option.value = "px";\r
-       option.selected = /px/.test(el.style.width);\r
-       select.appendChild(option);\r
-       td.appendChild(select);\r
-\r
-       select.style.marginRight = "0.5em";\r
-       td.appendChild(doc.createTextNode(i18n["Text align"] + ":"));\r
-       select = doc.createElement("select");\r
-       select.style.marginLeft = select.style.marginRight = "0.5em";\r
-       td.appendChild(select);\r
-       select.name = "f_st_textAlign";\r
-       options = ["Left", "Center", "Right", "Justify"];\r
-       if (tagname == "td") {\r
-               options.push("Char");\r
-       }\r
-       input = doc.createElement("input");\r
-       input.name = "f_st_textAlignChar";\r
-       input.size = "1";\r
-       input.style.fontFamily = "monospace";\r
-       td.appendChild(input);\r
-       for (i in options) {\r
-               var Val = options[i];\r
-               var val = Val.toLowerCase();\r
-               option = doc.createElement("option");\r
-               option.value = val;\r
-               option.innerHTML = i18n[Val];\r
-               option.selected = (el.style.textAlign.toLowerCase() == val);\r
-               select.appendChild(option);\r
-       }\r
-       function setCharVisibility(value) {\r
-               input.style.visibility = value ? "visible" : "hidden";\r
-               if (value) {\r
-                       input.focus();\r
-                       input.select();\r
-               }\r
-       };\r
-       select.onchange = function() { setCharVisibility(this.value == "char"); };\r
-       setCharVisibility(select.value == "char");\r
-\r
-       tr = doc.createElement("tr");\r
-       tbody.appendChild(tr);\r
-       td = doc.createElement("td");\r
-       td.className = "label";\r
-       tr.appendChild(td);\r
-       td.innerHTML = i18n["Height"] + ":";\r
-       td = doc.createElement("td");\r
-       tr.appendChild(td);\r
-       input = doc.createElement("input");\r
-       input.type = "text";\r
-       input.value = TableOperations.getLength(el.style.height);\r
-       input.size = "5";\r
-       input.name = "f_st_height";\r
-       input.style.marginRight = "0.5em";\r
-       td.appendChild(input);\r
-       select = doc.createElement("select");\r
-       select.name = "f_st_heightUnit";\r
-       option = doc.createElement("option");\r
-       option.innerHTML = i18n["percent"];\r
-       option.value = "%";\r
-       option.selected = /%/.test(el.style.height);\r
-       select.appendChild(option);\r
-       option = doc.createElement("option");\r
-       option.innerHTML = i18n["pixels"];\r
-       option.value = "px";\r
-       option.selected = /px/.test(el.style.height);\r
-       select.appendChild(option);\r
-       td.appendChild(select);\r
-\r
-       select.style.marginRight = "0.5em";\r
-       td.appendChild(doc.createTextNode(i18n["Vertical align"] + ":"));\r
-       select = doc.createElement("select");\r
-       select.name = "f_st_verticalAlign";\r
-       select.style.marginLeft = "0.5em";\r
-       td.appendChild(select);\r
-       options = ["Top", "Middle", "Bottom", "Baseline"];\r
-       for (i in options) {\r
-               var Val = options[i];\r
-               var val = Val.toLowerCase();\r
-               option = doc.createElement("option");\r
-               option.value = val;\r
-               option.innerHTML = i18n[Val];\r
-               option.selected = (el.style.verticalAlign.toLowerCase() == val);\r
-               select.appendChild(option);\r
-       }\r
-\r
-       return fieldset;\r
-};\r
-\r
-// Returns an HTML element containing the style attributes for the given\r
-// element.  This can be easily embedded into any dialog; the functionality is\r
-// also provided.\r
-TableOperations.createStyleFieldset = function(doc, editor, el) {\r
-       var i18n = TableOperations.I18N;\r
-       var fieldset = doc.createElement("fieldset");\r
-       var legend = doc.createElement("legend");\r
-       fieldset.appendChild(legend);\r
-       legend.innerHTML = i18n["CSS Style"];\r
-       var table = doc.createElement("table");\r
-       fieldset.appendChild(table);\r
-       table.style.width = "100%";\r
-       var tbody = doc.createElement("tbody");\r
-       table.appendChild(tbody);\r
-\r
-       var tr, td, input, select, option, options, i;\r
-\r
-       tr = doc.createElement("tr");\r
-       tbody.appendChild(tr);\r
-       td = doc.createElement("td");\r
-       tr.appendChild(td);\r
-       td.className = "label";\r
-       td.innerHTML = i18n["Background"] + ":";\r
-       td = doc.createElement("td");\r
-       tr.appendChild(td);\r
-       var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor");\r
-       df.firstChild.nextSibling.style.marginRight = "0.5em";\r
-       td.appendChild(df);\r
-       td.appendChild(doc.createTextNode(i18n["Image URL"] + ": "));\r
-       input = doc.createElement("input");\r
-       input.type = "text";\r
-       input.name = "f_st_backgroundImage";\r
-       if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) {\r
-               input.value = RegExp.$1;\r
-       }\r
-       // input.style.width = "100%";\r
-       td.appendChild(input);\r
-\r
-       tr = doc.createElement("tr");\r
-       tbody.appendChild(tr);\r
-       td = doc.createElement("td");\r
-       tr.appendChild(td);\r
-       td.className = "label";\r
-       td.innerHTML = i18n["FG Color"] + ":";\r
-       td = doc.createElement("td");\r
-       tr.appendChild(td);\r
-       td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color"));\r
-\r
-       // for better alignment we include an invisible field.\r
-       input = doc.createElement("input");\r
-       input.style.visibility = "hidden";\r
-       input.type = "text";\r
-       td.appendChild(input);\r
-\r
-       tr = doc.createElement("tr");\r
-       tbody.appendChild(tr);\r
-       td = doc.createElement("td");\r
-       tr.appendChild(td);\r
-       td.className = "label";\r
-       td.innerHTML = i18n["Border"] + ":";\r
-       td = doc.createElement("td");\r
-       tr.appendChild(td);\r
-\r
-       var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor");\r
-       var btn = colorButton.firstChild.nextSibling;\r
-       td.appendChild(colorButton);\r
-       // borderFields.push(btn);\r
-       btn.style.marginRight = "0.5em";\r
-\r
-       select = doc.createElement("select");\r
-       var borderFields = [];\r
-       td.appendChild(select);\r
-       select.name = "f_st_borderStyle";\r
-       options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"];\r
-       var currentBorderStyle = el.style.borderStyle;\r
-       // Gecko reports "solid solid solid solid" for "border-style: solid".\r
-       // That is, "top right bottom left" -- we only consider the first\r
-       // value.\r
-       (currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1);\r
-       for (i in options) {\r
-               var val = options[i];\r
-               option = doc.createElement("option");\r
-               option.value = val;\r
-               option.innerHTML = val;\r
-               (val == currentBorderStyle) && (option.selected = true);\r
-               select.appendChild(option);\r
-       }\r
-       select.style.marginRight = "0.5em";\r
-       function setBorderFieldsStatus(value) {\r
-               for (i in borderFields) {\r
-                       var el = borderFields[i];\r
-                       el.style.visibility = value ? "hidden" : "visible";\r
-                       if (!value && (el.tagName.toLowerCase() == "input")) {\r
-                               el.focus();\r
-                               el.select();\r
-                       }\r
-               }\r
-       };\r
-       select.onchange = function() { setBorderFieldsStatus(this.value == "none"); };\r
-\r
-       input = doc.createElement("input");\r
-       borderFields.push(input);\r
-       input.type = "text";\r
-       input.name = "f_st_borderWidth";\r
-       input.value = TableOperations.getLength(el.style.borderWidth);\r
-       input.size = "5";\r
-       td.appendChild(input);\r
-       input.style.marginRight = "0.5em";\r
-       var span = doc.createElement("span");\r
-       span.innerHTML = i18n["pixels"];\r
-       td.appendChild(span);\r
-       borderFields.push(span);\r
-\r
-       setBorderFieldsStatus(select.value == "none");\r
-\r
-       if (el.tagName.toLowerCase() == "table") {\r
-               // the border-collapse style is only for tables\r
-               tr = doc.createElement("tr");\r
-               tbody.appendChild(tr);\r
-               td = doc.createElement("td");\r
-               td.className = "label";\r
-               tr.appendChild(td);\r
-               input = doc.createElement("input");\r
-               input.type = "checkbox";\r
-               input.name = "f_st_borderCollapse";\r
-               input.id = "f_st_borderCollapse";\r
-               var val = (/collapse/i.test(el.style.borderCollapse));\r
-               input.checked = val ? 1 : 0;\r
-               td.appendChild(input);\r
-\r
-               td = doc.createElement("td");\r
-               tr.appendChild(td);\r
-               var label = doc.createElement("label");\r
-               label.htmlFor = "f_st_borderCollapse";\r
-               label.innerHTML = i18n["Collapsed borders"];\r
-               td.appendChild(label);\r
-       }\r
-\r
-//     tr = doc.createElement("tr");\r
-//     tbody.appendChild(tr);\r
-//     td = doc.createElement("td");\r
-//     td.className = "label";\r
-//     tr.appendChild(td);\r
-//     td.innerHTML = i18n["Margin"] + ":";\r
-//     td = doc.createElement("td");\r
-//     tr.appendChild(td);\r
-//     input = doc.createElement("input");\r
-//     input.type = "text";\r
-//     input.size = "5";\r
-//     input.name = "f_st_margin";\r
-//     td.appendChild(input);\r
-//     input.style.marginRight = "0.5em";\r
-//     td.appendChild(doc.createTextNode(i18n["Padding"] + ":"));\r
-\r
-//     input = doc.createElement("input");\r
-//     input.type = "text";\r
-//     input.size = "5";\r
-//     input.name = "f_st_padding";\r
-//     td.appendChild(input);\r
-//     input.style.marginLeft = "0.5em";\r
-//     input.style.marginRight = "0.5em";\r
-//     td.appendChild(doc.createTextNode(i18n["pixels"]));\r
-\r
-       return fieldset;\r
-};\r
-\r
-//// END GENERIC CODE -------------------------------------------------------\r
+// Table Operations Plugin for HTMLArea-3.0
+// Implementation by Mihai Bazon.  Sponsored by http://www.bloki.com
+//
+// htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
+// This notice MUST stay intact for use (see license.txt).
+//
+// A free WYSIWYG editor replacement for <textarea> fields.
+// For full source code and docs, visit http://www.interactivetools.com/
+//
+// Version 3.0 developed by Mihai Bazon for InteractiveTools.
+//   http://dynarch.com/mishoo
+//
+// $Id$
+
+// Object that will encapsulate all the table operations provided by
+// HTMLArea-3.0 (except "insert table" which is included in the main file)
+function TableOperations(editor) {
+       this.editor = editor;
+
+       var cfg = editor.config;
+       var tt = TableOperations.I18N;
+       var bl = TableOperations.btnList;
+       var self = this;
+
+       // register the toolbar buttons provided by this plugin
+       var toolbar = ["linebreak"];
+       for (var i in bl) {
+               var btn = bl[i];
+               if (!btn) {
+                       toolbar.push("separator");
+               } else {
+                       var id = "TO-" + btn[0];
+                       cfg.registerButton(id, tt[id], editor.imgURL(btn[0] + ".gif", "TableOperations"), false,
+                                          function(editor, id) {
+                                                  // dispatch button press event
+                                                  self.buttonPress(editor, id);
+                                          }, btn[1]);
+                       toolbar.push(id);
+               }
+       }
+
+       // add a new line in the toolbar
+       cfg.toolbar.push(toolbar);
+};
+
+TableOperations._pluginInfo = {
+       name          : "TableOperations",
+       version       : "1.0",
+       developer     : "Mihai Bazon",
+       developer_url : "http://dynarch.com/mishoo/",
+       c_owner       : "Mihai Bazon",
+       sponsor       : "Zapatec Inc.",
+       sponsor_url   : "http://www.bloki.com",
+       license       : "htmlArea"
+};
+
+/************************
+ * UTILITIES
+ ************************/
+
+// retrieves the closest element having the specified tagName in the list of
+// ancestors of the current selection/caret.
+TableOperations.prototype.getClosest = function(tagName) {
+       var editor = this.editor;
+       var ancestors = editor.getAllAncestors();
+       var ret = null;
+       tagName = ("" + tagName).toLowerCase();
+       for (var i in ancestors) {
+               var el = ancestors[i];
+               if (el.tagName.toLowerCase() == tagName) {
+                       ret = el;
+                       break;
+               }
+       }
+       return ret;
+};
+
+// this function requires the file PopupDiv/PopupWin to be loaded from browser
+TableOperations.prototype.dialogTableProperties = function() {
+       var i18n = TableOperations.I18N;
+       // retrieve existing values
+       var table = this.getClosest("table");
+       // this.editor.selectNodeContents(table);
+       // this.editor.updateToolbar();
+
+       var dialog = new PopupWin(this.editor, i18n["Table Properties"], function(dialog, params) {
+               TableOperations.processStyle(params, table);
+               for (var i in params) {
+                       var val = params[i];
+                       switch (i) {
+                           case "f_caption":
+                               if (/\S/.test(val)) {
+                                       // contains non white-space characters
+                                       var caption = table.getElementsByTagName("caption")[0];
+                                       if (!caption) {
+                                               caption = dialog.editor._doc.createElement("caption");
+                                               table.insertBefore(caption, table.firstChild);
+                                       }
+                                       caption.innerHTML = val;
+                               } else {
+                                       // search for caption and delete it if found
+                                       var caption = table.getElementsByTagName("caption")[0];
+                                       if (caption) {
+                                               caption.parentNode.removeChild(caption);
+                                       }
+                               }
+                               break;
+                           case "f_summary":
+                               table.summary = val;
+                               break;
+                           case "f_width":
+                               table.style.width = ("" + val) + params.f_unit;
+                               break;
+                           case "f_align":
+                               table.align = val;
+                               break;
+                           case "f_spacing":
+                               table.cellSpacing = val;
+                               break;
+                           case "f_padding":
+                               table.cellPadding = val;
+                               break;
+                           case "f_borders":
+                               table.border = val;
+                               break;
+                           case "f_frames":
+                               table.frame = val;
+                               break;
+                           case "f_rules":
+                               table.rules = val;
+                               break;
+                       }
+               }
+               // various workarounds to refresh the table display (Gecko,
+               // what's going on?! do not disappoint me!)
+               dialog.editor.forceRedraw();
+               dialog.editor.focusEditor();
+               dialog.editor.updateToolbar();
+               var save_collapse = table.style.borderCollapse;
+               table.style.borderCollapse = "collapse";
+               table.style.borderCollapse = "separate";
+               table.style.borderCollapse = save_collapse;
+       },
+
+       // this function gets called when the dialog needs to be initialized
+       function (dialog) {
+
+               var f_caption = "";
+               var capel = table.getElementsByTagName("caption")[0];
+               if (capel) {
+                       f_caption = capel.innerHTML;
+               }
+               var f_summary = table.summary;
+               var f_width = parseInt(table.style.width);
+               isNaN(f_width) && (f_width = "");
+               var f_unit = /%/.test(table.style.width) ? 'percent' : 'pixels';
+               var f_align = table.align;
+               var f_spacing = table.cellSpacing;
+               var f_padding = table.cellPadding;
+               var f_borders = table.border;
+               var f_frames = table.frame;
+               var f_rules = table.rules;
+
+               function selected(val) {
+                       return val ? " selected" : "";
+               };
+
+               // dialog contents
+               dialog.content.style.width = "400px";
+               dialog.content.innerHTML = " \
+<div class='title'\
+ style='background: url(" + dialog.baseURL + dialog.editor.imgURL("table-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n["Table Properties"] + "\
+</div> \
+<table style='width:100%'> \
+  <tr> \
+    <td> \
+      <fieldset><legend>" + i18n["Description"] + "</legend> \
+       <table style='width:100%'> \
+        <tr> \
+          <td class='label'>" + i18n["Caption"] + ":</td> \
+          <td class='value'><input type='text' name='f_caption' value='" + f_caption + "'/></td> \
+        </tr><tr> \
+          <td class='label'>" + i18n["Summary"] + ":</td> \
+          <td class='value'><input type='text' name='f_summary' value='" + f_summary + "'/></td> \
+        </tr> \
+       </table> \
+      </fieldset> \
+    </td> \
+  </tr> \
+  <tr><td id='--HA-layout'></td></tr> \
+  <tr> \
+    <td> \
+      <fieldset><legend>" + i18n["Spacing and padding"] + "</legend> \
+       <table style='width:100%'> \
+"+//        <tr> \
+//           <td class='label'>" + i18n["Width"] + ":</td> \
+//           <td><input type='text' name='f_width' value='" + f_width + "' size='5' /> \
+//             <select name='f_unit'> \
+//               <option value='%'" + selected(f_unit == "percent") + ">" + i18n["percent"] + "</option> \
+//               <option value='px'" + selected(f_unit == "pixels") + ">" + i18n["pixels"] + "</option> \
+//             </select> &nbsp;&nbsp;" + i18n["Align"] + ": \
+//             <select name='f_align'> \
+//               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
+//               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
+//               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
+//             </select> \
+//           </td> \
+//         </tr> \
+"        <tr> \
+          <td class='label'>" + i18n["Spacing"] + ":</td> \
+          <td><input type='text' name='f_spacing' size='5' value='" + f_spacing + "' /> &nbsp;" + i18n["Padding"] + ":\
+            <input type='text' name='f_padding' size='5' value='" + f_padding + "' /> &nbsp;&nbsp;" + i18n["pixels"] + "\
+          </td> \
+        </tr> \
+       </table> \
+      </fieldset> \
+    </td> \
+  </tr> \
+  <tr> \
+    <td> \
+      <fieldset><legend>Frame and borders</legend> \
+        <table width='100%'> \
+          <tr> \
+            <td class='label'>" + i18n["Borders"] + ":</td> \
+            <td><input name='f_borders' type='text' size='5' value='" + f_borders + "' /> &nbsp;&nbsp;" + i18n["pixels"] + "</td> \
+          </tr> \
+          <tr> \
+            <td class='label'>" + i18n["Frames"] + ":</td> \
+            <td> \
+              <select name='f_frames'> \
+                <option value='void'" + selected(f_frames == "void") + ">" + i18n["No sides"] + "</option> \
+                <option value='above'" + selected(f_frames == "above") + ">" + i18n["The top side only"] + "</option> \
+                <option value='below'" + selected(f_frames == "below") + ">" + i18n["The bottom side only"] + "</option> \
+                <option value='hsides'" + selected(f_frames == "hsides") + ">" + i18n["The top and bottom sides only"] + "</option> \
+                <option value='vsides'" + selected(f_frames == "vsides") + ">" + i18n["The right and left sides only"] + "</option> \
+                <option value='lhs'" + selected(f_frames == "lhs") + ">" + i18n["The left-hand side only"] + "</option> \
+                <option value='rhs'" + selected(f_frames == "rhs") + ">" + i18n["The right-hand side only"] + "</option> \
+                <option value='box'" + selected(f_frames == "box") + ">" + i18n["All four sides"] + "</option> \
+              </select> \
+            </td> \
+          </tr> \
+          <tr> \
+            <td class='label'>" + i18n["Rules"] + ":</td> \
+            <td> \
+              <select name='f_rules'> \
+                <option value='none'" + selected(f_rules == "none") + ">" + i18n["No rules"] + "</option> \
+                <option value='rows'" + selected(f_rules == "rows") + ">" + i18n["Rules will appear between rows only"] + "</option> \
+                <option value='cols'" + selected(f_rules == "cols") + ">" + i18n["Rules will appear between columns only"] + "</option> \
+                <option value='all'" + selected(f_rules == "all") + ">" + i18n["Rules will appear between all rows and columns"] + "</option> \
+              </select> \
+            </td> \
+          </tr> \
+        </table> \
+      </fieldset> \
+    </td> \
+  </tr> \
+  <tr> \
+    <td id='--HA-style'></td> \
+  </tr> \
+</table> \
+";
+               var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, table);
+               var p = dialog.doc.getElementById("--HA-style");
+               p.appendChild(st_prop);
+               var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, table);
+               p = dialog.doc.getElementById("--HA-layout");
+               p.appendChild(st_layout);
+               dialog.modal = true;
+               dialog.addButtons("ok", "cancel");
+               dialog.showAtElement(dialog.editor._iframe, "c");
+       });
+};
+
+// this function requires the file PopupDiv/PopupWin to be loaded from browser
+TableOperations.prototype.dialogRowCellProperties = function(cell) {
+       var i18n = TableOperations.I18N;
+       // retrieve existing values
+       var element = this.getClosest(cell ? "td" : "tr");
+       var table = this.getClosest("table");
+       // this.editor.selectNodeContents(element);
+       // this.editor.updateToolbar();
+
+       var dialog = new PopupWin(this.editor, i18n[cell ? "Cell Properties" : "Row Properties"], function(dialog, params) {
+               TableOperations.processStyle(params, element);
+               for (var i in params) {
+                       var val = params[i];
+                       switch (i) {
+                           case "f_align":
+                               element.align = val;
+                               break;
+                           case "f_char":
+                               element.ch = val;
+                               break;
+                           case "f_valign":
+                               element.vAlign = val;
+                               break;
+                       }
+               }
+               // various workarounds to refresh the table display (Gecko,
+               // what's going on?! do not disappoint me!)
+               dialog.editor.forceRedraw();
+               dialog.editor.focusEditor();
+               dialog.editor.updateToolbar();
+               var save_collapse = table.style.borderCollapse;
+               table.style.borderCollapse = "collapse";
+               table.style.borderCollapse = "separate";
+               table.style.borderCollapse = save_collapse;
+       },
+
+       // this function gets called when the dialog needs to be initialized
+       function (dialog) {
+
+               var f_align = element.align;
+               var f_valign = element.vAlign;
+               var f_char = element.ch;
+
+               function selected(val) {
+                       return val ? " selected" : "";
+               };
+
+               // dialog contents
+               dialog.content.style.width = "400px";
+               dialog.content.innerHTML = " \
+<div class='title'\
+ style='background: url(" + dialog.baseURL + dialog.editor.imgURL(cell ? "cell-prop.gif" : "row-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n[cell ? "Cell Properties" : "Row Properties"] + "</div> \
+<table style='width:100%'> \
+  <tr> \
+    <td id='--HA-layout'> \
+"+//      <fieldset><legend>" + i18n["Layout"] + "</legend> \
+//        <table style='width:100%'> \
+//         <tr> \
+//           <td class='label'>" + i18n["Align"] + ":</td> \
+//           <td> \
+//             <select name='f_align'> \
+//               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
+//               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
+//               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
+//               <option value='char'" + selected(f_align == "char") + ">" + i18n["Char"] + "</option> \
+//             </select> \
+//             &nbsp;&nbsp;" + i18n["Char"] + ": \
+//             <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \
+//           </td> \
+//         </tr><tr> \
+//           <td class='label'>" + i18n["Vertical align"] + ":</td> \
+//           <td> \
+//             <select name='f_valign'> \
+//               <option value='top'" + selected(f_valign == "top") + ">" + i18n["Top"] + "</option> \
+//               <option value='middle'" + selected(f_valign == "middle") + ">" + i18n["Middle"] + "</option> \
+//               <option value='bottom'" + selected(f_valign == "bottom") + ">" + i18n["Bottom"] + "</option> \
+//               <option value='baseline'" + selected(f_valign == "baseline") + ">" + i18n["Baseline"] + "</option> \
+//             </select> \
+//           </td> \
+//         </tr> \
+//        </table> \
+//       </fieldset> \
+"    </td> \
+  </tr> \
+  <tr> \
+    <td id='--HA-style'></td> \
+  </tr> \
+</table> \
+";
+               var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element);
+               var p = dialog.doc.getElementById("--HA-style");
+               p.appendChild(st_prop);
+               var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element);
+               p = dialog.doc.getElementById("--HA-layout");
+               p.appendChild(st_layout);
+               dialog.modal = true;
+               dialog.addButtons("ok", "cancel");
+               dialog.showAtElement(dialog.editor._iframe, "c");
+       });
+};
+
+// this function gets called when some button from the TableOperations toolbar
+// was pressed.
+TableOperations.prototype.buttonPress = function(editor, button_id) {
+       this.editor = editor;
+       var mozbr = HTMLArea.is_gecko ? "<br />" : "";
+       var i18n = TableOperations.I18N;
+
+       // helper function that clears the content in a table row
+       function clearRow(tr) {
+               var tds = tr.getElementsByTagName("td");
+               for (var i = tds.length; --i >= 0;) {
+                       var td = tds[i];
+                       td.rowSpan = 1;
+                       td.innerHTML = mozbr;
+               }
+       };
+
+       function splitRow(td) {
+               var n = parseInt("" + td.rowSpan);
+               var nc = parseInt("" + td.colSpan);
+               td.rowSpan = 1;
+               tr = td.parentNode;
+               var itr = tr.rowIndex;
+               var trs = tr.parentNode.rows;
+               var index = td.cellIndex;
+               while (--n > 0) {
+                       tr = trs[++itr];
+                       var otd = editor._doc.createElement("td");
+                       otd.colSpan = td.colSpan;
+                       otd.innerHTML = mozbr;
+                       tr.insertBefore(otd, tr.cells[index]);
+               }
+               editor.forceRedraw();
+               editor.updateToolbar();
+       };
+
+       function splitCol(td) {
+               var nc = parseInt("" + td.colSpan);
+               td.colSpan = 1;
+               tr = td.parentNode;
+               var ref = td.nextSibling;
+               while (--nc > 0) {
+                       var otd = editor._doc.createElement("td");
+                       otd.rowSpan = td.rowSpan;
+                       otd.innerHTML = mozbr;
+                       tr.insertBefore(otd, ref);
+               }
+               editor.forceRedraw();
+               editor.updateToolbar();
+       };
+
+       function splitCell(td) {
+               var nc = parseInt("" + td.colSpan);
+               splitCol(td);
+               var items = td.parentNode.cells;
+               var index = td.cellIndex;
+               while (nc-- > 0) {
+                       splitRow(items[index++]);
+               }
+       };
+
+       function selectNextNode(el) {
+               var node = el.nextSibling;
+               while (node && node.nodeType != 1) {
+                       node = node.nextSibling;
+               }
+               if (!node) {
+                       node = el.previousSibling;
+                       while (node && node.nodeType != 1) {
+                               node = node.previousSibling;
+                       }
+               }
+               if (!node) {
+                       node = el.parentNode;
+               }
+               editor.selectNodeContents(node);
+       };
+
+       switch (button_id) {
+               // ROWS
+
+           case "TO-row-insert-above":
+           case "TO-row-insert-under":
+               var tr = this.getClosest("tr");
+               if (!tr) {
+                       break;
+               }
+               var otr = tr.cloneNode(true);
+               clearRow(otr);
+               tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr);
+               editor.forceRedraw();
+               editor.focusEditor();
+               break;
+           case "TO-row-delete":
+               var tr = this.getClosest("tr");
+               if (!tr) {
+                       break;
+               }
+               var par = tr.parentNode;
+               if (par.rows.length == 1) {
+                       alert(i18n["not-del-last-row"]);
+                       break;
+               }
+               // set the caret first to a position that doesn't
+               // disappear.
+               selectNextNode(tr);
+               par.removeChild(tr);
+               editor.forceRedraw();
+               editor.focusEditor();
+               editor.updateToolbar();
+               break;
+           case "TO-row-split":
+               var td = this.getClosest("td");
+               if (!td) {
+                       break;
+               }
+               splitRow(td);
+               break;
+
+               // COLUMNS
+
+           case "TO-col-insert-before":
+           case "TO-col-insert-after":
+               var td = this.getClosest("td");
+               if (!td) {
+                       break;
+               }
+               var rows = td.parentNode.parentNode.rows;
+               var index = td.cellIndex;
+               for (var i = rows.length; --i >= 0;) {
+                       var tr = rows[i];
+                       var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)];
+                       var otd = editor._doc.createElement("td");
+                       otd.innerHTML = mozbr;
+                       tr.insertBefore(otd, ref);
+               }
+               editor.focusEditor();
+               break;
+           case "TO-col-split":
+               var td = this.getClosest("td");
+               if (!td) {
+                       break;
+               }
+               splitCol(td);
+               break;
+           case "TO-col-delete":
+               var td = this.getClosest("td");
+               if (!td) {
+                       break;
+               }
+               var index = td.cellIndex;
+               if (td.parentNode.cells.length == 1) {
+                       alert(i18n["not-del-last-col"]);
+                       break;
+               }
+               // set the caret first to a position that doesn't disappear
+               selectNextNode(td);
+               var rows = td.parentNode.parentNode.rows;
+               for (var i = rows.length; --i >= 0;) {
+                       var tr = rows[i];
+                       tr.removeChild(tr.cells[index]);
+               }
+               editor.forceRedraw();
+               editor.focusEditor();
+               editor.updateToolbar();
+               break;
+
+               // CELLS
+
+           case "TO-cell-split":
+               var td = this.getClosest("td");
+               if (!td) {
+                       break;
+               }
+               splitCell(td);
+               break;
+           case "TO-cell-insert-before":
+           case "TO-cell-insert-after":
+               var td = this.getClosest("td");
+               if (!td) {
+                       break;
+               }
+               var tr = td.parentNode;
+               var otd = editor._doc.createElement("td");
+               otd.innerHTML = mozbr;
+               tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td);
+               editor.forceRedraw();
+               editor.focusEditor();
+               break;
+           case "TO-cell-delete":
+               var td = this.getClosest("td");
+               if (!td) {
+                       break;
+               }
+               if (td.parentNode.cells.length == 1) {
+                       alert(i18n["not-del-last-cell"]);
+                       break;
+               }
+               // set the caret first to a position that doesn't disappear
+               selectNextNode(td);
+               td.parentNode.removeChild(td);
+               editor.forceRedraw();
+               editor.updateToolbar();
+               break;
+           case "TO-cell-merge":
+               // !! FIXME: Mozilla specific !!
+               var sel = editor._getSelection();
+               var range, i = 0;
+               var rows = [];
+               var row = null;
+               var cells = null;
+               if (!HTMLArea.is_ie) {
+                       try {
+                               while (range = sel.getRangeAt(i++)) {
+                                       var td = range.startContainer.childNodes[range.startOffset];
+                                       if (td.parentNode != row) {
+                                               row = td.parentNode;
+                                               (cells) && rows.push(cells);
+                                               cells = [];
+                                       }
+                                       cells.push(td);
+                               }
+                       } catch(e) {/* finished walking through selection */}
+                       rows.push(cells);
+               } else {
+                       // Internet Explorer "browser"
+                       var td = this.getClosest("td");
+                       if (!td) {
+                               alert(i18n["Please click into some cell"]);
+                               break;
+                       }
+                       var tr = td.parentElement;
+                       var no_cols = prompt(i18n["How many columns would you like to merge?"], 2);
+                       if (!no_cols) {
+                               // cancelled
+                               break;
+                       }
+                       var no_rows = prompt(i18n["How many rows would you like to merge?"], 2);
+                       if (!no_rows) {
+                               // cancelled
+                               break;
+                       }
+                       var cell_index = td.cellIndex;
+                       while (no_rows-- > 0) {
+                               td = tr.cells[cell_index];
+                               cells = [td];
+                               for (var i = 1; i < no_cols; ++i) {
+                                       td = td.nextSibling;
+                                       if (!td) {
+                                               break;
+                                       }
+                                       cells.push(td);
+                               }
+                               rows.push(cells);
+                               tr = tr.nextSibling;
+                               if (!tr) {
+                                       break;
+                               }
+                       }
+               }
+               var HTML = "";
+               for (i = 0; i < rows.length; ++i) {
+                       // i && (HTML += "<br />");
+                       var cells = rows[i];
+                       for (var j = 0; j < cells.length; ++j) {
+                               // j && (HTML += "&nbsp;");
+                               var cell = cells[j];
+                               HTML += cell.innerHTML;
+                               (i || j) && (cell.parentNode.removeChild(cell));
+                       }
+               }
+               var td = rows[0][0];
+               td.innerHTML = HTML;
+               td.rowSpan = rows.length;
+               td.colSpan = rows[0].length;
+               editor.selectNodeContents(td);
+               editor.forceRedraw();
+               editor.focusEditor();
+               break;
+
+               // PROPERTIES
+
+           case "TO-table-prop":
+               this.dialogTableProperties();
+               break;
+
+           case "TO-row-prop":
+               this.dialogRowCellProperties(false);
+               break;
+
+           case "TO-cell-prop":
+               this.dialogRowCellProperties(true);
+               break;
+
+           default:
+               alert("Button [" + button_id + "] not yet implemented");
+       }
+};
+
+// the list of buttons added by this plugin
+TableOperations.btnList = [
+       // table properties button
+       ["table-prop",       "table"],
+       null,                   // separator
+
+       // ROWS
+       ["row-prop",         "tr"],
+       ["row-insert-above", "tr"],
+       ["row-insert-under", "tr"],
+       ["row-delete",       "tr"],
+       ["row-split",        "td[rowSpan!=1]"],
+       null,
+
+       // COLS
+       ["col-insert-before", "td"],
+       ["col-insert-after",  "td"],
+       ["col-delete",        "td"],
+       ["col-split",         "td[colSpan!=1]"],
+       null,
+
+       // CELLS
+       ["cell-prop",          "td"],
+       ["cell-insert-before", "td"],
+       ["cell-insert-after",  "td"],
+       ["cell-delete",        "td"],
+       ["cell-merge",         "tr"],
+       ["cell-split",         "td[colSpan!=1,rowSpan!=1]"]
+       ];
+
+
+
+//// GENERIC CODE [style of any element; this should be moved into a separate
+//// file as it'll be very useful]
+//// BEGIN GENERIC CODE -----------------------------------------------------
+
+TableOperations.getLength = function(value) {
+       var len = parseInt(value);
+       if (isNaN(len)) {
+               len = "";
+       }
+       return len;
+};
+
+// Applies the style found in "params" to the given element.
+TableOperations.processStyle = function(params, element) {
+       var style = element.style;
+       for (var i in params) {
+               var val = params[i];
+               switch (i) {
+                   case "f_st_backgroundColor":
+                       style.backgroundColor = val;
+                       break;
+                   case "f_st_color":
+                       style.color = val;
+                       break;
+                   case "f_st_backgroundImage":
+                       if (/\S/.test(val)) {
+                               style.backgroundImage = "url(" + val + ")";
+                       } else {
+                               style.backgroundImage = "none";
+                       }
+                       break;
+                   case "f_st_borderWidth":
+                       style.borderWidth = val;
+                       break;
+                   case "f_st_borderStyle":
+                       style.borderStyle = val;
+                       break;
+                   case "f_st_borderColor":
+                       style.borderColor = val;
+                       break;
+                   case "f_st_borderCollapse":
+                       style.borderCollapse = val ? "collapse" : "";
+                       break;
+                   case "f_st_width":
+                       if (/\S/.test(val)) {
+                               style.width = val + params["f_st_widthUnit"];
+                       } else {
+                               style.width = "";
+                       }
+                       break;
+                   case "f_st_height":
+                       if (/\S/.test(val)) {
+                               style.height = val + params["f_st_heightUnit"];
+                       } else {
+                               style.height = "";
+                       }
+                       break;
+                   case "f_st_textAlign":
+                       if (val == "char") {
+                               var ch = params["f_st_textAlignChar"];
+                               if (ch == '"') {
+                                       ch = '\\"';
+                               }
+                               style.textAlign = '"' + ch + '"';
+                       } else {
+                               style.textAlign = val;
+                       }
+                       break;
+                   case "f_st_verticalAlign":
+                       style.verticalAlign = val;
+                       break;
+                   case "f_st_float":
+                       style.cssFloat = val;
+                       break;
+//                 case "f_st_margin":
+//                     style.margin = val + "px";
+//                     break;
+//                 case "f_st_padding":
+//                     style.padding = val + "px";
+//                     break;
+               }
+       }
+};
+
+// Returns an HTML element for a widget that allows color selection.  That is,
+// a button that contains the given color, if any, and when pressed will popup
+// the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
+// to select some color.  If a color is selected, an input field with the name
+// "f_st_"+name will be updated with the color value in #123456 format.
+TableOperations.createColorButton = function(doc, editor, color, name) {
+       if (!color) {
+               color = "";
+       } else if (!/#/.test(color)) {
+               color = HTMLArea._colorToRgb(color);
+       }
+
+       var df = doc.createElement("span");
+       var field = doc.createElement("input");
+       field.type = "hidden";
+       df.appendChild(field);
+       field.name = "f_st_" + name;
+       field.value = color;
+       var button = doc.createElement("span");
+       button.className = "buttonColor";
+       df.appendChild(button);
+       var span = doc.createElement("span");
+       span.className = "chooser";
+       // span.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
+       span.style.backgroundColor = color;
+       button.appendChild(span);
+       button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }};
+       button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }};
+       span.onclick = function() {
+               if (this.parentNode.disabled) {
+                       return false;
+               }
+               editor._popupDialog("select_color.html", function(color) {
+                       if (color) {
+                               span.style.backgroundColor = "#" + color;
+                               field.value = "#" + color;
+                       }
+               }, color);
+       };
+       var span2 = doc.createElement("span");
+       span2.innerHTML = "&#x00d7;";
+       span2.className = "nocolor";
+       span2.title = TableOperations.I18N["Unset color"];
+       button.appendChild(span2);
+       span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }};
+       span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }};
+       span2.onclick = function() {
+               span.style.backgroundColor = "";
+               field.value = "";
+       };
+       return df;
+};
+
+TableOperations.createStyleLayoutFieldset = function(doc, editor, el) {
+       var i18n = TableOperations.I18N;
+       var fieldset = doc.createElement("fieldset");
+       var legend = doc.createElement("legend");
+       fieldset.appendChild(legend);
+       legend.innerHTML = i18n["Layout"];
+       var table = doc.createElement("table");
+       fieldset.appendChild(table);
+       table.style.width = "100%";
+       var tbody = doc.createElement("tbody");
+       table.appendChild(tbody);
+
+       var tagname = el.tagName.toLowerCase();
+       var tr, td, input, select, option, options, i;
+
+       if (tagname != "td" && tagname != "tr" && tagname != "th") {
+               tr = doc.createElement("tr");
+               tbody.appendChild(tr);
+               td = doc.createElement("td");
+               td.className = "label";
+               tr.appendChild(td);
+               td.innerHTML = i18n["Float"] + ":";
+               td = doc.createElement("td");
+               tr.appendChild(td);
+               select = doc.createElement("select");
+               td.appendChild(select);
+               select.name = "f_st_float";
+               options = ["None", "Left", "Right"];
+               for (i in options) {
+                       var Val = options[i];
+                       var val = options[i].toLowerCase();
+                       option = doc.createElement("option");
+                       option.innerHTML = i18n[Val];
+                       option.value = val;
+                       option.selected = (("" + el.style.cssFloat).toLowerCase() == val);
+                       select.appendChild(option);
+               }
+       }
+
+       tr = doc.createElement("tr");
+       tbody.appendChild(tr);
+       td = doc.createElement("td");
+       td.className = "label";
+       tr.appendChild(td);
+       td.innerHTML = i18n["Width"] + ":";
+       td = doc.createElement("td");
+       tr.appendChild(td);
+       input = doc.createElement("input");
+       input.type = "text";
+       input.value = TableOperations.getLength(el.style.width);
+       input.size = "5";
+       input.name = "f_st_width";
+       input.style.marginRight = "0.5em";
+       td.appendChild(input);
+       select = doc.createElement("select");
+       select.name = "f_st_widthUnit";
+       option = doc.createElement("option");
+       option.innerHTML = i18n["percent"];
+       option.value = "%";
+       option.selected = /%/.test(el.style.width);
+       select.appendChild(option);
+       option = doc.createElement("option");
+       option.innerHTML = i18n["pixels"];
+       option.value = "px";
+       option.selected = /px/.test(el.style.width);
+       select.appendChild(option);
+       td.appendChild(select);
+
+       select.style.marginRight = "0.5em";
+       td.appendChild(doc.createTextNode(i18n["Text align"] + ":"));
+       select = doc.createElement("select");
+       select.style.marginLeft = select.style.marginRight = "0.5em";
+       td.appendChild(select);
+       select.name = "f_st_textAlign";
+       options = ["Left", "Center", "Right", "Justify"];
+       if (tagname == "td") {
+               options.push("Char");
+       }
+       input = doc.createElement("input");
+       input.name = "f_st_textAlignChar";
+       input.size = "1";
+       input.style.fontFamily = "monospace";
+       td.appendChild(input);
+       for (i in options) {
+               var Val = options[i];
+               var val = Val.toLowerCase();
+               option = doc.createElement("option");
+               option.value = val;
+               option.innerHTML = i18n[Val];
+               option.selected = (el.style.textAlign.toLowerCase() == val);
+               select.appendChild(option);
+       }
+       function setCharVisibility(value) {
+               input.style.visibility = value ? "visible" : "hidden";
+               if (value) {
+                       input.focus();
+                       input.select();
+               }
+       };
+       select.onchange = function() { setCharVisibility(this.value == "char"); };
+       setCharVisibility(select.value == "char");
+
+       tr = doc.createElement("tr");
+       tbody.appendChild(tr);
+       td = doc.createElement("td");
+       td.className = "label";
+       tr.appendChild(td);
+       td.innerHTML = i18n["Height"] + ":";
+       td = doc.createElement("td");
+       tr.appendChild(td);
+       input = doc.createElement("input");
+       input.type = "text";
+       input.value = TableOperations.getLength(el.style.height);
+       input.size = "5";
+       input.name = "f_st_height";
+       input.style.marginRight = "0.5em";
+       td.appendChild(input);
+       select = doc.createElement("select");
+       select.name = "f_st_heightUnit";
+       option = doc.createElement("option");
+       option.innerHTML = i18n["percent"];
+       option.value = "%";
+       option.selected = /%/.test(el.style.height);
+       select.appendChild(option);
+       option = doc.createElement("option");
+       option.innerHTML = i18n["pixels"];
+       option.value = "px";
+       option.selected = /px/.test(el.style.height);
+       select.appendChild(option);
+       td.appendChild(select);
+
+       select.style.marginRight = "0.5em";
+       td.appendChild(doc.createTextNode(i18n["Vertical align"] + ":"));
+       select = doc.createElement("select");
+       select.name = "f_st_verticalAlign";
+       select.style.marginLeft = "0.5em";
+       td.appendChild(select);
+       options = ["Top", "Middle", "Bottom", "Baseline"];
+       for (i in options) {
+               var Val = options[i];
+               var val = Val.toLowerCase();
+               option = doc.createElement("option");
+               option.value = val;
+               option.innerHTML = i18n[Val];
+               option.selected = (el.style.verticalAlign.toLowerCase() == val);
+               select.appendChild(option);
+       }
+
+       return fieldset;
+};
+
+// Returns an HTML element containing the style attributes for the given
+// element.  This can be easily embedded into any dialog; the functionality is
+// also provided.
+TableOperations.createStyleFieldset = function(doc, editor, el) {
+       var i18n = TableOperations.I18N;
+       var fieldset = doc.createElement("fieldset");
+       var legend = doc.createElement("legend");
+       fieldset.appendChild(legend);
+       legend.innerHTML = i18n["CSS Style"];
+       var table = doc.createElement("table");
+       fieldset.appendChild(table);
+       table.style.width = "100%";
+       var tbody = doc.createElement("tbody");
+       table.appendChild(tbody);
+
+       var tr, td, input, select, option, options, i;
+
+       tr = doc.createElement("tr");
+       tbody.appendChild(tr);
+       td = doc.createElement("td");
+       tr.appendChild(td);
+       td.className = "label";
+       td.innerHTML = i18n["Background"] + ":";
+       td = doc.createElement("td");
+       tr.appendChild(td);
+       var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor");
+       df.firstChild.nextSibling.style.marginRight = "0.5em";
+       td.appendChild(df);
+       td.appendChild(doc.createTextNode(i18n["Image URL"] + ": "));
+       input = doc.createElement("input");
+       input.type = "text";
+       input.name = "f_st_backgroundImage";
+       if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) {
+               input.value = RegExp.$1;
+       }
+       // input.style.width = "100%";
+       td.appendChild(input);
+
+       tr = doc.createElement("tr");
+       tbody.appendChild(tr);
+       td = doc.createElement("td");
+       tr.appendChild(td);
+       td.className = "label";
+       td.innerHTML = i18n["FG Color"] + ":";
+       td = doc.createElement("td");
+       tr.appendChild(td);
+       td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color"));
+
+       // for better alignment we include an invisible field.
+       input = doc.createElement("input");
+       input.style.visibility = "hidden";
+       input.type = "text";
+       td.appendChild(input);
+
+       tr = doc.createElement("tr");
+       tbody.appendChild(tr);
+       td = doc.createElement("td");
+       tr.appendChild(td);
+       td.className = "label";
+       td.innerHTML = i18n["Border"] + ":";
+       td = doc.createElement("td");
+       tr.appendChild(td);
+
+       var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor");
+       var btn = colorButton.firstChild.nextSibling;
+       td.appendChild(colorButton);
+       // borderFields.push(btn);
+       btn.style.marginRight = "0.5em";
+
+       select = doc.createElement("select");
+       var borderFields = [];
+       td.appendChild(select);
+       select.name = "f_st_borderStyle";
+       options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"];
+       var currentBorderStyle = el.style.borderStyle;
+       // Gecko reports "solid solid solid solid" for "border-style: solid".
+       // That is, "top right bottom left" -- we only consider the first
+       // value.
+       (currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1);
+       for (i in options) {
+               var val = options[i];
+               option = doc.createElement("option");
+               option.value = val;
+               option.innerHTML = val;
+               (val == currentBorderStyle) && (option.selected = true);
+               select.appendChild(option);
+       }
+       select.style.marginRight = "0.5em";
+       function setBorderFieldsStatus(value) {
+               for (i in borderFields) {
+                       var el = borderFields[i];
+                       el.style.visibility = value ? "hidden" : "visible";
+                       if (!value && (el.tagName.toLowerCase() == "input")) {
+                               el.focus();
+                               el.select();
+                       }
+               }
+       };
+       select.onchange = function() { setBorderFieldsStatus(this.value == "none"); };
+
+       input = doc.createElement("input");
+       borderFields.push(input);
+       input.type = "text";
+       input.name = "f_st_borderWidth";
+       input.value = TableOperations.getLength(el.style.borderWidth);
+       input.size = "5";
+       td.appendChild(input);
+       input.style.marginRight = "0.5em";
+       var span = doc.createElement("span");
+       span.innerHTML = i18n["pixels"];
+       td.appendChild(span);
+       borderFields.push(span);
+
+       setBorderFieldsStatus(select.value == "none");
+
+       if (el.tagName.toLowerCase() == "table") {
+               // the border-collapse style is only for tables
+               tr = doc.createElement("tr");
+               tbody.appendChild(tr);
+               td = doc.createElement("td");
+               td.className = "label";
+               tr.appendChild(td);
+               input = doc.createElement("input");
+               input.type = "checkbox";
+               input.name = "f_st_borderCollapse";
+               input.id = "f_st_borderCollapse";
+               var val = (/collapse/i.test(el.style.borderCollapse));
+               input.checked = val ? 1 : 0;
+               td.appendChild(input);
+
+               td = doc.createElement("td");
+               tr.appendChild(td);
+               var label = doc.createElement("label");
+               label.htmlFor = "f_st_borderCollapse";
+               label.innerHTML = i18n["Collapsed borders"];
+               td.appendChild(label);
+       }
+
+//     tr = doc.createElement("tr");
+//     tbody.appendChild(tr);
+//     td = doc.createElement("td");
+//     td.className = "label";
+//     tr.appendChild(td);
+//     td.innerHTML = i18n["Margin"] + ":";
+//     td = doc.createElement("td");
+//     tr.appendChild(td);
+//     input = doc.createElement("input");
+//     input.type = "text";
+//     input.size = "5";
+//     input.name = "f_st_margin";
+//     td.appendChild(input);
+//     input.style.marginRight = "0.5em";
+//     td.appendChild(doc.createTextNode(i18n["Padding"] + ":"));
+
+//     input = doc.createElement("input");
+//     input.type = "text";
+//     input.size = "5";
+//     input.name = "f_st_padding";
+//     td.appendChild(input);
+//     input.style.marginLeft = "0.5em";
+//     input.style.marginRight = "0.5em";
+//     td.appendChild(doc.createTextNode(i18n["pixels"]));
+
+       return fieldset;
+};
+
+//// END GENERIC CODE -------------------------------------------------------