Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / lib / editor / htmlarea / plugins / TableOperations / table-operations.js
blob02b4d20b68ff556e0c7f40479b1598af80faf869
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
13 // $Id$
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;
20     var cfg = editor.config;
21     var tt = TableOperations.I18N;
22     var bl = TableOperations.btnList;
23     var self = this;
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     }
42     // add a new line in the toolbar
43     cfg.toolbar.push(toolbar);
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"
57 /************************
58  * UTILITIES
59  ************************/
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;
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();
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     },
145     // this function gets called when the dialog needs to be initialized
146     function (dialog) {
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;
164         function selected(val) {
165             return val ? " selected" : "";
166         };
168         // dialog contents
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> &nbsp;&nbsp;" + 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 + "' /> &nbsp;" + i18n["Padding"] + ":\
212             <input type='text' name='f_padding' size='5' value='" + f_padding + "' /> &nbsp;&nbsp;" + 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 + "' /> &nbsp;&nbsp;" + 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> \
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         dialog.content.style.width = "400px";
272         if (document.all) {
273             dialog.content.style.height = dialog.content.clientHeight + 20 + 'px';
274         }
275     });
278 // this function requires the file PopupDiv/PopupWin to be loaded from browser
279 TableOperations.prototype.dialogRowCellProperties = function(cell) {
280     var i18n = TableOperations.I18N;
281     // retrieve existing values
282     var element = this.getClosest(cell ? "td" : "tr");
283     var table = this.getClosest("table");
284     // this.editor.selectNodeContents(element);
285     // this.editor.updateToolbar();
287     var dialog = new PopupWin(this.editor, i18n[cell ? "Cell Properties" : "Row Properties"], function(dialog, params) {
288         TableOperations.processStyle(params, element);
289         for (var i in params) {
290             var val = params[i];
291             switch (i) {
292                 case "f_align":
293                 element.align = val;
294                 break;
295                 case "f_char":
296                 element.ch = val;
297                 break;
298                 case "f_valign":
299                 element.vAlign = val;
300                 break;
301             }
302         }
303         // various workarounds to refresh the table display (Gecko,
304         // what's going on?! do not disappoint me!)
305         dialog.editor.forceRedraw();
306         dialog.editor.focusEditor();
307         dialog.editor.updateToolbar();
308         var save_collapse = table.style.borderCollapse;
309         table.style.borderCollapse = "collapse";
310         table.style.borderCollapse = "separate";
311         table.style.borderCollapse = save_collapse;
312     },
314     // this function gets called when the dialog needs to be initialized
315     function (dialog) {
317         var f_align = element.align;
318         var f_valign = element.vAlign;
319         var f_char = element.ch;
321         function selected(val) {
322             return val ? " selected" : "";
323         };
325         // dialog contents
326         dialog.content.style.width = "400px";
327         dialog.content.innerHTML = " \
328 <div class='title'\
329  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> \
330 <table style='width:100%'> \
331   <tr> \
332     <td id='--HA-layout'> \
333 "+//      <fieldset><legend>" + i18n["Layout"] + "</legend> \
334 //        <table style='width:100%'> \
335 //         <tr> \
336 //           <td class='label'>" + i18n["Align"] + ":</td> \
337 //           <td> \
338 //             <select name='f_align'> \
339 //               <option value='left'" + selected(f_align == "left") + ">" + i18n["Left"] + "</option> \
340 //               <option value='center'" + selected(f_align == "center") + ">" + i18n["Center"] + "</option> \
341 //               <option value='right'" + selected(f_align == "right") + ">" + i18n["Right"] + "</option> \
342 //               <option value='char'" + selected(f_align == "char") + ">" + i18n["Char"] + "</option> \
343 //             </select> \
344 //             &nbsp;&nbsp;" + i18n["Char"] + ": \
345 //             <input type='text' style='font-family: monospace; text-align: center' name='f_char' size='1' value='" + f_char + "' /> \
346 //           </td> \
347 //         </tr><tr> \
348 //           <td class='label'>" + i18n["Vertical align"] + ":</td> \
349 //           <td> \
350 //             <select name='f_valign'> \
351 //               <option value='top'" + selected(f_valign == "top") + ">" + i18n["Top"] + "</option> \
352 //               <option value='middle'" + selected(f_valign == "middle") + ">" + i18n["Middle"] + "</option> \
353 //               <option value='bottom'" + selected(f_valign == "bottom") + ">" + i18n["Bottom"] + "</option> \
354 //               <option value='baseline'" + selected(f_valign == "baseline") + ">" + i18n["Baseline"] + "</option> \
355 //             </select> \
356 //           </td> \
357 //         </tr> \
358 //        </table> \
359 //       </fieldset> \
360 "    </td> \
361   </tr> \
362   <tr> \
363     <td id='--HA-style'></td> \
364   </tr> \
365 </table> \
367         var st_prop = TableOperations.createStyleFieldset(dialog.doc, dialog.editor, element);
368         var p = dialog.doc.getElementById("--HA-style");
369         p.appendChild(st_prop);
370         var st_layout = TableOperations.createStyleLayoutFieldset(dialog.doc, dialog.editor, element);
371         p = dialog.doc.getElementById("--HA-layout");
372         p.appendChild(st_layout);
373         dialog.modal = true;
374         dialog.addButtons("ok", "cancel");
375         dialog.showAtElement(dialog.editor._iframe, "c");
376         if (document.all) {
377             dialog.content.style.height = dialog.content.clientHeight + 20 + 'px';
378         }
379     });
382 // this function gets called when some button from the TableOperations toolbar
383 // was pressed.
384 TableOperations.prototype.buttonPress = function(editor, button_id) {
385     this.editor = editor;
386     var mozbr = HTMLArea.is_gecko ? "<br />" : "";
387     var i18n = TableOperations.I18N;
389     // helper function that clears the content in a table row
390     function clearRow(tr) {
391         var tds = tr.getElementsByTagName("td");
392         for (var i = tds.length; --i >= 0;) {
393             var td = tds[i];
394             td.rowSpan = 1;
395             td.innerHTML = mozbr;
396         }
397     };
399     function splitRow(td) {
400         var n = parseInt("" + td.rowSpan);
401         var nc = parseInt("" + td.colSpan);
402         td.rowSpan = 1;
403         tr = td.parentNode;
404         var itr = tr.rowIndex;
405         var trs = tr.parentNode.rows;
406         var index = td.cellIndex;
407         while (--n > 0) {
408             tr = trs[++itr];
409             var otd = editor._doc.createElement("td");
410             otd.colSpan = td.colSpan;
411             otd.innerHTML = mozbr;
412             tr.insertBefore(otd, tr.cells[index]);
413         }
414         editor.forceRedraw();
415         editor.updateToolbar();
416     };
418     function splitCol(td) {
419         var nc = parseInt("" + td.colSpan);
420         td.colSpan = 1;
421         tr = td.parentNode;
422         var ref = td.nextSibling;
423         while (--nc > 0) {
424             var otd = editor._doc.createElement("td");
425             otd.rowSpan = td.rowSpan;
426             otd.innerHTML = mozbr;
427             tr.insertBefore(otd, ref);
428         }
429         editor.forceRedraw();
430         editor.updateToolbar();
431     };
433     function splitCell(td) {
434         var nc = parseInt("" + td.colSpan);
435         splitCol(td);
436         var items = td.parentNode.cells;
437         var index = td.cellIndex;
438         while (nc-- > 0) {
439             splitRow(items[index++]);
440         }
441     };
443     function selectNextNode(el) {
444         var node = el.nextSibling;
445         while (node && node.nodeType != 1) {
446             node = node.nextSibling;
447         }
448         if (!node) {
449             node = el.previousSibling;
450             while (node && node.nodeType != 1) {
451                 node = node.previousSibling;
452             }
453         }
454         if (!node) {
455             node = el.parentNode;
456         }
457         editor.selectNodeContents(node);
458     };
460     switch (button_id) {
461         // ROWS
463         case "TO-row-insert-above":
464         case "TO-row-insert-under":
465         var tr = this.getClosest("tr");
466         if (!tr) {
467             break;
468         }
469         var otr = tr.cloneNode(true);
470         clearRow(otr);
471         tr.parentNode.insertBefore(otr, /under/.test(button_id) ? tr.nextSibling : tr);
472         editor.forceRedraw();
473         editor.focusEditor();
474         break;
475         case "TO-row-delete":
476         var tr = this.getClosest("tr");
477         if (!tr) {
478             break;
479         }
480         var par = tr.parentNode;
481         if (par.rows.length == 1) {
482             alert(i18n["not-del-last-row"]);
483             break;
484         }
485         // set the caret first to a position that doesn't
486         // disappear.
487         selectNextNode(tr);
488         par.removeChild(tr);
489         editor.forceRedraw();
490         editor.focusEditor();
491         editor.updateToolbar();
492         break;
493         case "TO-row-split":
494         var td = this.getClosest("td");
495         if (!td) {
496             break;
497         }
498         splitRow(td);
499         break;
501         // COLUMNS
503         case "TO-col-insert-before":
504         case "TO-col-insert-after":
505         var td = this.getClosest("td");
506         if (!td) {
507             break;
508         }
509         var rows = td.parentNode.parentNode.rows;
510         var index = td.cellIndex;
511         for (var i = rows.length; --i >= 0;) {
512             var tr = rows[i];
513             var ref = tr.cells[index + (/after/.test(button_id) ? 1 : 0)];
514             var otd = editor._doc.createElement("td");
515             otd.innerHTML = mozbr;
516             tr.insertBefore(otd, ref);
517         }
518         editor.focusEditor();
519         break;
520         case "TO-col-split":
521         var td = this.getClosest("td");
522         if (!td) {
523             break;
524         }
525         splitCol(td);
526         break;
527         case "TO-col-delete":
528         var td = this.getClosest("td");
529         if (!td) {
530             break;
531         }
532         var index = td.cellIndex;
533         if (td.parentNode.cells.length == 1) {
534             alert(i18n["not-del-last-col"]);
535             break;
536         }
537         // set the caret first to a position that doesn't disappear
538         selectNextNode(td);
539         var rows = td.parentNode.parentNode.rows;
540         for (var i = rows.length; --i >= 0;) {
541             var tr = rows[i];
542             tr.removeChild(tr.cells[index]);
543         }
544         editor.forceRedraw();
545         editor.focusEditor();
546         editor.updateToolbar();
547         break;
549         // CELLS
551         case "TO-cell-split":
552         var td = this.getClosest("td");
553         if (!td) {
554             break;
555         }
556         splitCell(td);
557         break;
558         case "TO-cell-insert-before":
559         case "TO-cell-insert-after":
560         var td = this.getClosest("td");
561         if (!td) {
562             break;
563         }
564         var tr = td.parentNode;
565         var otd = editor._doc.createElement("td");
566         otd.innerHTML = mozbr;
567         tr.insertBefore(otd, /after/.test(button_id) ? td.nextSibling : td);
568         editor.forceRedraw();
569         editor.focusEditor();
570         break;
571         case "TO-cell-delete":
572         var td = this.getClosest("td");
573         if (!td) {
574             break;
575         }
576         if (td.parentNode.cells.length == 1) {
577             alert(i18n["not-del-last-cell"]);
578             break;
579         }
580         // set the caret first to a position that doesn't disappear
581         selectNextNode(td);
582         td.parentNode.removeChild(td);
583         editor.forceRedraw();
584         editor.updateToolbar();
585         break;
586         case "TO-cell-merge":
587         // !! FIXME: Mozilla specific !!
588         var sel = editor._getSelection();
589         var range, i = 0;
590         var rows = [];
591         var row = null;
592         var cells = null;
593         if (!HTMLArea.is_ie) {
594             try {
595                 while (range = sel.getRangeAt(i++)) {
596                     var td = range.startContainer.childNodes[range.startOffset];
597                     if (td.parentNode != row) {
598                         row = td.parentNode;
599                         (cells) && rows.push(cells);
600                         cells = [];
601                     }
602                     cells.push(td);
603                 }
604             } catch(e) {/* finished walking through selection */}
605             rows.push(cells);
606         } else {
607             // Internet Explorer "browser"
608             var td = this.getClosest("td");
609             if (!td) {
610                 alert(i18n["Please click into some cell"]);
611                 break;
612             }
613             var tr = td.parentElement;
614             var no_cols = prompt(i18n["How many columns would you like to merge?"], 2);
615             if (!no_cols) {
616                 // cancelled
617                 break;
618             }
619             var no_rows = prompt(i18n["How many rows would you like to merge?"], 2);
620             if (!no_rows) {
621                 // cancelled
622                 break;
623             }
624             var cell_index = td.cellIndex;
625             while (no_rows-- > 0) {
626                 td = tr.cells[cell_index];
627                 cells = [td];
628                 for (var i = 1; i < no_cols; ++i) {
629                     td = td.nextSibling;
630                     if (!td) {
631                         break;
632                     }
633                     cells.push(td);
634                 }
635                 rows.push(cells);
636                 tr = tr.nextSibling;
637                 if (!tr) {
638                     break;
639                 }
640             }
641         }
642         var HTML = "";
643         for (i = 0; i < rows.length; ++i) {
644             // i && (HTML += "<br />");
645             var cells = rows[i];
646             for (var j = 0; j < cells.length; ++j) {
647                 // j && (HTML += "&nbsp;");
648                 var cell = cells[j];
649                 HTML += cell.innerHTML;
650                 (i || j) && (cell.parentNode.removeChild(cell));
651             }
652         }
653         var td = rows[0][0];
654         td.innerHTML = HTML;
655         td.rowSpan = rows.length;
656         td.colSpan = rows[0].length;
657         editor.selectNodeContents(td);
658         editor.forceRedraw();
659         editor.focusEditor();
660         break;
662         // PROPERTIES
664         case "TO-table-prop":
665         this.dialogTableProperties();
666         break;
668         case "TO-row-prop":
669         this.dialogRowCellProperties(false);
670         break;
672         case "TO-cell-prop":
673         this.dialogRowCellProperties(true);
674         break;
676         default:
677         alert("Button [" + button_id + "] not yet implemented");
678     }
681 // the list of buttons added by this plugin
682 TableOperations.btnList = [
683     // table properties button
684     ["table-prop",       "table"],
685     null,           // separator
687     // ROWS
688     ["row-prop",         "tr"],
689     ["row-insert-above", "tr"],
690     ["row-insert-under", "tr"],
691     ["row-delete",       "tr"],
692     ["row-split",        "td[rowSpan!=1]"],
693     null,
695     // COLS
696     ["col-insert-before", "td"],
697     ["col-insert-after",  "td"],
698     ["col-delete",        "td"],
699     ["col-split",         "td[colSpan!=1]"],
700     null,
702     // CELLS
703     ["cell-prop",          "td"],
704     ["cell-insert-before", "td"],
705     ["cell-insert-after",  "td"],
706     ["cell-delete",        "td"],
707     ["cell-merge",         "tr"],
708     ["cell-split",         "td[colSpan!=1,rowSpan!=1]"]
709     ];
713 //// GENERIC CODE [style of any element; this should be moved into a separate
714 //// file as it'll be very useful]
715 //// BEGIN GENERIC CODE -----------------------------------------------------
717 TableOperations.getLength = function(value) {
718     var len = parseInt(value);
719     if (isNaN(len)) {
720         len = "";
721     }
722     return len;
725 // Applies the style found in "params" to the given element.
726 TableOperations.processStyle = function(params, element) {
727     var style = element.style;
728     for (var i in params) {
729         var val = params[i];
730         switch (i) {
731             case "f_st_backgroundColor":
732             style.backgroundColor = val;
733             break;
734             case "f_st_color":
735             style.color = val;
736             break;
737             case "f_st_backgroundImage":
738             if (/\S/.test(val)) {
739                 style.backgroundImage = "url(" + val + ")";
740             } else {
741                 style.backgroundImage = "none";
742             }
743             break;
744             case "f_st_borderWidth":
745             style.borderWidth = val;
746             break;
747             case "f_st_borderStyle":
748             style.borderStyle = val;
749             break;
750             case "f_st_borderColor":
751             style.borderColor = val;
752             break;
753             case "f_st_borderCollapse":
754             style.borderCollapse = val ? "collapse" : "";
755             break;
756             case "f_st_width":
757             if (/\S/.test(val)) {
758                 style.width = val + params["f_st_widthUnit"];
759             } else {
760                 style.width = "";
761             }
762             break;
763             case "f_st_height":
764             if (/\S/.test(val)) {
765                 style.height = val + params["f_st_heightUnit"];
766             } else {
767                 style.height = "";
768             }
769             break;
770             case "f_st_textAlign":
771             if (val == "char") {
772                 var ch = params["f_st_textAlignChar"];
773                 if (ch == '"') {
774                     ch = '\\"';
775                 }
776                 style.textAlign = '"' + ch + '"';
777             } else {
778                 style.textAlign = val;
779             }
780             break;
781             case "f_st_verticalAlign":
782             style.verticalAlign = val;
783             break;
784             case "f_st_float":
785             style.cssFloat = val;
786             break;
787 //          case "f_st_margin":
788 //          style.margin = val + "px";
789 //          break;
790 //          case "f_st_padding":
791 //          style.padding = val + "px";
792 //          break;
793         }
794     }
797 // Returns an HTML element for a widget that allows color selection.  That is,
798 // a button that contains the given color, if any, and when pressed will popup
799 // the sooner-or-later-to-be-rewritten select_color.html dialog allowing user
800 // to select some color.  If a color is selected, an input field with the name
801 // "f_st_"+name will be updated with the color value in #123456 format.
802 TableOperations.createColorButton = function(doc, editor, color, name) {
803     if (!color) {
804         color = "";
805     } else if (!/#/.test(color)) {
806         color = HTMLArea._colorToRgb(color);
807     }
809     var df = doc.createElement("span");
810     var field = doc.createElement("input");
811     field.type = "hidden";
812     df.appendChild(field);
813     field.name = "f_st_" + name;
814     field.value = color;
815     var button = doc.createElement("span");
816     button.className = "buttonColor";
817     df.appendChild(button);
818     var span = doc.createElement("span");
819     span.className = "chooser";
820     // span.innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
821     span.style.backgroundColor = color;
822     button.appendChild(span);
823     button.onmouseover = function() { if (!this.disabled) { this.className += " buttonColor-hilite"; }};
824     button.onmouseout = function() { if (!this.disabled) { this.className = "buttonColor"; }};
825     span.onclick = function() {
826         if (this.parentNode.disabled) {
827             return false;
828         }
829         editor._popupDialog("select_color.php", function(color) {
830             if (color) {
831                 span.style.backgroundColor = "#" + color;
832                 field.value = "#" + color;
833             }
834         }, color);
835     };
836     var span2 = doc.createElement("span");
837     span2.innerHTML = "&#x00d7;";
838     span2.className = "nocolor";
839     span2.title = TableOperations.I18N["Unset color"];
840     button.appendChild(span2);
841     span2.onmouseover = function() { if (!this.parentNode.disabled) { this.className += " nocolor-hilite"; }};
842     span2.onmouseout = function() { if (!this.parentNode.disabled) { this.className = "nocolor"; }};
843     span2.onclick = function() {
844         span.style.backgroundColor = "";
845         field.value = "";
846     };
847     return df;
850 TableOperations.createStyleLayoutFieldset = function(doc, editor, el) {
851     var i18n = TableOperations.I18N;
852     var fieldset = doc.createElement("fieldset");
853     var legend = doc.createElement("legend");
854     fieldset.appendChild(legend);
855     legend.innerHTML = i18n["Layout"];
856     var table = doc.createElement("table");
857     fieldset.appendChild(table);
858     table.style.width = "100%";
859     var tbody = doc.createElement("tbody");
860     table.appendChild(tbody);
862     var tagname = el.tagName.toLowerCase();
863     var tr, td, input, select, option, options, i;
865     if (tagname != "td" && tagname != "tr" && tagname != "th") {
866         tr = doc.createElement("tr");
867         tbody.appendChild(tr);
868         td = doc.createElement("td");
869         td.className = "label";
870         tr.appendChild(td);
871         td.innerHTML = i18n["Float"] + ":";
872         td = doc.createElement("td");
873         tr.appendChild(td);
874         select = doc.createElement("select");
875         td.appendChild(select);
876         select.name = "f_st_float";
877         options = ["None", "Left", "Right"];
878         for (i in options) {
879             var Val = options[i];
880             var val = options[i].toLowerCase();
881             option = doc.createElement("option");
882             option.innerHTML = i18n[Val];
883             option.value = val;
884             option.selected = (("" + el.style.cssFloat).toLowerCase() == val);
885             select.appendChild(option);
886         }
887     }
889     tr = doc.createElement("tr");
890     tbody.appendChild(tr);
891     td = doc.createElement("td");
892     td.className = "label";
893     tr.appendChild(td);
894     td.innerHTML = i18n["Width"] + ":";
895     td = doc.createElement("td");
896     tr.appendChild(td);
897     input = doc.createElement("input");
898     input.type = "text";
899     input.value = TableOperations.getLength(el.style.width);
900     input.size = "5";
901     input.name = "f_st_width";
902     input.style.marginRight = "0.5em";
903     td.appendChild(input);
904     select = doc.createElement("select");
905     select.name = "f_st_widthUnit";
906     option = doc.createElement("option");
907     option.innerHTML = i18n["percent"];
908     option.value = "%";
909     option.selected = /%/.test(el.style.width);
910     select.appendChild(option);
911     option = doc.createElement("option");
912     option.innerHTML = i18n["pixels"];
913     option.value = "px";
914     option.selected = /px/.test(el.style.width);
915     select.appendChild(option);
916     td.appendChild(select);
918     select.style.marginRight = "0.5em";
919     td.appendChild(doc.createTextNode(i18n["Text align"] + ":"));
920     select = doc.createElement("select");
921     select.style.marginLeft = select.style.marginRight = "0.5em";
922     td.appendChild(select);
923     select.name = "f_st_textAlign";
924     options = ["Left", "Center", "Right", "Justify"];
925     if (tagname == "td") {
926         options.push("Char");
927     }
928     input = doc.createElement("input");
929     input.name = "f_st_textAlignChar";
930     input.size = "1";
931     input.style.fontFamily = "monospace";
932     td.appendChild(input);
933     for (i in options) {
934         var Val = options[i];
935         var val = Val.toLowerCase();
936         option = doc.createElement("option");
937         option.value = val;
938         option.innerHTML = i18n[Val];
939         option.selected = (el.style.textAlign.toLowerCase() == val);
940         select.appendChild(option);
941     }
942     function setCharVisibility(value) {
943         input.style.visibility = value ? "visible" : "hidden";
944         if (value) {
945             input.focus();
946             input.select();
947         }
948     };
949     select.onchange = function() { setCharVisibility(this.value == "char"); };
950     setCharVisibility(select.value == "char");
952     tr = doc.createElement("tr");
953     tbody.appendChild(tr);
954     td = doc.createElement("td");
955     td.className = "label";
956     tr.appendChild(td);
957     td.innerHTML = i18n["Height"] + ":";
958     td = doc.createElement("td");
959     tr.appendChild(td);
960     input = doc.createElement("input");
961     input.type = "text";
962     input.value = TableOperations.getLength(el.style.height);
963     input.size = "5";
964     input.name = "f_st_height";
965     input.style.marginRight = "0.5em";
966     td.appendChild(input);
967     select = doc.createElement("select");
968     select.name = "f_st_heightUnit";
969     option = doc.createElement("option");
970     option.innerHTML = i18n["percent"];
971     option.value = "%";
972     option.selected = /%/.test(el.style.height);
973     select.appendChild(option);
974     option = doc.createElement("option");
975     option.innerHTML = i18n["pixels"];
976     option.value = "px";
977     option.selected = /px/.test(el.style.height);
978     select.appendChild(option);
979     td.appendChild(select);
981     select.style.marginRight = "0.5em";
982     td.appendChild(doc.createTextNode(i18n["Vertical align"] + ":"));
983     select = doc.createElement("select");
984     select.name = "f_st_verticalAlign";
985     select.style.marginLeft = "0.5em";
986     td.appendChild(select);
987     options = ["Top", "Middle", "Bottom", "Baseline"];
988     for (i in options) {
989         var Val = options[i];
990         var val = Val.toLowerCase();
991         option = doc.createElement("option");
992         option.value = val;
993         option.innerHTML = i18n[Val];
994         option.selected = (el.style.verticalAlign.toLowerCase() == val);
995         select.appendChild(option);
996     }
998     return fieldset;
1001 // Returns an HTML element containing the style attributes for the given
1002 // element.  This can be easily embedded into any dialog; the functionality is
1003 // also provided.
1004 TableOperations.createStyleFieldset = function(doc, editor, el) {
1005     var i18n = TableOperations.I18N;
1006     var fieldset = doc.createElement("fieldset");
1007     var legend = doc.createElement("legend");
1008     fieldset.appendChild(legend);
1009     legend.innerHTML = i18n["CSS Style"];
1010     var table = doc.createElement("table");
1011     fieldset.appendChild(table);
1012     table.style.width = "100%";
1013     var tbody = doc.createElement("tbody");
1014     table.appendChild(tbody);
1016     var tr, td, input, select, option, options, i;
1018     tr = doc.createElement("tr");
1019     tbody.appendChild(tr);
1020     td = doc.createElement("td");
1021     tr.appendChild(td);
1022     td.className = "label";
1023     td.innerHTML = i18n["Background"] + ":";
1024     td = doc.createElement("td");
1025     tr.appendChild(td);
1026     var df = TableOperations.createColorButton(doc, editor, el.style.backgroundColor, "backgroundColor");
1027     df.firstChild.nextSibling.style.marginRight = "0.5em";
1028     td.appendChild(df);
1029     td.appendChild(doc.createTextNode(i18n["Image URL"] + ": "));
1030     input = doc.createElement("input");
1031     input.type = "text";
1032     input.name = "f_st_backgroundImage";
1033     if (el.style.backgroundImage.match(/url\(\s*(.*?)\s*\)/)) {
1034         input.value = RegExp.$1;
1035     }
1036     // input.style.width = "100%";
1037     td.appendChild(input);
1039     tr = doc.createElement("tr");
1040     tbody.appendChild(tr);
1041     td = doc.createElement("td");
1042     tr.appendChild(td);
1043     td.className = "label";
1044     td.innerHTML = i18n["FG Color"] + ":";
1045     td = doc.createElement("td");
1046     tr.appendChild(td);
1047     td.appendChild(TableOperations.createColorButton(doc, editor, el.style.color, "color"));
1049     // for better alignment we include an invisible field.
1050     input = doc.createElement("input");
1051     input.style.visibility = "hidden";
1052     input.type = "text";
1053     td.appendChild(input);
1055     tr = doc.createElement("tr");
1056     tbody.appendChild(tr);
1057     td = doc.createElement("td");
1058     tr.appendChild(td);
1059     td.className = "label";
1060     td.innerHTML = i18n["Border"] + ":";
1061     td = doc.createElement("td");
1062     tr.appendChild(td);
1064     var colorButton = TableOperations.createColorButton(doc, editor, el.style.borderColor, "borderColor");
1065     var btn = colorButton.firstChild.nextSibling;
1066     td.appendChild(colorButton);
1067     // borderFields.push(btn);
1068     btn.style.marginRight = "0.5em";
1070     select = doc.createElement("select");
1071     var borderFields = [];
1072     td.appendChild(select);
1073     select.name = "f_st_borderStyle";
1074     options = ["none", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset"];
1075     var currentBorderStyle = el.style.borderStyle;
1076     // Gecko reports "solid solid solid solid" for "border-style: solid".
1077     // That is, "top right bottom left" -- we only consider the first
1078     // value.
1079     (currentBorderStyle.match(/([^\s]*)\s/)) && (currentBorderStyle = RegExp.$1);
1080     for (i in options) {
1081         var val = options[i];
1082         option = doc.createElement("option");
1083         option.value = val;
1084         option.innerHTML = val;
1085         (val == currentBorderStyle) && (option.selected = true);
1086         select.appendChild(option);
1087     }
1088     select.style.marginRight = "0.5em";
1089     function setBorderFieldsStatus(value) {
1090         for (i in borderFields) {
1091             var el = borderFields[i];
1092             el.style.visibility = value ? "hidden" : "visible";
1093             if (!value && (el.tagName.toLowerCase() == "input")) {
1094                 el.focus();
1095                 el.select();
1096             }
1097         }
1098     };
1099     select.onchange = function() { setBorderFieldsStatus(this.value == "none"); };
1101     input = doc.createElement("input");
1102     borderFields.push(input);
1103     input.type = "text";
1104     input.name = "f_st_borderWidth";
1105     input.value = TableOperations.getLength(el.style.borderWidth);
1106     input.size = "5";
1107     td.appendChild(input);
1108     input.style.marginRight = "0.5em";
1109     var span = doc.createElement("span");
1110     span.innerHTML = i18n["pixels"];
1111     td.appendChild(span);
1112     borderFields.push(span);
1114     setBorderFieldsStatus(select.value == "none");
1116     if (el.tagName.toLowerCase() == "table") {
1117         // the border-collapse style is only for tables
1118         tr = doc.createElement("tr");
1119         tbody.appendChild(tr);
1120         td = doc.createElement("td");
1121         td.className = "label";
1122         tr.appendChild(td);
1123         input = doc.createElement("input");
1124         input.type = "checkbox";
1125         input.name = "f_st_borderCollapse";
1126         input.id = "f_st_borderCollapse";
1127         var val = (/collapse/i.test(el.style.borderCollapse));
1128         input.checked = val ? 1 : 0;
1129         td.appendChild(input);
1131         td = doc.createElement("td");
1132         tr.appendChild(td);
1133         var label = doc.createElement("label");
1134         label.htmlFor = "f_st_borderCollapse";
1135         label.innerHTML = i18n["Collapsed borders"];
1136         td.appendChild(label);
1137     }
1139 //  tr = doc.createElement("tr");
1140 //  tbody.appendChild(tr);
1141 //  td = doc.createElement("td");
1142 //  td.className = "label";
1143 //  tr.appendChild(td);
1144 //  td.innerHTML = i18n["Margin"] + ":";
1145 //  td = doc.createElement("td");
1146 //  tr.appendChild(td);
1147 //  input = doc.createElement("input");
1148 //  input.type = "text";
1149 //  input.size = "5";
1150 //  input.name = "f_st_margin";
1151 //  td.appendChild(input);
1152 //  input.style.marginRight = "0.5em";
1153 //  td.appendChild(doc.createTextNode(i18n["Padding"] + ":"));
1155 //  input = doc.createElement("input");
1156 //  input.type = "text";
1157 //  input.size = "5";
1158 //  input.name = "f_st_padding";
1159 //  td.appendChild(input);
1160 //  input.style.marginLeft = "0.5em";
1161 //  input.style.marginRight = "0.5em";
1162 //  td.appendChild(doc.createTextNode(i18n["pixels"]));
1164     return fieldset;
1167 //// END GENERIC CODE -------------------------------------------------------