yllar
2006-12-14 38bf9d3b71067a51ffc9a915ea288929d1fb08e4
commit | author | age
a0109c 1 /**
f0ea59 2  * $Id: editor_plugin_src.js 126 2006-10-22 16:19:55Z spocke $
a0109c 3  *
S 4  * @author Moxiecode
5  * @copyright Copyright © 2004-2006, Moxiecode Systems AB, All rights reserved.
6  */
7
8 /* Import plugin specific language pack */
f0ea59 9 tinyMCE.importPluginLanguagePack('table');
a0109c 10
S 11 var TinyMCE_TablePlugin = {
12     getInfo : function() {
13         return {
14             longname : 'Tables',
f0ea59 15             author : 'Moxiecode Systems AB',
a0109c 16             authorurl : 'http://tinymce.moxiecode.com',
S 17             infourl : 'http://tinymce.moxiecode.com/tinymce/docs/plugin_table.html',
18             version : tinyMCE.majorVersion + "." + tinyMCE.minorVersion
19         };
20     },
21
22     initInstance : function(inst) {
23         if (tinyMCE.isGecko) {
24             var doc = inst.getDoc();
25             tinyMCE.addEvent(doc, "mouseup", TinyMCE_TablePlugin._mouseDownHandler);
26         }
27
28         inst.tableRowClipboard = null;
29     },
30
31     /**
32      * Returns the HTML contents of the table control.
33      */
34     getControlHTML : function(control_name) {
35         var controls = new Array(
36             ['table', 'table.gif', 'lang_table_desc', 'mceInsertTable', true],
f0ea59 37             ['delete_table', 'table_delete.gif', 'lang_table_del', 'mceTableDelete'],
a0109c 38             ['delete_col', 'table_delete_col.gif', 'lang_table_delete_col_desc', 'mceTableDeleteCol'],
S 39             ['delete_row', 'table_delete_row.gif', 'lang_table_delete_row_desc', 'mceTableDeleteRow'],
40             ['col_after', 'table_insert_col_after.gif', 'lang_table_col_after_desc', 'mceTableInsertColAfter'],
41             ['col_before', 'table_insert_col_before.gif', 'lang_table_col_before_desc', 'mceTableInsertColBefore'],
42             ['row_after', 'table_insert_row_after.gif', 'lang_table_row_after_desc', 'mceTableInsertRowAfter'],
43             ['row_before', 'table_insert_row_before.gif', 'lang_table_row_before_desc', 'mceTableInsertRowBefore'],
44             ['row_props', 'table_row_props.gif', 'lang_table_row_desc', 'mceTableRowProps', true],
45             ['cell_props', 'table_cell_props.gif', 'lang_table_cell_desc', 'mceTableCellProps', true],
46             ['split_cells', 'table_split_cells.gif', 'lang_table_split_cells_desc', 'mceTableSplitCells', true],
47             ['merge_cells', 'table_merge_cells.gif', 'lang_table_merge_cells_desc', 'mceTableMergeCells', true]);
48
49         // Render table control
50         for (var i=0; i<controls.length; i++) {
51             var but = controls[i];
52             var cmd = 'tinyMCE.execInstanceCommand(\'{$editor_id}\',\'' + but[3] + '\', ' + (but.length > 4 ? but[4] : false) + (but.length > 5 ? ', \'' + but[5] + '\'' : '') + ');return false;';
53
54             if (but[0] == control_name)
55                 return tinyMCE.getButtonHTML(control_name, but[2], '{$pluginurl}/images/'+ but[1], but[3], (but.length > 4 ? but[4] : false));
56         }
57
58         // Special tablecontrols
59         if (control_name == "tablecontrols") {
60             var html = "";
61
62             html += tinyMCE.getControlHTML("table");
63             html += tinyMCE.getControlHTML("separator");
64             html += tinyMCE.getControlHTML("row_props");
65             html += tinyMCE.getControlHTML("cell_props");
66             html += tinyMCE.getControlHTML("separator");
67             html += tinyMCE.getControlHTML("row_before");
68             html += tinyMCE.getControlHTML("row_after");
69             html += tinyMCE.getControlHTML("delete_row");
70             html += tinyMCE.getControlHTML("separator");
71             html += tinyMCE.getControlHTML("col_before");
72             html += tinyMCE.getControlHTML("col_after");
73             html += tinyMCE.getControlHTML("delete_col");
74             html += tinyMCE.getControlHTML("separator");
75             html += tinyMCE.getControlHTML("split_cells");
76             html += tinyMCE.getControlHTML("merge_cells");
77
78             return html;
79         }
80
81         return "";
82     },
83
84     /**
85      * Executes the table commands.
86      */
87     execCommand : function(editor_id, element, command, user_interface, value) {
88         // Is table command
89         switch (command) {
90             case "mceInsertTable":
91             case "mceTableRowProps":
92             case "mceTableCellProps":
93             case "mceTableSplitCells":
94             case "mceTableMergeCells":
95             case "mceTableInsertRowBefore":
96             case "mceTableInsertRowAfter":
97             case "mceTableDeleteRow":
98             case "mceTableInsertColBefore":
99             case "mceTableInsertColAfter":
100             case "mceTableDeleteCol":
101             case "mceTableCutRow":
102             case "mceTableCopyRow":
103             case "mceTablePasteRowBefore":
104             case "mceTablePasteRowAfter":
105             case "mceTableDelete":
106                 var inst = tinyMCE.getInstanceById(editor_id);
107
108                 inst.execCommand('mceBeginUndoLevel');
109                 TinyMCE_TablePlugin._doExecCommand(editor_id, element, command, user_interface, value);
110                 inst.execCommand('mceEndUndoLevel');
111
112                 return true;
113         }
114
115         // Pass to next handler in chain
116         return false;
117     },
118
119     handleNodeChange : function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) {
f0ea59 120         var colspan = "1", rowspan = "1", tdElm;
a0109c 121
S 122         var inst = tinyMCE.getInstanceById(editor_id);
123
124         // Reset table controls
125         tinyMCE.switchClass(editor_id + '_table', 'mceButtonNormal');
f0ea59 126         tinyMCE.switchClass(editor_id + '_delete_table', 'mceButtonDisabled');
a0109c 127         tinyMCE.switchClass(editor_id + '_row_props', 'mceButtonDisabled');
S 128         tinyMCE.switchClass(editor_id + '_cell_props', 'mceButtonDisabled');
129         tinyMCE.switchClass(editor_id + '_row_before', 'mceButtonDisabled');
130         tinyMCE.switchClass(editor_id + '_row_after', 'mceButtonDisabled');
131         tinyMCE.switchClass(editor_id + '_delete_row', 'mceButtonDisabled');
132         tinyMCE.switchClass(editor_id + '_col_before', 'mceButtonDisabled');
133         tinyMCE.switchClass(editor_id + '_col_after', 'mceButtonDisabled');
134         tinyMCE.switchClass(editor_id + '_delete_col', 'mceButtonDisabled');
135         tinyMCE.switchClass(editor_id + '_split_cells', 'mceButtonDisabled');
136         tinyMCE.switchClass(editor_id + '_merge_cells', 'mceButtonDisabled');
137
138         // Within a td element
139         if (tdElm = tinyMCE.getParentElement(node, "td,th")) {
140             tinyMCE.switchClass(editor_id + '_cell_props', 'mceButtonSelected');
f0ea59 141             tinyMCE.switchClass(editor_id + '_delete_table', 'mceButtonNormal');
a0109c 142             tinyMCE.switchClass(editor_id + '_row_before', 'mceButtonNormal');
S 143             tinyMCE.switchClass(editor_id + '_row_after', 'mceButtonNormal');
144             tinyMCE.switchClass(editor_id + '_delete_row', 'mceButtonNormal');
145             tinyMCE.switchClass(editor_id + '_col_before', 'mceButtonNormal');
146             tinyMCE.switchClass(editor_id + '_col_after', 'mceButtonNormal');
147             tinyMCE.switchClass(editor_id + '_delete_col', 'mceButtonNormal');
148
149             colspan = tinyMCE.getAttrib(tdElm, "colspan");
150             rowspan = tinyMCE.getAttrib(tdElm, "rowspan");
151
152             colspan = colspan == "" ? "1" : colspan;
153             rowspan = rowspan == "" ? "1" : rowspan;
154
155             if (colspan != "1" || rowspan != "1")
156                 tinyMCE.switchClass(editor_id + '_split_cells', 'mceButtonNormal');
157         }
158
159         // Within a tr element
160         if (tinyMCE.getParentElement(node, "tr"))
161             tinyMCE.switchClass(editor_id + '_row_props', 'mceButtonSelected');
162
163         // Within table
164         if (tinyMCE.getParentElement(node, "table")) {
165             tinyMCE.switchClass(editor_id + '_table', 'mceButtonSelected');
166             tinyMCE.switchClass(editor_id + '_merge_cells', 'mceButtonNormal');
167         }
168     },
169
170     // Private plugin internal methods
171
172     _mouseDownHandler : function(e) {
173         var elm = tinyMCE.isMSIE ? event.srcElement : e.target;
174         var focusElm = tinyMCE.selectedInstance.getFocusElement();
175
176         // If press on special Mozilla create TD/TR thingie
177         if (elm.nodeName == "BODY" && (focusElm.nodeName == "TD" || focusElm.nodeName == "TH" || (focusElm.parentNode && focusElm.parentNode.nodeName == "TD") ||(focusElm.parentNode && focusElm.parentNode.nodeName == "TH") )) {
178             window.setTimeout(function() {
179                 var tableElm = tinyMCE.getParentElement(focusElm, "table");
180                 tinyMCE.handleVisualAid(tableElm, true, tinyMCE.settings['visual'], tinyMCE.selectedInstance);
181             }, 10);
182         }
183     },
184
185     /**
186      * Executes the table commands.
187      */
188     _doExecCommand : function(editor_id, element, command, user_interface, value) {
189         var inst = tinyMCE.getInstanceById(editor_id);
190         var focusElm = inst.getFocusElement();
191         var trElm = tinyMCE.getParentElement(focusElm, "tr");
192         var tdElm = tinyMCE.getParentElement(focusElm, "td,th");
193         var tableElm = tinyMCE.getParentElement(focusElm, "table");
194         var doc = inst.contentWindow.document;
195         var tableBorder = tableElm ? tableElm.getAttribute("border") : "";
196
197         // Get first TD if no TD found
198         if (trElm && tdElm == null)
199             tdElm = trElm.cells[0];
200
201         // ------- Inner functions ---------
202         function inArray(ar, v) {
203             for (var i=0; i<ar.length; i++) {
204                 // Is array
205                 if (ar[i].length > 0 && inArray(ar[i], v))
206                     return true;
207
208                 // Found value
209                 if (ar[i] == v)
210                     return true;
211             }
212
213             return false;
214         }
215
216         function makeTD() {
217             var newTD = doc.createElement("td");
218             newTD.innerHTML = "&nbsp;";
219         }
220
221         function getColRowSpan(td) {
222             var colspan = tinyMCE.getAttrib(td, "colspan");
223             var rowspan = tinyMCE.getAttrib(td, "rowspan");
224
225             colspan = colspan == "" ? 1 : parseInt(colspan);
226             rowspan = rowspan == "" ? 1 : parseInt(rowspan);
227
228             return {colspan : colspan, rowspan : rowspan};
229         }
230
231         function getCellPos(grid, td) {
f0ea59 232             var x, y;
S 233
234             for (y=0; y<grid.length; y++) {
235                 for (x=0; x<grid[y].length; x++) {
a0109c 236                     if (grid[y][x] == td)
S 237                         return {cellindex : x, rowindex : y};
238                 }
239             }
240
241             return null;
242         }
243
244         function getCell(grid, row, col) {
245             if (grid[row] && grid[row][col])
246                 return grid[row][col];
247
248             return null;
249         }
250
251         function getTableGrid(table) {
f0ea59 252             var grid = new Array(), rows = table.rows, x, y, td, sd, xstart, x2, y2;
a0109c 253
f0ea59 254             for (y=0; y<rows.length; y++) {
S 255                 for (x=0; x<rows[y].cells.length; x++) {
256                     td = rows[y].cells[x];
257                     sd = getColRowSpan(td);
a0109c 258
S 259                     // All ready filled
260                     for (xstart = x; grid[y] && grid[y][xstart]; xstart++) ;
261
262                     // Fill box
f0ea59 263                     for (y2=y; y2<y+sd['rowspan']; y2++) {
a0109c 264                         if (!grid[y2])
S 265                             grid[y2] = new Array();
266
f0ea59 267                         for (x2=xstart; x2<xstart+sd['colspan']; x2++)
a0109c 268                             grid[y2][x2] = td;
S 269                     }
270                 }
271             }
272
273             return grid;
274         }
275
276         function trimRow(table, tr, td, new_tr) {
f0ea59 277             var grid = getTableGrid(table), cpos = getCellPos(grid, td);
S 278             var cells, lastElm;
a0109c 279
S 280             // Time to crop away some
281             if (new_tr.cells.length != tr.childNodes.length) {
f0ea59 282                 cells = tr.childNodes;
S 283                 lastElm = null;
a0109c 284
S 285                 for (var x=0; td = getCell(grid, cpos.rowindex, x); x++) {
286                     var remove = true;
287                     var sd = getColRowSpan(td);
288
289                     // Remove due to rowspan
290                     if (inArray(cells, td)) {
291                         new_tr.childNodes[x]._delete = true;
292                     } else if ((lastElm == null || td != lastElm) && sd.colspan > 1) { // Remove due to colspan
293                         for (var i=x; i<x+td.colSpan; i++)
294                             new_tr.childNodes[i]._delete = true;
295                     }
296
297                     if ((lastElm == null || td != lastElm) && sd.rowspan > 1)
298                         td.rowSpan = sd.rowspan + 1;
299
300                     lastElm = td;
301                 }
302
303                 deleteMarked(tableElm);
304             }
305         }
306
307         function prevElm(node, name) {
308             while ((node = node.previousSibling) != null) {
309                 if (node.nodeName == name)
310                     return node;
311             }
312
313             return null;
314         }
315
316         function nextElm(node, names) {
317             var namesAr = names.split(',');
318
319             while ((node = node.nextSibling) != null) {
320                 for (var i=0; i<namesAr.length; i++) {
321                     if (node.nodeName.toLowerCase() == namesAr[i].toLowerCase() )
322                         return node;
323                 }
324             }
325
326             return null;
327         }
328
329         function deleteMarked(tbl) {
330             if (tbl.rows == 0)
331                 return;
332
333             var tr = tbl.rows[0];
334             do {
335                 var next = nextElm(tr, "TR");
336
337                 // Delete row
338                 if (tr._delete) {
339                     tr.parentNode.removeChild(tr);
340                     continue;
341                 }
342
343                 // Delete cells
344                 var td = tr.cells[0];
345                 if (td.cells > 1) {
346                     do {
347                         var nexttd = nextElm(td, "TD,TH");
348
349                         if (td._delete)
350                             td.parentNode.removeChild(td);
351                     } while ((td = nexttd) != null);
352                 }
353             } while ((tr = next) != null);
354         }
355
356         function addRows(td_elm, tr_elm, rowspan) {
357             // Add rows
358             td_elm.rowSpan = 1;
359             var trNext = nextElm(tr_elm, "TR");
360             for (var i=1; i<rowspan && trNext; i++) {
361                 var newTD = doc.createElement("td");
362                 newTD.innerHTML = "&nbsp;";
363
364                 if (tinyMCE.isMSIE)
365                     trNext.insertBefore(newTD, trNext.cells(td_elm.cellIndex));
366                 else
367                     trNext.insertBefore(newTD, trNext.cells[td_elm.cellIndex]);
368
369                 trNext = nextElm(trNext, "TR");
370             }
371         }
372
373         function copyRow(doc, table, tr) {
374             var grid = getTableGrid(table);
375             var newTR = tr.cloneNode(false);
376             var cpos = getCellPos(grid, tr.cells[0]);
377             var lastCell = null;
378             var tableBorder = tinyMCE.getAttrib(table, "border");
379             var tdElm = null;
380
381             for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
382                 var newTD = null;
383
384                 if (lastCell != tdElm) {
385                     for (var i=0; i<tr.cells.length; i++) {
386                         if (tdElm == tr.cells[i]) {
387                             newTD = tdElm.cloneNode(true);
388                             break;
389                         }
390                     }
391                 }
392
393                 if (newTD == null) {
394                     newTD = doc.createElement("td");
395                     newTD.innerHTML = "&nbsp;";
396                 }
397
398                 // Reset col/row span
399                 newTD.colSpan = 1;
400                 newTD.rowSpan = 1;
401
402                 newTR.appendChild(newTD);
403
404                 lastCell = tdElm;
405             }
406
407             return newTR;
408         }
409
410         // ---- Commands -----
411
412         // Handle commands
413         switch (command) {
414             case "mceTableRowProps":
415                 if (trElm == null)
416                     return true;
417
418                 if (user_interface) {
419                     // Setup template
420                     var template = new Array();
421
422                     template['file'] = '../../plugins/table/row.htm';
423                     template['width'] = 380;
424                     template['height'] = 295;
425
426                     // Language specific width and height addons
427                     template['width'] += tinyMCE.getLang('lang_table_rowprops_delta_width', 0);
428                     template['height'] += tinyMCE.getLang('lang_table_rowprops_delta_height', 0);
429
430                     // Open window
431                     tinyMCE.openWindow(template, {editor_id : inst.editorId, inline : "yes"});
432                 }
433
434                 return true;
435
436             case "mceTableCellProps":
437                 if (tdElm == null)
438                     return true;
439
440                 if (user_interface) {
441                     // Setup template
442                     var template = new Array();
443
444                     template['file'] = '../../plugins/table/cell.htm';
445                     template['width'] = 380;
446                     template['height'] = 295;
447
448                     // Language specific width and height addons
449                     template['width'] += tinyMCE.getLang('lang_table_cellprops_delta_width', 0);
450                     template['height'] += tinyMCE.getLang('lang_table_cellprops_delta_height', 0);
451
452                     // Open window
453                     tinyMCE.openWindow(template, {editor_id : inst.editorId, inline : "yes"});
454                 }
455
456                 return true;
457
458             case "mceInsertTable":
459                 if (user_interface) {
460                     // Setup template
461                     var template = new Array();
462
463                     template['file'] = '../../plugins/table/table.htm';
464                     template['width'] = 380;
465                     template['height'] = 295;
466
467                     // Language specific width and height addons
468                     template['width'] += tinyMCE.getLang('lang_table_table_delta_width', 0);
469                     template['height'] += tinyMCE.getLang('lang_table_table_delta_height', 0);
470
471                     // Open window
472                     tinyMCE.openWindow(template, {editor_id : inst.editorId, inline : "yes", action : value});
473                 }
474
475                 return true;
476
477             case "mceTableDelete":
478                 var table = tinyMCE.getParentElement(inst.getFocusElement(), "table");
479                 if (table) {
480                     table.parentNode.removeChild(table);
481                     inst.repaint();
482                 }
483                 return true;
484
485             case "mceTableSplitCells":
486             case "mceTableMergeCells":
487             case "mceTableInsertRowBefore":
488             case "mceTableInsertRowAfter":
489             case "mceTableDeleteRow":
490             case "mceTableInsertColBefore":
491             case "mceTableInsertColAfter":
492             case "mceTableDeleteCol":
493             case "mceTableCutRow":
494             case "mceTableCopyRow":
495             case "mceTablePasteRowBefore":
496             case "mceTablePasteRowAfter":
497                 // No table just return (invalid command)
498                 if (!tableElm)
499                     return true;
500
501                 // Table has a tbody use that reference
502                 // Changed logic by ApTest 2005.07.12 (www.aptest.com)
503                 // Now lookk at the focused element and take its parentNode.  That will be a tbody or a table.
f0ea59 504                 if (trElm && tableElm != trElm.parentNode)
a0109c 505                     tableElm = trElm.parentNode;
S 506
507                 if (tableElm && trElm) {
508                     switch (command) {
509                         case "mceTableCutRow":
510                             if (!trElm || !tdElm)
511                                 return true;
512
513                             inst.tableRowClipboard = copyRow(doc, tableElm, trElm);
514                             inst.execCommand("mceTableDeleteRow");
515                             break;
516
517                         case "mceTableCopyRow":
518                             if (!trElm || !tdElm)
519                                 return true;
520
521                             inst.tableRowClipboard = copyRow(doc, tableElm, trElm);
522                             break;
523
524                         case "mceTablePasteRowBefore":
525                             if (!trElm || !tdElm)
526                                 return true;
527
528                             var newTR = inst.tableRowClipboard.cloneNode(true);
529
530                             var prevTR = prevElm(trElm, "TR");
531                             if (prevTR != null)
532                                 trimRow(tableElm, prevTR, prevTR.cells[0], newTR);
533
534                             trElm.parentNode.insertBefore(newTR, trElm);
535                             break;
536
537                         case "mceTablePasteRowAfter":
538                             if (!trElm || !tdElm)
539                                 return true;
540                             
541                             var nextTR = nextElm(trElm, "TR");
542                             var newTR = inst.tableRowClipboard.cloneNode(true);
543
544                             trimRow(tableElm, trElm, tdElm, newTR);
545
546                             if (nextTR == null)
547                                 trElm.parentNode.appendChild(newTR);
548                             else
549                                 nextTR.parentNode.insertBefore(newTR, nextTR);
550
551                             break;
552
f0ea59 553                         case "mceTableInsertRowBefore":
S 554                             if (!trElm || !tdElm)
555                                 return true;
556
557                             var grid = getTableGrid(tableElm);
558                             var cpos = getCellPos(grid, tdElm);
559                             var newTR = doc.createElement("tr");
560                             var lastTDElm = null;
561
562                             cpos.rowindex--;
563                             if (cpos.rowindex < 0)
564                                 cpos.rowindex = 0;
565
566                             // Create cells
567                             for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
568                                 if (tdElm != lastTDElm) {
569                                     var sd = getColRowSpan(tdElm);
570
571                                     if (sd['rowspan'] == 1) {
572                                         var newTD = doc.createElement("td");
573
574                                         newTD.innerHTML = "&nbsp;";
575                                         newTD.colSpan = tdElm.colSpan;
576
577                                         newTR.appendChild(newTD);
578                                     } else
579                                         tdElm.rowSpan = sd['rowspan'] + 1;
580
581                                     lastTDElm = tdElm;
582                                 }
583                             }
584
585                             trElm.parentNode.insertBefore(newTR, trElm);
586
587                             grid = getTableGrid(tableElm);
588                             inst.selection.selectNode(getCell(grid, cpos.rowindex + 1, cpos.cellindex), tinyMCE.isGecko, true); // Only collape on gecko
589                         break;
590
a0109c 591                         case "mceTableInsertRowAfter":
S 592                             if (!trElm || !tdElm)
593                                 return true;
594
595                             var grid = getTableGrid(tableElm);
596                             var cpos = getCellPos(grid, tdElm);
597                             var newTR = doc.createElement("tr");
598                             var lastTDElm = null;
599
600                             // Create cells
601                             for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
602                                 if (tdElm != lastTDElm) {
603                                     var sd = getColRowSpan(tdElm);
604
605                                     if (sd['rowspan'] == 1) {
606                                         var newTD = doc.createElement("td");
607
608                                         newTD.innerHTML = "&nbsp;";
609                                         newTD.colSpan = tdElm.colSpan;
610
611                                         newTR.appendChild(newTD);
612                                     } else
613                                         tdElm.rowSpan = sd['rowspan'] + 1;
614
615                                     lastTDElm = tdElm;
616                                 }
617                             }
618
619                             if (newTR.hasChildNodes()) {
620                                 var nextTR = nextElm(trElm, "TR");
621                                 if (nextTR)
622                                     nextTR.parentNode.insertBefore(newTR, nextTR);
623                                 else
624                                     tableElm.appendChild(newTR);
625                             }
f0ea59 626
S 627                             grid = getTableGrid(tableElm);
628                             inst.selection.selectNode(getCell(grid, cpos.rowindex, cpos.cellindex), tinyMCE.isGecko, true); // Only collape on gecko
a0109c 629                         break;
S 630
631                         case "mceTableDeleteRow":
632                             if (!trElm || !tdElm)
633                                 return true;
f0ea59 634
a0109c 635                             var grid = getTableGrid(tableElm);
S 636                             var cpos = getCellPos(grid, tdElm);
637
638                             // Only one row, remove whole table
639                             if (grid.length == 1) {
f0ea59 640                                 tableElm = tinyMCE.getParentElement(tableElm, "table"); // Look for table instead of tbody
a0109c 641                                 tableElm.parentNode.removeChild(tableElm);
S 642                                 return true;
643                             }
644
645                             // Move down row spanned cells
646                             var cells = trElm.cells;
647                             var nextTR = nextElm(trElm, "TR");
648                             for (var x=0; x<cells.length; x++) {
649                                 if (cells[x].rowSpan > 1) {
650                                     var newTD = cells[x].cloneNode(true);
651                                     var sd = getColRowSpan(cells[x]);
652
653                                     newTD.rowSpan = sd.rowspan - 1;
654
655                                     var nextTD = nextTR.cells[x];
656
657                                     if (nextTD == null)
658                                         nextTR.appendChild(newTD);
659                                     else
660                                         nextTR.insertBefore(newTD, nextTD);
661                                 }
662                             }
663
664                             // Delete cells
665                             var lastTDElm = null;
666                             for (var x=0; tdElm = getCell(grid, cpos.rowindex, x); x++) {
667                                 if (tdElm != lastTDElm) {
668                                     var sd = getColRowSpan(tdElm);
669
670                                     if (sd.rowspan > 1) {
671                                         tdElm.rowSpan = sd.rowspan - 1;
672                                     } else {
673                                         trElm = tdElm.parentNode;
674
675                                         if (trElm.parentNode)
676                                             trElm._delete = true;
677                                     }
678
679                                     lastTDElm = tdElm;
680                                 }
681                             }
682
683                             deleteMarked(tableElm);
684
685                             cpos.rowindex--;
686                             if (cpos.rowindex < 0)
687                                 cpos.rowindex = 0;
688
f0ea59 689                             // Recalculate grid and select
S 690                             grid = getTableGrid(tableElm);
691                             inst.selection.selectNode(getCell(grid, cpos.rowindex, 0), tinyMCE.isGecko, true); // Only collape on gecko
a0109c 692                         break;
S 693
694                         case "mceTableInsertColBefore":
695                             if (!trElm || !tdElm)
696                                 return true;
697
698                             var grid = getTableGrid(tableElm);
699                             var cpos = getCellPos(grid, tdElm);
700                             var lastTDElm = null;
701
702                             for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
703                                 if (tdElm != lastTDElm) {
704                                     var sd = getColRowSpan(tdElm);
705
706                                     if (sd['colspan'] == 1) {
707                                         var newTD = doc.createElement(tdElm.nodeName);
708
709                                         newTD.innerHTML = "&nbsp;";
710                                         newTD.rowSpan = tdElm.rowSpan;
711
712                                         tdElm.parentNode.insertBefore(newTD, tdElm);
713                                     } else
714                                         tdElm.colSpan++;
715
716                                     lastTDElm = tdElm;
717                                 }
718                             }
f0ea59 719
S 720                             grid = getTableGrid(tableElm);
721                             inst.selection.selectNode(getCell(grid, cpos.rowindex, cpos.cellindex + 1), tinyMCE.isGecko, true); // Only collape on gecko
a0109c 722                         break;
S 723
724                         case "mceTableInsertColAfter":
725                             if (!trElm || !tdElm)
726                                 return true;
727
728                             var grid = getTableGrid(tableElm);
729                             var cpos = getCellPos(grid, tdElm);
730                             var lastTDElm = null;
731
732                             for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
733                                 if (tdElm != lastTDElm) {
734                                     var sd = getColRowSpan(tdElm);
735
736                                     if (sd['colspan'] == 1) {
737                                         var newTD = doc.createElement(tdElm.nodeName);
738
739                                         newTD.innerHTML = "&nbsp;";
740                                         newTD.rowSpan = tdElm.rowSpan;
741
742                                         var nextTD = nextElm(tdElm, "TD,TH");
743                                         if (nextTD == null)
744                                             tdElm.parentNode.appendChild(newTD);
745                                         else
746                                             nextTD.parentNode.insertBefore(newTD, nextTD);
747                                     } else
748                                         tdElm.colSpan++;
749
750                                     lastTDElm = tdElm;
751                                 }
752                             }
f0ea59 753
S 754                             grid = getTableGrid(tableElm);
755                             inst.selection.selectNode(getCell(grid, cpos.rowindex, cpos.cellindex), tinyMCE.isGecko, true); // Only collape on gecko
a0109c 756                         break;
S 757
758                         case "mceTableDeleteCol":
759                             if (!trElm || !tdElm)
760                                 return true;
761
762                             var grid = getTableGrid(tableElm);
763                             var cpos = getCellPos(grid, tdElm);
764                             var lastTDElm = null;
765
766                             // Only one col, remove whole table
767                             if (grid.length > 1 && grid[0].length <= 1) {
f0ea59 768                                 tableElm = tinyMCE.getParentElement(tableElm, "table"); // Look for table instead of tbody
a0109c 769                                 tableElm.parentNode.removeChild(tableElm);
S 770                                 return true;
771                             }
772
773                             // Delete cells
774                             for (var y=0; tdElm = getCell(grid, y, cpos.cellindex); y++) {
775                                 if (tdElm != lastTDElm) {
776                                     var sd = getColRowSpan(tdElm);
777
778                                     if (sd['colspan'] > 1)
779                                         tdElm.colSpan = sd['colspan'] - 1;
780                                     else {
781                                         if (tdElm.parentNode)
782                                             tdElm.parentNode.removeChild(tdElm);
783                                     }
784
785                                     lastTDElm = tdElm;
786                                 }
787                             }
788
789                             cpos.cellindex--;
790                             if (cpos.cellindex < 0)
791                                 cpos.cellindex = 0;
792
f0ea59 793                             // Recalculate grid and select
S 794                             grid = getTableGrid(tableElm);
795                             inst.selection.selectNode(getCell(grid, cpos.rowindex, 0), tinyMCE.isGecko, true); // Only collape on gecko
a0109c 796                         break;
S 797
798                     case "mceTableSplitCells":
799                         if (!trElm || !tdElm)
800                             return true;
801
802                         var spandata = getColRowSpan(tdElm);
803
804                         var colspan = spandata["colspan"];
805                         var rowspan = spandata["rowspan"];
806
807                         // Needs splitting
808                         if (colspan > 1 || rowspan > 1) {
809                             // Generate cols
810                             tdElm.colSpan = 1;
811                             for (var i=1; i<colspan; i++) {
812                                 var newTD = doc.createElement("td");
813
814                                 newTD.innerHTML = "&nbsp;";
815
816                                 trElm.insertBefore(newTD, nextElm(tdElm, "TD,TH"));
817
818                                 if (rowspan > 1)
819                                     addRows(newTD, trElm, rowspan);
820                             }
821
822                             addRows(tdElm, trElm, rowspan);
823                         }
824
825                         // Apply visual aids
826                         tableElm = tinyMCE.getParentElement(inst.getFocusElement(), "table");
827                         break;
828
829                     case "mceTableMergeCells":
830                         var rows = new Array();
831                         var sel = inst.getSel();
832                         var grid = getTableGrid(tableElm);
833
834                         if (tinyMCE.isMSIE || sel.rangeCount == 1) {
835                             if (user_interface) {
836                                 // Setup template
837                                 var template = new Array();
838                                 var sp = getColRowSpan(tdElm);
839
840                                 template['file'] = '../../plugins/table/merge_cells.htm';
841                                 template['width'] = 250;
842                                 template['height'] = 105 + (tinyMCE.isNS7 ? 25 : 0);
843
844                                 // Language specific width and height addons
845                                 template['width'] += tinyMCE.getLang('lang_table_merge_cells_delta_width', 0);
846                                 template['height'] += tinyMCE.getLang('lang_table_merge_cells_delta_height', 0);
847
848                                 // Open window
849                                 tinyMCE.openWindow(template, {editor_id : inst.editorId, inline : "yes", action : "update", numcols : sp.colspan, numrows : sp.rowspan});
850
851                                 return true;
852                             } else {
853                                 var numRows = parseInt(value['numrows']);
854                                 var numCols = parseInt(value['numcols']);
855                                 var cpos = getCellPos(grid, tdElm);
856
857                                 if (("" + numRows) == "NaN")
858                                     numRows = 1;
859
860                                 if (("" + numCols) == "NaN")
861                                     numCols = 1;
862
863                                 // Get rows and cells
864                                 var tRows = tableElm.rows;
865                                 for (var y=cpos.rowindex; y<grid.length; y++) {
866                                     var rowCells = new Array();
867
868                                     for (var x=cpos.cellindex; x<grid[y].length; x++) {
869                                         var td = getCell(grid, y, x);
870
871                                         if (td && !inArray(rows, td) && !inArray(rowCells, td)) {
872                                             var cp = getCellPos(grid, td);
873
874                                             // Within range
875                                             if (cp.cellindex < cpos.cellindex+numCols && cp.rowindex < cpos.rowindex+numRows)
876                                                 rowCells[rowCells.length] = td;
877                                         }
878                                     }
879
880                                     if (rowCells.length > 0)
881                                         rows[rows.length] = rowCells;
882                                 }
883
884                                 //return true;
885                             }
886                         } else {
887                             var cells = new Array();
888                             var sel = inst.getSel();
889                             var lastTR = null;
890                             var curRow = null;
891                             var x1 = -1, y1 = -1, x2, y2;
892
893                             // Only one cell selected, whats the point?
894                             if (sel.rangeCount < 2)
895                                 return true;
896
897                             // Get all selected cells
898                             for (var i=0; i<sel.rangeCount; i++) {
899                                 var rng = sel.getRangeAt(i);
900                                 var tdElm = rng.startContainer.childNodes[rng.startOffset];
901
902                                 if (!tdElm)
903                                     break;
904
905                                 if (tdElm.nodeName == "TD")
906                                     cells[cells.length] = tdElm;
907                             }
908
909                             // Get rows and cells
910                             var tRows = tableElm.rows;
911                             for (var y=0; y<tRows.length; y++) {
912                                 var rowCells = new Array();
913
914                                 for (var x=0; x<tRows[y].cells.length; x++) {
915                                     var td = tRows[y].cells[x];
916
917                                     for (var i=0; i<cells.length; i++) {
918                                         if (td == cells[i]) {
919                                             rowCells[rowCells.length] = td;
920                                         }
921                                     }
922                                 }
923
924                                 if (rowCells.length > 0)
925                                     rows[rows.length] = rowCells;
926                             }
927
928                             // Find selected cells in grid and box
929                             var curRow = new Array();
930                             var lastTR = null;
931                             for (var y=0; y<grid.length; y++) {
932                                 for (var x=0; x<grid[y].length; x++) {
933                                     grid[y][x]._selected = false;
934
935                                     for (var i=0; i<cells.length; i++) {
936                                         if (grid[y][x] == cells[i]) {
937                                             // Get start pos
938                                             if (x1 == -1) {
939                                                 x1 = x;
940                                                 y1 = y;
941                                             }
942
943                                             // Get end pos
944                                             x2 = x;
945                                             y2 = y;
946
947                                             grid[y][x]._selected = true;
948                                         }
949                                     }
950                                 }
951                             }
952
953                             // Is there gaps, if so deny
954                             for (var y=y1; y<=y2; y++) {
955                                 for (var x=x1; x<=x2; x++) {
956                                     if (!grid[y][x]._selected) {
957                                         alert("Invalid selection for merge.");
958                                         return true;
959                                     }
960                                 }
961                             }
962                         }
963
964                         // Validate selection and get total rowspan and colspan
965                         var rowSpan = 1, colSpan = 1;
966
967                         // Validate horizontal and get total colspan
968                         var lastRowSpan = -1;
969                         for (var y=0; y<rows.length; y++) {
970                             var rowColSpan = 0;
971
972                             for (var x=0; x<rows[y].length; x++) {
973                                 var sd = getColRowSpan(rows[y][x]);
974
975                                 rowColSpan += sd['colspan'];
976
977                                 if (lastRowSpan != -1 && sd['rowspan'] != lastRowSpan) {
978                                     alert("Invalid selection for merge.");
979                                     return true;
980                                 }
981
982                                 lastRowSpan = sd['rowspan'];
983                             }
984
985                             if (rowColSpan > colSpan)
986                                 colSpan = rowColSpan;
987
988                             lastRowSpan = -1;
989                         }
990
991                         // Validate vertical and get total rowspan
992                         var lastColSpan = -1;
993                         for (var x=0; x<rows[0].length; x++) {
994                             var colRowSpan = 0;
995
996                             for (var y=0; y<rows.length; y++) {
997                                 var sd = getColRowSpan(rows[y][x]);
998
999                                 colRowSpan += sd['rowspan'];
1000
1001                                 if (lastColSpan != -1 && sd['colspan'] != lastColSpan) {
1002                                     alert("Invalid selection for merge.");
1003                                     return true;
1004                                 }
1005
1006                                 lastColSpan = sd['colspan'];
1007                             }
1008
1009                             if (colRowSpan > rowSpan)
1010                                 rowSpan = colRowSpan;
1011
1012                             lastColSpan = -1;
1013                         }
1014
1015                         // Setup td
1016                         tdElm = rows[0][0];
1017                         tdElm.rowSpan = rowSpan;
1018                         tdElm.colSpan = colSpan;
1019
1020                         // Merge cells
1021                         for (var y=0; y<rows.length; y++) {
1022                             for (var x=0; x<rows[y].length; x++) {
1023                                 var html = rows[y][x].innerHTML;
1024                                 var chk = tinyMCE.regexpReplace(html, "[ \t\r\n]", "");
1025
1026                                 if (chk != "<br/>" && chk != "<br>" && chk != "&nbsp;" && (x+y > 0))
1027                                     tdElm.innerHTML += html;
1028
1029                                 // Not current cell
1030                                 if (rows[y][x] != tdElm && !rows[y][x]._deleted) {
1031                                     var cpos = getCellPos(grid, rows[y][x]);
1032                                     var tr = rows[y][x].parentNode;
1033
1034                                     tr.removeChild(rows[y][x]);
1035                                     rows[y][x]._deleted = true;
1036
1037                                     // Empty TR, remove it
1038                                     if (!tr.hasChildNodes()) {
1039                                         tr.parentNode.removeChild(tr);
1040
1041                                         var lastCell = null;
1042                                         for (var x=0; cellElm = getCell(grid, cpos.rowindex, x); x++) {
1043                                             if (cellElm != lastCell && cellElm.rowSpan > 1)
1044                                                 cellElm.rowSpan--;
1045
1046                                             lastCell = cellElm;
1047                                         }
1048
1049                                         if (tdElm.rowSpan > 1)
1050                                             tdElm.rowSpan--;
1051                                     }
1052                                 }
1053                             }
1054                         }
1055
1056                         break;
1057                     }
1058
1059                     tableElm = tinyMCE.getParentElement(inst.getFocusElement(), "table");
1060                     tinyMCE.handleVisualAid(tableElm, true, tinyMCE.settings['visual'], tinyMCE.selectedInstance);
1061                     tinyMCE.triggerNodeChange();
1062                     inst.repaint();
1063                 }
1064
1065             return true;
1066         }
1067
1068         // Pass to next handler in chain
1069         return false;
1070     }
1071 };
1072
1073 tinyMCE.addPlugin("table", TinyMCE_TablePlugin);