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