home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 April / CMCD0404.ISO / Software / Freeware / Programare / groupoffice-com-2.01 / controls / htmlarea / plugins / TableOperations / table-operations.js < prev   
Text File  |  2004-03-08  |  36KB  |  1,161 lines

  1. // Table Operations Plugin for HTMLArea-3.0
  2. // Implementation by Mihai Bazon.  Sponsored by http://www.bloki.com
  3. //
  4. // htmlArea v3.0 - Copyright (c) 2002 interactivetools.com, inc.
  5. // This notice MUST stay intact for use (see license.txt).
  6. //
  7. // A free WYSIWYG editor replacement for <textarea> fields.
  8. // For full source code and docs, visit http://www.interactivetools.com/
  9. //
  10. // Version 3.0 developed by Mihai Bazon for InteractiveTools.
  11. //   http://dynarch.com/mishoo
  12. //
  13. // $Id: table-operations.js,v 1.5 2004/03/03 11:22:41 mschering Exp $
  14.  
  15. // Object that will encapsulate all the table operations provided by
  16. // HTMLArea-3.0 (except "insert table" which is included in the main file)
  17. function TableOperations(editor) {
  18.     this.editor = editor;
  19.  
  20.     var cfg = editor.config;
  21.     var tt = TableOperations.I18N;
  22.     var bl = TableOperations.btnList;
  23.     var self = this;
  24.  
  25.     // register the toolbar buttons provided by this plugin
  26.     var toolbar = ["linebreak"];
  27.     for (var i in bl) {
  28.         var btn = bl[i];
  29.         if (!btn) {
  30.             toolbar.push("separator");
  31.         } else {
  32.             var id = "TO-" + btn[0];
  33.             cfg.registerButton(id, tt[id], editor.imgURL(btn[0] + ".gif", "TableOperations"), false,
  34.                        function(editor, id) {
  35.                            // dispatch button press event
  36.                            self.buttonPress(editor, id);
  37.                        }, btn[1]);
  38.             toolbar.push(id);
  39.         }
  40.     }
  41.  
  42.     // add a new line in the toolbar
  43.     cfg.toolbar.push(toolbar);
  44. };
  45.  
  46. TableOperations._pluginInfo = {
  47.     name          : "TableOperations",
  48.     version       : "1.0",
  49.     developer     : "Mihai Bazon",
  50.     developer_url : "http://dynarch.com/mishoo/",
  51.     c_owner       : "Mihai Bazon",
  52.     sponsor       : "Zapatec Inc.",
  53.     sponsor_url   : "http://www.bloki.com",
  54.     license       : "htmlArea"
  55. };
  56.  
  57. /************************
  58.  * UTILITIES
  59.  ************************/
  60.  
  61. // retrieves the closest element having the specified tagName in the list of
  62. // ancestors of the current selection/caret.
  63. TableOperations.prototype.getClosest = function(tagName) {
  64.     var editor = this.editor;
  65.     var ancestors = editor.getAllAncestors();
  66.     var ret = null;
  67.     tagName = ("" + tagName).toLowerCase();
  68.     for (var i in ancestors) {
  69.         var el = ancestors[i];
  70.         if (el.tagName.toLowerCase() == tagName) {
  71.             ret = el;
  72.             break;
  73.         }
  74.     }
  75.     return ret;
  76. };
  77.  
  78. // this function requires the file PopupDiv/PopupWin to be loaded from browser
  79. TableOperations.prototype.dialogTableProperties = function() {
  80.     var i18n = TableOperations.I18N;
  81.     // retrieve existing values
  82.     var table = this.getClosest("table");
  83.     // this.editor.selectNodeContents(table);
  84.     // this.editor.updateToolbar();
  85.  
  86.     var dialog = new PopupWin(this.editor, i18n["Table Properties"], function(dialog, params) {
  87.         TableOperations.processStyle(params, table);
  88.         for (var i in params) {
  89.             var val = params[i];
  90.             switch (i) {
  91.                 case "f_caption":
  92.                 if (/\S/.test(val)) {
  93.                     // contains non white-space characters
  94.                     var caption = table.getElementsByTagName("caption")[0];
  95.                     if (!caption) {
  96.                         caption = dialog.editor._doc.createElement("caption");
  97.                         table.insertBefore(caption, table.firstChild);
  98.                     }
  99.                     caption.innerHTML = val;
  100.                 } else {
  101.                     // search for caption and delete it if found
  102.                     var caption = table.getElementsByTagName("caption")[0];
  103.                     if (caption) {
  104.                         caption.parentNode.removeChild(caption);
  105.                     }
  106.                 }
  107.                 break;
  108.                 case "f_summary":
  109.                 table.summary = val;
  110.                 break;
  111.                 case "f_width":
  112.                 table.style.width = ("" + val) + params.f_unit;
  113.                 break;
  114.                 case "f_align":
  115.                 table.align = val;
  116.                 break;
  117.                 case "f_spacing":
  118.                 table.cellSpacing = val;
  119.                 break;
  120.                 case "f_padding":
  121.                 table.cellPadding = val;
  122.                 break;
  123.                 case "f_borders":
  124.                 table.border = val;
  125.                 break;
  126.                 case "f_frames":
  127.                 table.frame = val;
  128.                 break;
  129.                 case "f_rules":
  130.                 table.rules = val;
  131.                 break;
  132.             }
  133.         }
  134.         // various workarounds to refresh the table display (Gecko,
  135.         // what's going on?! do not disappoint me!)
  136.         dialog.editor.forceRedraw();
  137.         dialog.editor.focusEditor();
  138.         dialog.editor.updateToolbar();
  139.         var save_collapse = table.style.borderCollapse;
  140.         table.style.borderCollapse = "collapse";
  141.         table.style.borderCollapse = "separate";
  142.         table.style.borderCollapse = save_collapse;
  143.     },
  144.  
  145.     // this function gets called when the dialog needs to be initialized
  146.     function (dialog) {
  147.  
  148.         var f_caption = "";
  149.         var capel = table.getElementsByTagName("caption")[0];
  150.         if (capel) {
  151.             f_caption = capel.innerHTML;
  152.         }
  153.         var f_summary = table.summary;
  154.         var f_width = parseInt(table.style.width);
  155.         isNaN(f_width) && (f_width = "");
  156.         var f_unit = /%/.test(table.style.width) ? 'percent' : 'pixels';
  157.         var f_align = table.align;
  158.         var f_spacing = table.cellSpacing;
  159.         var f_padding = table.cellPadding;
  160.         var f_borders = table.border;
  161.         var f_frames = table.frame;
  162.         var f_rules = table.rules;
  163.  
  164.         function selected(val) {
  165.             return val ? " selected" : "";
  166.         };
  167.  
  168.         // dialog contents
  169.         dialog.content.style.width = "400px";
  170.         dialog.content.innerHTML = " \
  171. <div class='title'\
  172.  style='background: url(" + dialog.baseURL + dialog.editor.imgURL("table-prop.gif", "TableOperations") + ") #fff 98% 50% no-repeat'>" + i18n["Table Properties"] + "\
  173. </div> \
  174. <table style='width:100%'> \
  175.   <tr> \
  176.     <td> \
  177.       <fieldset><legend>" + i18n["Description"] + "</legend> \
  178.        <table style='width:100%'> \
  179.         <tr> \
  180.           <td class='label'>" + i18n["Caption"] + ":</td> \
  181.           <td class='value'><input type='text' name='f_caption' value='" + f_caption + "'/></td> \
  182.         </tr><tr> \
  183.           <td class='label'>" + i18n["Summary"] + ":</td> \
  184.           <td class='value'><input type='text' name='f_summary' value='" + f_summary + "'/></td> \
  185.         </tr> \
  186.        </table> \
  187.       </fieldset> \
  188.     </td> \
  189.   </tr> \
  190.   <tr><td id='--HA-layout'></td></tr> \
  191.   <tr> \
  192.     <td> \
  193.       <fieldset><legend>" + i18n["Spacing and padding"] + "</legend> \
  194.        <table style='width:100%'> \
  195. "+//        <tr> \
  196. //           <td class='label'>" + i18n["Width"] + ":</td> \
  197. //           <td><input type='text' name='f_width' value='" + f_width + "' size='5' /> \
  198. //             <select name='f_unit'> \
  199. //               <option value='%'" + selected(f_unit == "percent") + ">" + i18n["percent"] + "</option> \
  200. //               <option value='px'" + selected(f_unit == "pixels") + ">" + i18n["pixels"] + "</option> \
  201. //             </select>   " + i18n["Align"] + ": \
  202. //             <select name='f_align'> \
  203. //               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
  204. //               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
  205. //               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
  206. //             </select> \
  207. //           </td> \
  208. //         </tr> \
  209. "        <tr> \
  210.           <td class='label'>" + i18n["Spacing"] + ":</td> \
  211.           <td><input type='text' name='f_spacing' size='5' value='" + f_spacing + "' />  " + i18n["Padding"] + ":\
  212.             <input type='text' name='f_padding' size='5' value='" + f_padding + "' />   " + i18n["pixels"] + "\
  213.           </td> \
  214.         </tr> \
  215.        </table> \
  216.       </fieldset> \
  217.     </td> \
  218.   </tr> \
  219.   <tr> \
  220.     <td> \
  221.       <fieldset><legend>Frame and borders</legend> \
  222.         <table width='100%'> \
  223.           <tr> \
  224.             <td class='label'>" + i18n["Borders"] + ":</td> \
  225.             <td><input name='f_borders' type='text' size='5' value='" + f_borders + "' />   " + i18n["pixels"] + "</td> \
  226.           </tr> \
  227.           <tr> \
  228.             <td class='label'>" + i18n["Frames"] + ":</td> \
  229.             <td> \
  230.               <select name='f_frames'> \
  231.                 <option value='void'" + selected(f_frames == "void") + ">" + i18n["No sides"] + "</option> \
  232.                 <option value='above'" + selected(f_frames == "above") + ">" + i18n["The top side only"] + "</option> \
  233.                 <option value='below'" + selected(f_frames == "below") + ">" + i18n["The bottom side only"] + "</option> \
  234.                 <option value='hsides'" + selected(f_frames == "hsides") + ">" + i18n["The top and bottom sides only"] + "</option> \
  235.                 <option value='vsides'" + selected(f_frames == "vsides") + ">" + i18n["The right and left sides only"] + "</option> \
  236.                 <option value='lhs'" + selected(f_frames == "lhs") + ">" + i18n["The left-hand side only"] + "</option> \
  237.                 <option value='rhs'" + selected(f_frames == "rhs") + ">" + i18n["The right-hand side only"] + "</option> \
  238.                 <option value='box'" + selected(f_frames == "box") + ">" + i18n["All four sides"] + "</option> \
  239.               </select> \
  240.             </td> \
  241.           </tr> \
  242.           <tr> \
  243.             <td class='label'>" + i18n["Rules"] + ":</td> \
  244.             <td> \
  245.               <select name='f_rules'> \
  246.                 <option value='none'" + selected(f_rules == "none") + ">" + i18n["No rules"] + "</option> \
  247.                 <option value='rows'" + selected(f_rules == "rows") + ">" + i18n["Rules will appear between rows only"] + "</option> \
  248.                 <option value='cols'" + selected(f_rules == "cols") + ">" + i18n["Rules will appear between columns only"] + "</option> \
  249.                 <option value='all'" + selected(f_rules == "all") + ">" + i18n["Rules will appear between all rows and columns"] + "</option> \
  250.               </select> \
  251.             </td> \
  252.           </tr> \
  253.         </table> \
  254.       </fieldset> \
  255.     </td> \
  256.   </tr> \
  257.   <tr> \
  258.     <td id='--HA-style'></td> \
  259.   </tr> \
  260. </table> \
  261. ";
  262.         var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, table);
  263.         var p = dialog.doc.getElementById("--HA-style");
  264.         p.appendChild(st_prop);
  265.         var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, table);
  266.         p = dialog.doc.getElementById("--HA-layout");
  267.         p.appendChild(st_layout);
  268.         dialog.modal = true;
  269.         dialog.addButtons("ok", "cancel");
  270.         dialog.showAtElement(dialog.editor._iframe, "c");
  271.     });
  272. };
  273.  
  274. // this function requires the file PopupDiv/PopupWin to be loaded from browser
  275. TableOperations.prototype.dialogRowCellProperties = function(cell) {
  276.     var i18n = TableOperations.I18N;
  277.     // retrieve existing values
  278.     var element = this.getClosest(cell ? "td" : "tr");
  279.     var table = this.getClosest("table");
  280.     // this.editor.selectNodeContents(element);
  281.     // this.editor.updateToolbar();
  282.  
  283.     var dialog = new PopupWin(this.editor, i18n[cell ? "Cell Properties" : "Row Properties"], function(dialog, params) {
  284.         TableOperations.processStyle(params, element);
  285.         for (var i in params) {
  286.             var val = params[i];
  287.             switch (i) {
  288.                 case "f_align":
  289.                 element.align = val;
  290.                 break;
  291.                 case "f_char":
  292.                 element.ch = val;
  293.                 break;
  294.                 case "f_valign":
  295.                 element.vAlign = val;
  296.                 break;
  297.             }
  298.         }
  299.         // various workarounds to refresh the table display (Gecko,
  300.         // what's going on?! do not disappoint me!)
  301.         dialog.editor.forceRedraw();
  302.         dialog.editor.focusEditor();
  303.         dialog.editor.updateToolbar();
  304.         var save_collapse = table.style.borderCollapse;
  305.         table.style.borderCollapse = "collapse";
  306.         table.style.borderCollapse = "separate";
  307.         table.style.borderCollapse = save_collapse;
  308.     },
  309.  
  310.     // this function gets called when the dialog needs to be initialized
  311.     function (dialog) {
  312.  
  313.         var f_align = element.align;
  314.         var f_valign = element.vAlign;
  315.         var f_char = element.ch;
  316.  
  317.         function selected(val) {
  318.             return val ? " selected" : "";
  319.         };
  320.  
  321.         // dialog contents
  322.         dialog.content.style.width = "400px";
  323.         dialog.content.innerHTML = " \
  324. <div class='title'\
  325.  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> \
  326. <table style='width:100%'> \
  327.   <tr> \
  328.     <td id='--HA-layout'> \
  329. "+//      <fieldset><legend>" + i18n["Layout"] + "</legend> \
  330. //        <table style='width:100%'> \
  331. //         <tr> \
  332. //           <td class='label'>" + i18n["Align"] + ":</td> \
  333. //           <td> \
  334. //             <select name='f_align'> \
  335. //               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
  336. //               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
  337. //               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
  338. //               <option value='char'" + selected(f_align == "char") + ">" + i18n["Char"] + "</option> \
  339. //             </select> \
  340. //               " + i18n["Char"] + ": \
  341. //             <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \
  342. //           </td> \
  343. //         </tr><tr> \
  344. //           <td class='label'>" + i18n["Vertical align"] + ":</td> \
  345. //           <td> \
  346. //             <select name='f_valign'> \
  347. //               <option value='top'" + selected(f_valign == "top") + ">" + i18n["Top"] + "</option> \
  348. //               <option value='middle'" + selected(f_valign == "middle") + ">" + i18n["Middle"] + "</option> \
  349. //               <option value='bottom'" + selected(f_valign == "bottom") + ">" + i18n["Bottom"] + "</option> \
  350. //               <option value='baseline'" + selected(f_valign == "baseline") + ">" + i18n["Baseline"] + "</option> \
  351. //             </select> \
  352. //           </td> \
  353. //         </tr> \
  354. //        </table> \
  355. //       </fieldset> \
  356. "    </td> \
  357.   </tr> \
  358.   <tr> \
  359.     <td id='--HA-style'></td> \
  360.   </tr> \
  361. </table> \
  362. ";
  363.         var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element);
  364.         var p = dialog.doc.getElementById("--HA-style");
  365.         p.appendChild(st_prop);
  366.         var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element);
  367.         p = dialog.doc.getElementById("--HA-layout");
  368.         p.appendChild(st_layout);
  369.         dialog.modal = true;
  370.         dialog.addButtons("ok", "cancel");
  371.         dialog.showAtElement(dialog.editor._iframe, "c");
  372.     });
  373. };
  374.  
  375. // this function gets called when some button from the TableOperations toolbar
  376. // was pressed.
  377. TableOperations.prototype.buttonPress = function(editor, button_id) {
  378.     this.editor = editor;
  379.     var mozbr = HTMLArea.is_gecko ? "<br />" : "";
  380.     var i18n = TableOperations.I18N;
  381.  
  382.     // helper function that clears the content in a table row
  383.     function clearRow(tr) {
  384.         var tds = tr.getElementsByTagName("td");
  385.         for (var i = tds.length; --i >= 0;) {
  386.             var td = tds[i];
  387.             td.rowSpan = 1;
  388.             td.innerHTML = mozbr;
  389.         }
  390.     };
  391.  
  392.     function splitRow(td) {
  393.         var n = parseInt("" + td.rowSpan);
  394.         var nc = parseInt("" + td.colSpan);
  395.         td.rowSpan = 1;
  396.         tr = td.parentNode;
  397.         var itr = tr.rowIndex;
  398.         var trs = tr.parentNode.rows;
  399.         var index = td.cellIndex;
  400.         while (--n > 0) {
  401.             tr = trs[++itr];
  402.             var otd = editor._doc.createElement("td");
  403.             otd.colSpan = td.colSpan;
  404.             otd.innerHTML = mozbr;
  405.             tr.insertBefore(otd, tr.cells[index]);
  406.         }
  407.         editor.forceRedraw();
  408.         editor.updateToolbar();
  409.     };
  410.  
  411.     function splitCol(td) {
  412.         var nc = parseInt("" + td.colSpan);
  413.         td.colSpan = 1;
  414.         tr = td.parentNode;
  415.         var ref = td.nextSibling;
  416.         while (--nc > 0) {
  417.             var otd = editor._doc.createElement("td");
  418.             otd.rowSpan = td.rowSpan;
  419.             otd.innerHTML = mozbr;
  420.             tr.insertBefore(otd, ref);
  421.         }
  422.         editor.forceRedraw();
  423.         editor.updateToolbar();
  424.     };
  425.  
  426.     function splitCell(td) {
  427.         var nc = parseInt("" + td.colSpan);
  428.         splitCol(td);
  429.         var items = td.parentNode.cells;
  430.         var index = td.cellIndex;
  431.         while (nc-- > 0) {
  432.             splitRow(items[index++]);
  433.         }
  434.     };
  435.  
  436.     function selectNextNode(el) {
  437.         var node = el.nextSibling;
  438.         while (node && node.nodeType != 1) {
  439.             node = node.nextSibling;
  440.         }
  441.         if (!node) {
  442.             node = el.previousSibling;
  443.             while (node && node.nodeType != 1) {
  444.                 node = node.previousSibling;
  445.             }
  446.         }
  447.         if (!node) {
  448.             node = el.parentNode;
  449.         }
  450.         editor.selectNodeContents(node);
  451.     };
  452.  
  453.     switch (button_id) {
  454.         // ROWS
  455.  
  456.         case "TO-row-insert-above":
  457.         case "TO-row-insert-under":
  458.         var tr = this.getClosest("tr");
  459.         if (!tr) {
  460.             break;
  461.         }
  462.         var otr = tr.cloneNode(true);
  463.         clearRow(otr);
  464.         tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr);
  465.         editor.forceRedraw();
  466.         editor.focusEditor();
  467.         break;
  468.         case "TO-row-delete":
  469.         var tr = this.getClosest("tr");
  470.         if (!tr) {
  471.             break;
  472.         }
  473.         var par = tr.parentNode;
  474.         if (par.rows.length == 1) {
  475.             alert(i18n["not-del-last-row"]);
  476.             break;
  477.         }
  478.         // set the caret first to a position that doesn't
  479.         // disappear.
  480.         selectNextNode(tr);
  481.         par.removeChild(tr);
  482.         editor.forceRedraw();
  483.         editor.focusEditor();
  484.         editor.updateToolbar();
  485.         break;
  486.         case "TO-row-split":
  487.         var td = this.getClosest("td");
  488.         if (!td) {
  489.             break;
  490.         }
  491.         splitRow(td);
  492.         break;
  493.  
  494.         // COLUMNS
  495.  
  496.         case "TO-col-insert-before":
  497.         case "TO-col-insert-after":
  498.         var td = this.getClosest("td");
  499.         if (!td) {
  500.             break;
  501.         }
  502.         var rows = td.parentNode.parentNode.rows;
  503.         var index = td.cellIndex;
  504.         for (var i = rows.length; --i >= 0;) {
  505.             var tr = rows[i];
  506.             var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)];
  507.             var otd = editor._doc.createElement("td");
  508.             otd.innerHTML = mozbr;
  509.             tr.insertBefore(otd, ref);
  510.         }
  511.         editor.focusEditor();
  512.         break;
  513.         case "TO-col-split":
  514.         var td = this.getClosest("td");
  515.         if (!td) {
  516.             break;
  517.         }
  518.         splitCol(td);
  519.         break;
  520.         case "TO-col-delete":
  521.         var td = this.getClosest("td");
  522.         if (!td) {
  523.             break;
  524.         }
  525.         var index = td.cellIndex;
  526.         if (td.parentNode.cells.length == 1) {
  527.             alert(i18n["not-del-last-col"]);
  528.             break;
  529.         }
  530.         // set the caret first to a position that doesn't disappear
  531.         selectNextNode(td);
  532.         var rows = td.parentNode.parentNode.rows;
  533.         for (var i = rows.length; --i >= 0;) {
  534.             var tr = rows[i];
  535.             tr.removeChild(tr.cells[index]);
  536.         }
  537.         editor.forceRedraw();
  538.         editor.focusEditor();
  539.         editor.updateToolbar();
  540.         break;
  541.  
  542.         // CELLS
  543.  
  544.         case "TO-cell-split":
  545.         var td = this.getClosest("td");
  546.         if (!td) {
  547.             break;
  548.         }
  549.         splitCell(td);
  550.         break;
  551.         case "TO-cell-insert-before":
  552.         case "TO-cell-insert-after":
  553.         var td = this.getClosest("td");
  554.         if (!td) {
  555.             break;
  556.         }
  557.         var tr = td.parentNode;
  558.         var otd = editor._doc.createElement("td");
  559.         otd.innerHTML = mozbr;
  560.         tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td);
  561.         editor.forceRedraw();
  562.         editor.focusEditor();
  563.         break;
  564.         case "TO-cell-delete":
  565.         var td = this.getClosest("td");
  566.         if (!td) {
  567.             break;
  568.         }
  569.         if (td.parentNode.cells.length == 1) {
  570.             alert(i18n["not-del-last-cell"]);
  571.             break;
  572.         }
  573.         // set the caret first to a position that doesn't disappear
  574.         selectNextNode(td);
  575.         td.parentNode.removeChild(td);
  576.         editor.forceRedraw();
  577.         editor.updateToolbar();
  578.         break;
  579.         case "TO-cell-merge":
  580.         // !! FIXME: Mozilla specific !!
  581.         var sel = editor._getSelection();
  582.         var range, i = 0;
  583.         var rows = [];
  584.         var row = null;
  585.         var cells = null;
  586.         if (!HTMLArea.is_ie) {
  587.             try {
  588.                 while (range = sel.getRangeAt(i++)) {
  589.                     var td = range.startContainer.childNodes[range.startOffset];
  590.                     if (td.parentNode != row) {
  591.                         row = td.parentNode;
  592.                         (cells) && rows.push(cells);
  593.                         cells = [];
  594.                     }
  595.                     cells.push(td);
  596.                 }
  597.             } catch(e) {/* finished walking through selection */}
  598.             rows.push(cells);
  599.         } else {
  600.             // Internet Explorer "browser"
  601.             var td = this.getClosest("td");
  602.             if (!td) {
  603.                 alert(i18n["Please click into some cell"]);
  604.                 break;
  605.             }
  606.             var tr = td.parentElement;
  607.             var no_cols = prompt(i18n["How many columns would you like to merge?"], 2);
  608.             if (!no_cols) {
  609.                 // cancelled
  610.                 break;
  611.             }
  612.             var no_rows = prompt(i18n["How many rows would you like to merge?"], 2);
  613.             if (!no_rows) {
  614.                 // cancelled
  615.                 break;
  616.             }
  617.             var cell_index = td.cellIndex;
  618.             while (no_rows-- > 0) {
  619.                 td = tr.cells[cell_index];
  620.                 cells = [td];
  621.                 for (var i = 1; i < no_cols; ++i) {
  622.                     td = td.nextSibling;
  623.                     if (!td) {
  624.                         break;
  625.                     }
  626.                     cells.push(td);
  627.                 }
  628.                 rows.push(cells);
  629.                 tr = tr.nextSibling;
  630.                 if (!tr) {
  631.                     break;
  632.                 }
  633.             }
  634.         }
  635.         var HTML = "";
  636.         for (i = 0; i < rows.length; ++i) {
  637.             // i && (HTML += "<br />");
  638.             var cells = rows[i];
  639.             for (var j = 0; j < cells.length; ++j) {
  640.                 // j && (HTML += " ");
  641.                 var cell = cells[j];
  642.                 HTML += cell.innerHTML;
  643.                 (i || j) && (cell.parentNode.removeChild(cell));
  644.             }
  645.         }
  646.         var td = rows[0][0];
  647.         td.innerHTML = HTML;
  648.         td.rowSpan = rows.length;
  649.         td.colSpan = rows[0].length;
  650.         editor.selectNodeContents(td);
  651.         editor.forceRedraw();
  652.         editor.focusEditor();
  653.         break;
  654.  
  655.         // PROPERTIES
  656.  
  657.         case "TO-table-prop":
  658.         this.dialogTableProperties();
  659.         break;
  660.  
  661.         case "TO-row-prop":
  662.         this.dialogRowCellProperties(false);
  663.         break;
  664.  
  665.         case "TO-cell-prop":
  666.         this.dialogRowCellProperties(true);
  667.         break;
  668.  
  669.         default:
  670.         alert("Button [" + button_id + "] not yet implemented");
  671.     }
  672. };
  673.  
  674. // the list of buttons added by this plugin
  675. TableOperations.btnList = [
  676.     // table properties button
  677.     ["table-prop",       "table"],
  678.     null,            // separator
  679.  
  680.     // ROWS
  681.     ["row-prop",         "tr"],
  682.     ["row-insert-above", "tr"],
  683.     ["row-insert-under", "tr"],
  684.     ["row-delete",       "tr"],
  685.     ["row-split",        "td[rowSpan!=1]"],
  686.     null,
  687.  
  688.     // COLS
  689.     ["col-insert-before", "td"],
  690.     ["col-insert-after",  "td"],
  691.     ["col-delete",        "td"],
  692.     ["col-split",         "td[colSpan!=1]"],
  693.     null,
  694.  
  695.     // CELLS
  696.     ["cell-prop",          "td"],
  697.     ["cell-insert-before", "td"],
  698.     ["cell-insert-after",  "td"],
  699.     ["cell-delete",        "td"],
  700.     ["cell-merge",         "tr"],
  701.     ["cell-split",         "td[colSpan!=1,rowSpan!=1]"]
  702.     ];
  703.  
  704.  
  705.  
  706. //// GENERIC CODE [style of any element; this should be moved into a separate
  707. //// file as it'll be very useful]
  708. //// BEGIN GENERIC CODE -----------------------------------------------------
  709.  
  710. TableOperations.getLength = function(value) {
  711.     var len = parseInt(value);
  712.     if (isNaN(len)) {
  713.         len = "";
  714.     }
  715.     return len;
  716. };
  717.  
  718. // Applies the style found in "params" to the given element.
  719. TableOperations.processStyle = function(params, element) {
  720.     var style = element.style;
  721.     for (var i in params) {
  722.         var val = params[i];
  723.         switch (i) {
  724.             case "f_st_backgroundColor":
  725.             style.backgroundColor = val;
  726.             break;
  727.             case "f_st_color":
  728.             style.color = val;
  729.             break;
  730.             case "f_st_backgroundImage":
  731.             if (/\S/.test(val)) {
  732.                 style.backgroundImage = "url(" + val + ")";
  733.             } else {
  734.                 style.backgroundImage = "none";
  735.             }
  736.             break;
  737.             case "f_st_borderWidth":
  738.             style.borderWidth = val;
  739.             break;
  740.             case "f_st_borderStyle":
  741.             style.borderStyle = val;
  742.             break;
  743.             case "f_st_borderColor":
  744.             style.borderColor = val;
  745.             break;
  746.             case "f_st_borderCollapse":
  747.             style.borderCollapse = val ? "collapse" : "";
  748.             break;
  749.             case "f_st_width":
  750.             if (/\S/.test(val)) {
  751.                 style.width = val + params["f_st_widthUnit"];
  752.             } else {
  753.                 style.width = "";
  754.             }
  755.             break;
  756.             case "f_st_height":
  757.             if (/\S/.test(val)) {
  758.                 style.height = val + params["f_st_heightUnit"];
  759.             } else {
  760.                 style.height = "";
  761.             }
  762.             break;
  763.             case "f_st_textAlign":
  764.             if (val == "char") {
  765.                 var ch = params["f_st_textAlignChar"];
  766.                 if (ch == '"') {
  767.                     ch = '\\"';
  768.                 }
  769.                 style.textAlign = '"' + ch + '"';
  770.             } else {
  771.                 style.textAlign = val;
  772.             }
  773.             break;
  774.             case "f_st_verticalAlign":
  775.             style.verticalAlign = val;
  776.             break;
  777.             case "f_st_float":
  778.             style.cssFloat = val;
  779.             break;
  780. //             case "f_st_margin":
  781. //             style.margin = val + "px";
  782. //             break;
  783. //             case "f_st_padding":
  784. //             style.padding = val + "px";
  785. //             break;
  786.         }
  787.     }
  788. };
  789.  
  790. // Returns an HTML element for a widget that allows color selection.  That is,
  791. // a button that contains the given color, if any, and when pressed will popup
  792. // the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
  793. // to select some color.  If a color is selected, an input field with the name
  794. // "f_st_"+name will be updated with the color value in #123456 format.
  795. TableOperations.createColorButton = function(doc, editor, color, name) {
  796.     if (!color) {
  797.         color = "";
  798.     } else if (!/#/.test(color)) {
  799.         color = HTMLArea._colorToRgb(color);
  800.     }
  801.  
  802.     var df = doc.createElement("span");
  803.      var field = doc.createElement("input");
  804.     field.type = "hidden";
  805.     df.appendChild(field);
  806.      field.name = "f_st_" + name;
  807.     field.value = color;
  808.     var button = doc.createElement("span");
  809.     button.className = "buttonColor";
  810.     df.appendChild(button);
  811.     var span = doc.createElement("span");
  812.     span.className = "chooser";
  813.     // span.innerHTML = "       ";
  814.     span.style.backgroundColor = color;
  815.     button.appendChild(span);
  816.     button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }};
  817.     button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }};
  818.     span.onclick = function() {
  819.         if (this.parentNode.disabled) {
  820.             return false;
  821.         }
  822.         editor._popupDialog("select_color.html", function(color) {
  823.             if (color) {
  824.                 span.style.backgroundColor = "#" + color;
  825.                 field.value = "#" + color;
  826.             }
  827.         }, color);
  828.     };
  829.     var span2 = doc.createElement("span");
  830.     span2.innerHTML = "×";
  831.     span2.className = "nocolor";
  832.     span2.title = TableOperations.I18N["Unset color"];
  833.     button.appendChild(span2);
  834.     span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }};
  835.     span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }};
  836.     span2.onclick = function() {
  837.         span.style.backgroundColor = "";
  838.         field.value = "";
  839.     };
  840.     return df;
  841. };
  842.  
  843. TableOperations.createStyleLayoutFieldset = function(doc, editor, el) {
  844.     var i18n = TableOperations.I18N;
  845.     var fieldset = doc.createElement("fieldset");
  846.     var legend = doc.createElement("legend");
  847.     fieldset.appendChild(legend);
  848.     legend.innerHTML = i18n["Layout"];
  849.     var table = doc.createElement("table");
  850.     fieldset.appendChild(table);
  851.     table.style.width = "100%";
  852.     var tbody = doc.createElement("tbody");
  853.     table.appendChild(tbody);
  854.  
  855.     var tagname = el.tagName.toLowerCase();
  856.     var tr, td, input, select, option, options, i;
  857.  
  858.     if (tagname != "td" && tagname != "tr" && tagname != "th") {
  859.         tr = doc.createElement("tr");
  860.         tbody.appendChild(tr);
  861.         td = doc.createElement("td");
  862.         td.className = "label";
  863.         tr.appendChild(td);
  864.         td.innerHTML = i18n["Float"] + ":";
  865.         td = doc.createElement("td");
  866.         tr.appendChild(td);
  867.         select = doc.createElement("select");
  868.         td.appendChild(select);
  869.         select.name = "f_st_float";
  870.         options = ["None", "Left", "Right"];
  871.         for (i in options) {
  872.             var Val = options[i];
  873.             var val = options[i].toLowerCase();
  874.             option = doc.createElement("option");
  875.             option.innerHTML = i18n[Val];
  876.             option.value = val;
  877.             option.selected = (("" + el.style.cssFloat).toLowerCase() == val);
  878.             select.appendChild(option);
  879.         }
  880.     }
  881.  
  882.     tr = doc.createElement("tr");
  883.     tbody.appendChild(tr);
  884.     td = doc.createElement("td");
  885.     td.className = "label";
  886.     tr.appendChild(td);
  887.     td.innerHTML = i18n["Width"] + ":";
  888.     td = doc.createElement("td");
  889.     tr.appendChild(td);
  890.     input = doc.createElement("input");
  891.     input.type = "text";
  892.     input.value = TableOperations.getLength(el.style.width);
  893.     input.size = "5";
  894.     input.name = "f_st_width";
  895.     input.style.marginRight = "0.5em";
  896.     td.appendChild(input);
  897.     select = doc.createElement("select");
  898.     select.name = "f_st_widthUnit";
  899.     option = doc.createElement("option");
  900.     option.innerHTML = i18n["percent"];
  901.     option.value = "%";
  902.     option.selected = /%/.test(el.style.width);
  903.     select.appendChild(option);
  904.     option = doc.createElement("option");
  905.     option.innerHTML = i18n["pixels"];
  906.     option.value = "px";
  907.     option.selected = /px/.test(el.style.width);
  908.     select.appendChild(option);
  909.     td.appendChild(select);
  910.  
  911.     select.style.marginRight = "0.5em";
  912.     td.appendChild(doc.createTextNode(i18n["Text align"] + ":"));
  913.     select = doc.createElement("select");
  914.     select.style.marginLeft = select.style.marginRight = "0.5em";
  915.     td.appendChild(select);
  916.     select.name = "f_st_textAlign";
  917.     options = ["Left", "Center", "Right", "Justify"];
  918.     if (tagname == "td") {
  919.         options.push("Char");
  920.     }
  921.     input = doc.createElement("input");
  922.     input.name = "f_st_textAlignChar";
  923.     input.size = "1";
  924.     input.style.fontFamily = "monospace";
  925.     td.appendChild(input);
  926.     for (i in options) {
  927.         var Val = options[i];
  928.         var val = Val.toLowerCase();
  929.         option = doc.createElement("option");
  930.         option.value = val;
  931.         option.innerHTML = i18n[Val];
  932.         option.selected = (el.style.textAlign.toLowerCase() == val);
  933.         select.appendChild(option);
  934.     }
  935.     function setCharVisibility(value) {
  936.         input.style.visibility = value ? "visible" : "hidden";
  937.         if (value) {
  938.             input.focus();
  939.             input.select();
  940.         }
  941.     };
  942.     select.onchange = function() { setCharVisibility(this.value == "char"); };
  943.     setCharVisibility(select.value == "char");
  944.  
  945.     tr = doc.createElement("tr");
  946.     tbody.appendChild(tr);
  947.     td = doc.createElement("td");
  948.     td.className = "label";
  949.     tr.appendChild(td);
  950.     td.innerHTML = i18n["Height"] + ":";
  951.     td = doc.createElement("td");
  952.     tr.appendChild(td);
  953.     input = doc.createElement("input");
  954.     input.type = "text";
  955.     input.value = TableOperations.getLength(el.style.height);
  956.     input.size = "5";
  957.     input.name = "f_st_height";
  958.     input.style.marginRight = "0.5em";
  959.     td.appendChild(input);
  960.     select = doc.createElement("select");
  961.     select.name = "f_st_heightUnit";
  962.     option = doc.createElement("option");
  963.     option.innerHTML = i18n["percent"];
  964.     option.value = "%";
  965.     option.selected = /%/.test(el.style.height);
  966.     select.appendChild(option);
  967.     option = doc.createElement("option");
  968.     option.innerHTML = i18n["pixels"];
  969.     option.value = "px";
  970.     option.selected = /px/.test(el.style.height);
  971.     select.appendChild(option);
  972.     td.appendChild(select);
  973.  
  974.     select.style.marginRight = "0.5em";
  975.     td.appendChild(doc.createTextNode(i18n["Vertical align"] + ":"));
  976.     select = doc.createElement("select");
  977.     select.name = "f_st_verticalAlign";
  978.     select.style.marginLeft = "0.5em";
  979.     td.appendChild(select);
  980.     options = ["Top", "Middle", "Bottom", "Baseline"];
  981.     for (i in options) {
  982.         var Val = options[i];
  983.         var val = Val.toLowerCase();
  984.         option = doc.createElement("option");
  985.         option.value = val;
  986.         option.innerHTML = i18n[Val];
  987.         option.selected = (el.style.verticalAlign.toLowerCase() == val);
  988.         select.appendChild(option);
  989.     }
  990.  
  991.     return fieldset;
  992. };
  993.  
  994. // Returns an HTML element containing the style attributes for the given
  995. // element.  This can be easily embedded into any dialog; the functionality is
  996. // also provided.
  997. TableOperations.createStyleFieldset = function(doc, editor, el) {
  998.     var i18n = TableOperations.I18N;
  999.     var fieldset = doc.createElement("fieldset");
  1000.     var legend = doc.createElement("legend");
  1001.     fieldset.appendChild(legend);
  1002.     legend.innerHTML = i18n["CSS Style"];
  1003.     var table = doc.createElement("table");
  1004.     fieldset.appendChild(table);
  1005.     table.style.width = "100%";
  1006.     var tbody = doc.createElement("tbody");
  1007.     table.appendChild(tbody);
  1008.  
  1009.     var tr, td, input, select, option, options, i;
  1010.  
  1011.     tr = doc.createElement("tr");
  1012.     tbody.appendChild(tr);
  1013.     td = doc.createElement("td");
  1014.     tr.appendChild(td);
  1015.     td.className = "label";
  1016.     td.innerHTML = i18n["Background"] + ":";
  1017.     td = doc.createElement("td");
  1018.     tr.appendChild(td);
  1019.     var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor");
  1020.     df.firstChild.nextSibling.style.marginRight = "0.5em";
  1021.     td.appendChild(df);
  1022.     td.appendChild(doc.createTextNode(i18n["Image URL"] + ": "));
  1023.     input = doc.createElement("input");
  1024.     input.type = "text";
  1025.     input.name = "f_st_backgroundImage";
  1026.     if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) {
  1027.         input.value = RegExp.$1;
  1028.     }
  1029.     // input.style.width = "100%";
  1030.     td.appendChild(input);
  1031.  
  1032.     tr = doc.createElement("tr");
  1033.     tbody.appendChild(tr);
  1034.     td = doc.createElement("td");
  1035.     tr.appendChild(td);
  1036.     td.className = "label";
  1037.     td.innerHTML = i18n["FG Color"] + ":";
  1038.     td = doc.createElement("td");
  1039.     tr.appendChild(td);
  1040.     td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color"));
  1041.  
  1042.     // for better alignment we include an invisible field.
  1043.     input = doc.createElement("input");
  1044.     input.style.visibility = "hidden";
  1045.     input.type = "text";
  1046.     td.appendChild(input);
  1047.  
  1048.     tr = doc.createElement("tr");
  1049.     tbody.appendChild(tr);
  1050.     td = doc.createElement("td");
  1051.     tr.appendChild(td);
  1052.     td.className = "label";
  1053.     td.innerHTML = i18n["Border"] + ":";
  1054.     td = doc.createElement("td");
  1055.     tr.appendChild(td);
  1056.  
  1057.     var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor");
  1058.     var btn = colorButton.firstChild.nextSibling;
  1059.     td.appendChild(colorButton);
  1060.     // borderFields.push(btn);
  1061.     btn.style.marginRight = "0.5em";
  1062.  
  1063.     select = doc.createElement("select");
  1064.     var borderFields = [];
  1065.     td.appendChild(select);
  1066.     select.name = "f_st_borderStyle";
  1067.     options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"];
  1068.     var currentBorderStyle = el.style.borderStyle;
  1069.     // Gecko reports "solid solid solid solid" for "border-style: solid".
  1070.     // That is, "top right bottom left" -- we only consider the first
  1071.     // value.
  1072.     (currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1);
  1073.     for (i in options) {
  1074.         var val = options[i];
  1075.         option = doc.createElement("option");
  1076.         option.value = val;
  1077.         option.innerHTML = val;
  1078.         (val == currentBorderStyle) && (option.selected = true);
  1079.         select.appendChild(option);
  1080.     }
  1081.     select.style.marginRight = "0.5em";
  1082.     function setBorderFieldsStatus(value) {
  1083.         for (i in borderFields) {
  1084.             var el = borderFields[i];
  1085.             el.style.visibility = value ? "hidden" : "visible";
  1086.             if (!value && (el.tagName.toLowerCase() == "input")) {
  1087.                 el.focus();
  1088.                 el.select();
  1089.             }
  1090.         }
  1091.     };
  1092.     select.onchange = function() { setBorderFieldsStatus(this.value == "none"); };
  1093.  
  1094.     input = doc.createElement("input");
  1095.     borderFields.push(input);
  1096.     input.type = "text";
  1097.     input.name = "f_st_borderWidth";
  1098.     input.value = TableOperations.getLength(el.style.borderWidth);
  1099.     input.size = "5";
  1100.     td.appendChild(input);
  1101.     input.style.marginRight = "0.5em";
  1102.     var span = doc.createElement("span");
  1103.     span.innerHTML = i18n["pixels"];
  1104.     td.appendChild(span);
  1105.     borderFields.push(span);
  1106.  
  1107.     setBorderFieldsStatus(select.value == "none");
  1108.  
  1109.     if (el.tagName.toLowerCase() == "table") {
  1110.         // the border-collapse style is only for tables
  1111.         tr = doc.createElement("tr");
  1112.         tbody.appendChild(tr);
  1113.         td = doc.createElement("td");
  1114.         td.className = "label";
  1115.         tr.appendChild(td);
  1116.         input = doc.createElement("input");
  1117.         input.type = "checkbox";
  1118.         input.name = "f_st_borderCollapse";
  1119.         input.id = "f_st_borderCollapse";
  1120.         var val = (/collapse/i.test(el.style.borderCollapse));
  1121.         input.checked = val ? 1 : 0;
  1122.         td.appendChild(input);
  1123.  
  1124.         td = doc.createElement("td");
  1125.         tr.appendChild(td);
  1126.         var label = doc.createElement("label");
  1127.         label.htmlFor = "f_st_borderCollapse";
  1128.         label.innerHTML = i18n["Collapsed borders"];
  1129.         td.appendChild(label);
  1130.     }
  1131.  
  1132. //     tr = doc.createElement("tr");
  1133. //     tbody.appendChild(tr);
  1134. //     td = doc.createElement("td");
  1135. //     td.className = "label";
  1136. //     tr.appendChild(td);
  1137. //     td.innerHTML = i18n["Margin"] + ":";
  1138. //     td = doc.createElement("td");
  1139. //     tr.appendChild(td);
  1140. //     input = doc.createElement("input");
  1141. //     input.type = "text";
  1142. //     input.size = "5";
  1143. //     input.name = "f_st_margin";
  1144. //     td.appendChild(input);
  1145. //     input.style.marginRight = "0.5em";
  1146. //     td.appendChild(doc.createTextNode(i18n["Padding"] + ":"));
  1147.  
  1148. //     input = doc.createElement("input");
  1149. //     input.type = "text";
  1150. //     input.size = "5";
  1151. //     input.name = "f_st_padding";
  1152. //     td.appendChild(input);
  1153. //     input.style.marginLeft = "0.5em";
  1154. //     input.style.marginRight = "0.5em";
  1155. //     td.appendChild(doc.createTextNode(i18n["pixels"]));
  1156.  
  1157.     return fieldset;
  1158. };
  1159.  
  1160. //// END GENERIC CODE -------------------------------------------------------
  1161.