source: trunk/plugins/TableOperations/table-operations.js @ 988

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