]> git.mxchange.org Git - friendica.git/blob - library/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin_src.js
updated tinymce to 3.5.11
[friendica.git] / library / tinymce / jscripts / tiny_mce / plugins / table / editor_plugin_src.js
1 /**
2  * editor_plugin_src.js
3  *
4  * Copyright 2009, Moxiecode Systems AB
5  * Released under LGPL License.
6  *
7  * License: http://tinymce.moxiecode.com/license
8  * Contributing: http://tinymce.moxiecode.com/contributing
9  */
10
11 (function(tinymce) {
12         var each = tinymce.each;
13
14         // Checks if the selection/caret is at the start of the specified block element
15         function isAtStart(rng, par) {
16                 var doc = par.ownerDocument, rng2 = doc.createRange(), elm;
17
18                 rng2.setStartBefore(par);
19                 rng2.setEnd(rng.endContainer, rng.endOffset);
20
21                 elm = doc.createElement('body');
22                 elm.appendChild(rng2.cloneContents());
23
24                 // Check for text characters of other elements that should be treated as content
25                 return elm.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi, '-').replace(/<[^>]+>/g, '').length == 0;
26         };
27
28         function getSpanVal(td, name) {
29                 return parseInt(td.getAttribute(name) || 1);
30         }
31
32         /**
33          * Table Grid class.
34          */
35         function TableGrid(table, dom, selection) {
36                 var grid, startPos, endPos, selectedCell;
37
38                 buildGrid();
39                 selectedCell = dom.getParent(selection.getStart(), 'th,td');
40                 if (selectedCell) {
41                         startPos = getPos(selectedCell);
42                         endPos = findEndPos();
43                         selectedCell = getCell(startPos.x, startPos.y);
44                 }
45
46                 function cloneNode(node, children) {
47                         node = node.cloneNode(children);
48                         node.removeAttribute('id');
49
50                         return node;
51                 }
52
53                 function buildGrid() {
54                         var startY = 0;
55
56                         grid = [];
57
58                         each(['thead', 'tbody', 'tfoot'], function(part) {
59                                 var rows = dom.select('> ' + part + ' tr', table);
60
61                                 each(rows, function(tr, y) {
62                                         y += startY;
63
64                                         each(dom.select('> td, > th', tr), function(td, x) {
65                                                 var x2, y2, rowspan, colspan;
66
67                                                 // Skip over existing cells produced by rowspan
68                                                 if (grid[y]) {
69                                                         while (grid[y][x])
70                                                                 x++;
71                                                 }
72
73                                                 // Get col/rowspan from cell
74                                                 rowspan = getSpanVal(td, 'rowspan');
75                                                 colspan = getSpanVal(td, 'colspan');
76
77                                                 // Fill out rowspan/colspan right and down
78                                                 for (y2 = y; y2 < y + rowspan; y2++) {
79                                                         if (!grid[y2])
80                                                                 grid[y2] = [];
81
82                                                         for (x2 = x; x2 < x + colspan; x2++) {
83                                                                 grid[y2][x2] = {
84                                                                         part : part,
85                                                                         real : y2 == y && x2 == x,
86                                                                         elm : td,
87                                                                         rowspan : rowspan,
88                                                                         colspan : colspan
89                                                                 };
90                                                         }
91                                                 }
92                                         });
93                                 });
94
95                                 startY += rows.length;
96                         });
97                 };
98
99                 function getCell(x, y) {
100                         var row;
101
102                         row = grid[y];
103                         if (row)
104                                 return row[x];
105                 };
106
107                 function setSpanVal(td, name, val) {
108                         if (td) {
109                                 val = parseInt(val);
110
111                                 if (val === 1)
112                                         td.removeAttribute(name, 1);
113                                 else
114                                         td.setAttribute(name, val, 1);
115                         }
116                 }
117
118                 function isCellSelected(cell) {
119                         return cell && (dom.hasClass(cell.elm, 'mceSelected') || cell == selectedCell);
120                 };
121
122                 function getSelectedRows() {
123                         var rows = [];
124
125                         each(table.rows, function(row) {
126                                 each(row.cells, function(cell) {
127                                         if (dom.hasClass(cell, 'mceSelected') || cell == selectedCell.elm) {
128                                                 rows.push(row);
129                                                 return false;
130                                         }
131                                 });
132                         });
133
134                         return rows;
135                 };
136
137                 function deleteTable() {
138                         var rng = dom.createRng();
139
140                         rng.setStartAfter(table);
141                         rng.setEndAfter(table);
142
143                         selection.setRng(rng);
144
145                         dom.remove(table);
146                 };
147
148                 function cloneCell(cell) {
149                         var formatNode;
150
151                         // Clone formats
152                         tinymce.walk(cell, function(node) {
153                                 var curNode;
154
155                                 if (node.nodeType == 3) {
156                                         each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {
157                                                 node = cloneNode(node, false);
158
159                                                 if (!formatNode)
160                                                         formatNode = curNode = node;
161                                                 else if (curNode)
162                                                         curNode.appendChild(node);
163
164                                                 curNode = node;
165                                         });
166
167                                         // Add something to the inner node
168                                         if (curNode)
169                                                 curNode.innerHTML = tinymce.isIE && !tinymce.isIE11 ? '&nbsp;' : '<br data-mce-bogus="1" />';
170
171                                         return false;
172                                 }
173                         }, 'childNodes');
174
175                         cell = cloneNode(cell, false);
176                         setSpanVal(cell, 'rowSpan', 1);
177                         setSpanVal(cell, 'colSpan', 1);
178
179                         if (formatNode) {
180                                 cell.appendChild(formatNode);
181                         } else {
182                                 if (!tinymce.isIE || tinymce.isIE11)
183                                         cell.innerHTML = '<br data-mce-bogus="1" />';
184                         }
185
186                         return cell;
187                 };
188
189                 function cleanup() {
190                         var rng = dom.createRng();
191
192                         // Empty rows
193                         each(dom.select('tr', table), function(tr) {
194                                 if (tr.cells.length == 0)
195                                         dom.remove(tr);
196                         });
197
198                         // Empty table
199                         if (dom.select('tr', table).length == 0) {
200                                 rng.setStartAfter(table);
201                                 rng.setEndAfter(table);
202                                 selection.setRng(rng);
203                                 dom.remove(table);
204                                 return;
205                         }
206
207                         // Empty header/body/footer
208                         each(dom.select('thead,tbody,tfoot', table), function(part) {
209                                 if (part.rows.length == 0)
210                                         dom.remove(part);
211                         });
212
213                         // Restore selection to start position if it still exists
214                         buildGrid();
215
216                         // Restore the selection to the closest table position
217                         row = grid[Math.min(grid.length - 1, startPos.y)];
218                         if (row) {
219                                 selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true);
220                                 selection.collapse(true);
221                         }
222                 };
223
224                 function fillLeftDown(x, y, rows, cols) {
225                         var tr, x2, r, c, cell;
226
227                         tr = grid[y][x].elm.parentNode;
228                         for (r = 1; r <= rows; r++) {
229                                 tr = dom.getNext(tr, 'tr');
230
231                                 if (tr) {
232                                         // Loop left to find real cell
233                                         for (x2 = x; x2 >= 0; x2--) {
234                                                 cell = grid[y + r][x2].elm;
235
236                                                 if (cell.parentNode == tr) {
237                                                         // Append clones after
238                                                         for (c = 1; c <= cols; c++)
239                                                                 dom.insertAfter(cloneCell(cell), cell);
240
241                                                         break;
242                                                 }
243                                         }
244
245                                         if (x2 == -1) {
246                                                 // Insert nodes before first cell
247                                                 for (c = 1; c <= cols; c++)
248                                                         tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]);
249                                         }
250                                 }
251                         }
252                 };
253
254                 function split() {
255                         each(grid, function(row, y) {
256                                 each(row, function(cell, x) {
257                                         var colSpan, rowSpan, newCell, i;
258
259                                         if (isCellSelected(cell)) {
260                                                 cell = cell.elm;
261                                                 colSpan = getSpanVal(cell, 'colspan');
262                                                 rowSpan = getSpanVal(cell, 'rowspan');
263
264                                                 if (colSpan > 1 || rowSpan > 1) {
265                                                         setSpanVal(cell, 'rowSpan', 1);
266                                                         setSpanVal(cell, 'colSpan', 1);
267
268                                                         // Insert cells right
269                                                         for (i = 0; i < colSpan - 1; i++)
270                                                                 dom.insertAfter(cloneCell(cell), cell);
271
272                                                         fillLeftDown(x, y, rowSpan - 1, colSpan);
273                                                 }
274                                         }
275                                 });
276                         });
277                 };
278
279                 function merge(cell, cols, rows) {
280                         var startX, startY, endX, endY, x, y, startCell, endCell, cell, children, count;
281
282                         // Use specified cell and cols/rows
283                         if (cell) {
284                                 pos = getPos(cell);
285                                 startX = pos.x;
286                                 startY = pos.y;
287                                 endX = startX + (cols - 1);
288                                 endY = startY + (rows - 1);
289                         } else {
290                                 startPos = endPos = null;
291
292                                 // Calculate start/end pos by checking for selected cells in grid works better with context menu
293                                 each(grid, function(row, y) {
294                                         each(row, function(cell, x) {
295                                                 if (isCellSelected(cell)) {
296                                                         if (!startPos) {
297                                                                 startPos = {x: x, y: y};
298                                                         }
299
300                                                         endPos = {x: x, y: y};
301                                                 }
302                                         });
303                                 });
304
305                                 // Use selection
306                                 startX = startPos.x;
307                                 startY = startPos.y;
308                                 endX = endPos.x;
309                                 endY = endPos.y;
310                         }
311
312                         // Find start/end cells
313                         startCell = getCell(startX, startY);
314                         endCell = getCell(endX, endY);
315
316                         // Check if the cells exists and if they are of the same part for example tbody = tbody
317                         if (startCell && endCell && startCell.part == endCell.part) {
318                                 // Split and rebuild grid
319                                 split();
320                                 buildGrid();
321
322                                 // Set row/col span to start cell
323                                 startCell = getCell(startX, startY).elm;
324                                 setSpanVal(startCell, 'colSpan', (endX - startX) + 1);
325                                 setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);
326
327                                 // Remove other cells and add it's contents to the start cell
328                                 for (y = startY; y <= endY; y++) {
329                                         for (x = startX; x <= endX; x++) {
330                                                 if (!grid[y] || !grid[y][x])
331                                                         continue;
332
333                                                 cell = grid[y][x].elm;
334
335                                                 if (cell != startCell) {
336                                                         // Move children to startCell
337                                                         children = tinymce.grep(cell.childNodes);
338                                                         each(children, function(node) {
339                                                                 startCell.appendChild(node);
340                                                         });
341
342                                                         // Remove bogus nodes if there is children in the target cell
343                                                         if (children.length) {
344                                                                 children = tinymce.grep(startCell.childNodes);
345                                                                 count = 0;
346                                                                 each(children, function(node) {
347                                                                         if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1)
348                                                                                 startCell.removeChild(node);
349                                                                 });
350                                                         }
351                                                         
352                                                         // Remove cell
353                                                         dom.remove(cell);
354                                                 }
355                                         }
356                                 }
357
358                                 // Remove empty rows etc and restore caret location
359                                 cleanup();
360                         }
361                 };
362
363                 function insertRow(before) {
364                         var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;
365
366                         // Find first/last row
367                         each(grid, function(row, y) {
368                                 each(row, function(cell, x) {
369                                         if (isCellSelected(cell)) {
370                                                 cell = cell.elm;
371                                                 rowElm = cell.parentNode;
372                                                 newRow = cloneNode(rowElm, false);
373                                                 posY = y;
374
375                                                 if (before)
376                                                         return false;
377                                         }
378                                 });
379
380                                 if (before)
381                                         return !posY;
382                         });
383
384                         for (x = 0; x < grid[0].length; x++) {
385                                 // Cell not found could be because of an invalid table structure
386                                 if (!grid[posY][x])
387                                         continue;
388
389                                 cell = grid[posY][x].elm;
390
391                                 if (cell != lastCell) {
392                                         if (!before) {
393                                                 rowSpan = getSpanVal(cell, 'rowspan');
394                                                 if (rowSpan > 1) {
395                                                         setSpanVal(cell, 'rowSpan', rowSpan + 1);
396                                                         continue;
397                                                 }
398                                         } else {
399                                                 // Check if cell above can be expanded
400                                                 if (posY > 0 && grid[posY - 1][x]) {
401                                                         otherCell = grid[posY - 1][x].elm;
402                                                         rowSpan = getSpanVal(otherCell, 'rowSpan');
403                                                         if (rowSpan > 1) {
404                                                                 setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
405                                                                 continue;
406                                                         }
407                                                 }
408                                         }
409
410                                         // Insert new cell into new row
411                                         newCell = cloneCell(cell);
412                                         setSpanVal(newCell, 'colSpan', cell.colSpan);
413
414                                         newRow.appendChild(newCell);
415
416                                         lastCell = cell;
417                                 }
418                         }
419
420                         if (newRow.hasChildNodes()) {
421                                 if (!before)
422                                         dom.insertAfter(newRow, rowElm);
423                                 else
424                                         rowElm.parentNode.insertBefore(newRow, rowElm);
425                         }
426                 };
427
428                 function insertCol(before) {
429                         var posX, lastCell;
430
431                         // Find first/last column
432                         each(grid, function(row, y) {
433                                 each(row, function(cell, x) {
434                                         if (isCellSelected(cell)) {
435                                                 posX = x;
436
437                                                 if (before)
438                                                         return false;
439                                         }
440                                 });
441
442                                 if (before)
443                                         return !posX;
444                         });
445
446                         each(grid, function(row, y) {
447                                 var cell, rowSpan, colSpan;
448
449                                 if (!row[posX])
450                                         return;
451
452                                 cell = row[posX].elm;
453                                 if (cell != lastCell) {
454                                         colSpan = getSpanVal(cell, 'colspan');
455                                         rowSpan = getSpanVal(cell, 'rowspan');
456
457                                         if (colSpan == 1) {
458                                                 if (!before) {
459                                                         dom.insertAfter(cloneCell(cell), cell);
460                                                         fillLeftDown(posX, y, rowSpan - 1, colSpan);
461                                                 } else {
462                                                         cell.parentNode.insertBefore(cloneCell(cell), cell);
463                                                         fillLeftDown(posX, y, rowSpan - 1, colSpan);
464                                                 }
465                                         } else
466                                                 setSpanVal(cell, 'colSpan', cell.colSpan + 1);
467
468                                         lastCell = cell;
469                                 }
470                         });
471                 };
472
473                 function deleteCols() {
474                         var cols = [];
475
476                         // Get selected column indexes
477                         each(grid, function(row, y) {
478                                 each(row, function(cell, x) {
479                                         if (isCellSelected(cell) && tinymce.inArray(cols, x) === -1) {
480                                                 each(grid, function(row) {
481                                                         var cell = row[x].elm, colSpan;
482
483                                                         colSpan = getSpanVal(cell, 'colSpan');
484
485                                                         if (colSpan > 1)
486                                                                 setSpanVal(cell, 'colSpan', colSpan - 1);
487                                                         else
488                                                                 dom.remove(cell);
489                                                 });
490
491                                                 cols.push(x);
492                                         }
493                                 });
494                         });
495
496                         cleanup();
497                 };
498
499                 function deleteRows() {
500                         var rows;
501
502                         function deleteRow(tr) {
503                                 var nextTr, pos, lastCell;
504
505                                 nextTr = dom.getNext(tr, 'tr');
506
507                                 // Move down row spanned cells
508                                 each(tr.cells, function(cell) {
509                                         var rowSpan = getSpanVal(cell, 'rowSpan');
510
511                                         if (rowSpan > 1) {
512                                                 setSpanVal(cell, 'rowSpan', rowSpan - 1);
513                                                 pos = getPos(cell);
514                                                 fillLeftDown(pos.x, pos.y, 1, 1);
515                                         }
516                                 });
517
518                                 // Delete cells
519                                 pos = getPos(tr.cells[0]);
520                                 each(grid[pos.y], function(cell) {
521                                         var rowSpan;
522
523                                         cell = cell.elm;
524
525                                         if (cell != lastCell) {
526                                                 rowSpan = getSpanVal(cell, 'rowSpan');
527
528                                                 if (rowSpan <= 1)
529                                                         dom.remove(cell);
530                                                 else
531                                                         setSpanVal(cell, 'rowSpan', rowSpan - 1);
532
533                                                 lastCell = cell;
534                                         }
535                                 });
536                         };
537
538                         // Get selected rows and move selection out of scope
539                         rows = getSelectedRows();
540
541                         // Delete all selected rows
542                         each(rows.reverse(), function(tr) {
543                                 deleteRow(tr);
544                         });
545
546                         cleanup();
547                 };
548
549                 function cutRows() {
550                         var rows = getSelectedRows();
551
552                         dom.remove(rows);
553                         cleanup();
554
555                         return rows;
556                 };
557
558                 function copyRows() {
559                         var rows = getSelectedRows();
560
561                         each(rows, function(row, i) {
562                                 rows[i] = cloneNode(row, true);
563                         });
564
565                         return rows;
566                 };
567
568                 function pasteRows(rows, before) {
569                         // If we don't have any rows in the clipboard, return immediately
570                         if(!rows)
571                                 return;
572
573                         var selectedRows = getSelectedRows(),
574                                 targetRow = selectedRows[before ? 0 : selectedRows.length - 1],
575                                 targetCellCount = targetRow.cells.length;
576
577                         // Calc target cell count
578                         each(grid, function(row) {
579                                 var match;
580
581                                 targetCellCount = 0;
582                                 each(row, function(cell, x) {
583                                         if (cell.real)
584                                                 targetCellCount += cell.colspan;
585
586                                         if (cell.elm.parentNode == targetRow)
587                                                 match = 1;
588                                 });
589
590                                 if (match)
591                                         return false;
592                         });
593
594                         if (!before)
595                                 rows.reverse();
596
597                         each(rows, function(row) {
598                                 var cellCount = row.cells.length, cell;
599
600                                 // Remove col/rowspans
601                                 for (i = 0; i < cellCount; i++) {
602                                         cell = row.cells[i];
603                                         setSpanVal(cell, 'colSpan', 1);
604                                         setSpanVal(cell, 'rowSpan', 1);
605                                 }
606
607                                 // Needs more cells
608                                 for (i = cellCount; i < targetCellCount; i++)
609                                         row.appendChild(cloneCell(row.cells[cellCount - 1]));
610
611                                 // Needs less cells
612                                 for (i = targetCellCount; i < cellCount; i++)
613                                         dom.remove(row.cells[i]);
614
615                                 // Add before/after
616                                 if (before)
617                                         targetRow.parentNode.insertBefore(row, targetRow);
618                                 else
619                                         dom.insertAfter(row, targetRow);
620                         });
621
622                         // Remove current selection
623                         dom.removeClass(dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
624                 };
625
626                 function getPos(target) {
627                         var pos;
628
629                         each(grid, function(row, y) {
630                                 each(row, function(cell, x) {
631                                         if (cell.elm == target) {
632                                                 pos = {x : x, y : y};
633                                                 return false;
634                                         }
635                                 });
636
637                                 return !pos;
638                         });
639
640                         return pos;
641                 };
642
643                 function setStartCell(cell) {
644                         startPos = getPos(cell);
645                 };
646
647                 function findEndPos() {
648                         var pos, maxX, maxY;
649
650                         maxX = maxY = 0;
651
652                         each(grid, function(row, y) {
653                                 each(row, function(cell, x) {
654                                         var colSpan, rowSpan;
655
656                                         if (isCellSelected(cell)) {
657                                                 cell = grid[y][x];
658
659                                                 if (x > maxX)
660                                                         maxX = x;
661
662                                                 if (y > maxY)
663                                                         maxY = y;
664
665                                                 if (cell.real) {
666                                                         colSpan = cell.colspan - 1;
667                                                         rowSpan = cell.rowspan - 1;
668
669                                                         if (colSpan) {
670                                                                 if (x + colSpan > maxX)
671                                                                         maxX = x + colSpan;
672                                                         }
673
674                                                         if (rowSpan) {
675                                                                 if (y + rowSpan > maxY)
676                                                                         maxY = y + rowSpan;
677                                                         }
678                                                 }
679                                         }
680                                 });
681                         });
682
683                         return {x : maxX, y : maxY};
684                 };
685
686                 function setEndCell(cell) {
687                         var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan;
688
689                         endPos = getPos(cell);
690
691                         if (startPos && endPos) {
692                                 // Get start/end positions
693                                 startX = Math.min(startPos.x, endPos.x);
694                                 startY = Math.min(startPos.y, endPos.y);
695                                 endX = Math.max(startPos.x, endPos.x);
696                                 endY = Math.max(startPos.y, endPos.y);
697
698                                 // Expand end positon to include spans
699                                 maxX = endX;
700                                 maxY = endY;
701
702                                 // Expand startX
703                                 for (y = startY; y <= maxY; y++) {
704                                         cell = grid[y][startX];
705
706                                         if (!cell.real) {
707                                                 if (startX - (cell.colspan - 1) < startX)
708                                                         startX -= cell.colspan - 1;
709                                         }
710                                 }
711
712                                 // Expand startY
713                                 for (x = startX; x <= maxX; x++) {
714                                         cell = grid[startY][x];
715
716                                         if (!cell.real) {
717                                                 if (startY - (cell.rowspan - 1) < startY)
718                                                         startY -= cell.rowspan - 1;
719                                         }
720                                 }
721
722                                 // Find max X, Y
723                                 for (y = startY; y <= endY; y++) {
724                                         for (x = startX; x <= endX; x++) {
725                                                 cell = grid[y][x];
726
727                                                 if (cell.real) {
728                                                         colSpan = cell.colspan - 1;
729                                                         rowSpan = cell.rowspan - 1;
730
731                                                         if (colSpan) {
732                                                                 if (x + colSpan > maxX)
733                                                                         maxX = x + colSpan;
734                                                         }
735
736                                                         if (rowSpan) {
737                                                                 if (y + rowSpan > maxY)
738                                                                         maxY = y + rowSpan;
739                                                         }
740                                                 }
741                                         }
742                                 }
743
744                                 // Remove current selection
745                                 dom.removeClass(dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
746
747                                 // Add new selection
748                                 for (y = startY; y <= maxY; y++) {
749                                         for (x = startX; x <= maxX; x++) {
750                                                 if (grid[y][x])
751                                                         dom.addClass(grid[y][x].elm, 'mceSelected');
752                                         }
753                                 }
754                         }
755                 };
756
757                 // Expose to public
758                 tinymce.extend(this, {
759                         deleteTable : deleteTable,
760                         split : split,
761                         merge : merge,
762                         insertRow : insertRow,
763                         insertCol : insertCol,
764                         deleteCols : deleteCols,
765                         deleteRows : deleteRows,
766                         cutRows : cutRows,
767                         copyRows : copyRows,
768                         pasteRows : pasteRows,
769                         getPos : getPos,
770                         setStartCell : setStartCell,
771                         setEndCell : setEndCell
772                 });
773         };
774
775         tinymce.create('tinymce.plugins.TablePlugin', {
776                 init : function(ed, url) {
777                         var winMan, clipboardRows, hasCellSelection = true; // Might be selected cells on reload
778
779                         function createTableGrid(node) {
780                                 var selection = ed.selection, tblElm = ed.dom.getParent(node || selection.getNode(), 'table');
781
782                                 if (tblElm)
783                                         return new TableGrid(tblElm, ed.dom, selection);
784                         };
785
786                         function cleanup() {
787                                 // Restore selection possibilities
788                                 ed.getBody().style.webkitUserSelect = '';
789
790                                 if (hasCellSelection) {
791                                         ed.dom.removeClass(ed.dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
792                                         hasCellSelection = false;
793                                 }
794                         };
795
796                         // Register buttons
797                         each([
798                                 ['table', 'table.desc', 'mceInsertTable', true],
799                                 ['delete_table', 'table.del', 'mceTableDelete'],
800                                 ['delete_col', 'table.delete_col_desc', 'mceTableDeleteCol'],
801                                 ['delete_row', 'table.delete_row_desc', 'mceTableDeleteRow'],
802                                 ['col_after', 'table.col_after_desc', 'mceTableInsertColAfter'],
803                                 ['col_before', 'table.col_before_desc', 'mceTableInsertColBefore'],
804                                 ['row_after', 'table.row_after_desc', 'mceTableInsertRowAfter'],
805                                 ['row_before', 'table.row_before_desc', 'mceTableInsertRowBefore'],
806                                 ['row_props', 'table.row_desc', 'mceTableRowProps', true],
807                                 ['cell_props', 'table.cell_desc', 'mceTableCellProps', true],
808                                 ['split_cells', 'table.split_cells_desc', 'mceTableSplitCells', true],
809                                 ['merge_cells', 'table.merge_cells_desc', 'mceTableMergeCells', true]
810                         ], function(c) {
811                                 ed.addButton(c[0], {title : c[1], cmd : c[2], ui : c[3]});
812                         });
813
814                         // Select whole table is a table border is clicked
815                         if (!tinymce.isIE) {
816                                 ed.onClick.add(function(ed, e) {
817                                         e = e.target;
818
819                                         if (e.nodeName === 'TABLE') {
820                                                 ed.selection.select(e);
821                                                 ed.nodeChanged();
822                                         }
823                                 });
824                         }
825
826                         ed.onPreProcess.add(function(ed, args) {
827                                 var nodes, i, node, dom = ed.dom, value;
828
829                                 nodes = dom.select('table', args.node);
830                                 i = nodes.length;
831                                 while (i--) {
832                                         node = nodes[i];
833                                         dom.setAttrib(node, 'data-mce-style', '');
834
835                                         if ((value = dom.getAttrib(node, 'width'))) {
836                                                 dom.setStyle(node, 'width', value);
837                                                 dom.setAttrib(node, 'width', '');
838                                         }
839
840                                         if ((value = dom.getAttrib(node, 'height'))) {
841                                                 dom.setStyle(node, 'height', value);
842                                                 dom.setAttrib(node, 'height', '');
843                                         }
844                                 }
845                         });
846
847                         // Handle node change updates
848                         ed.onNodeChange.add(function(ed, cm, n) {
849                                 var p;
850
851                                 n = ed.selection.getStart();
852                                 p = ed.dom.getParent(n, 'td,th,caption');
853                                 cm.setActive('table', n.nodeName === 'TABLE' || !!p);
854
855                                 // Disable table tools if we are in caption
856                                 if (p && p.nodeName === 'CAPTION')
857                                         p = 0;
858
859                                 cm.setDisabled('delete_table', !p);
860                                 cm.setDisabled('delete_col', !p);
861                                 cm.setDisabled('delete_table', !p);
862                                 cm.setDisabled('delete_row', !p);
863                                 cm.setDisabled('col_after', !p);
864                                 cm.setDisabled('col_before', !p);
865                                 cm.setDisabled('row_after', !p);
866                                 cm.setDisabled('row_before', !p);
867                                 cm.setDisabled('row_props', !p);
868                                 cm.setDisabled('cell_props', !p);
869                                 cm.setDisabled('split_cells', !p);
870                                 cm.setDisabled('merge_cells', !p);
871                         });
872
873                         ed.onInit.add(function(ed) {
874                                 var startTable, startCell, dom = ed.dom, tableGrid;
875
876                                 winMan = ed.windowManager;
877
878                                 // Add cell selection logic
879                                 ed.onMouseDown.add(function(ed, e) {
880                                         if (e.button != 2) {
881                                                 cleanup();
882
883                                                 startCell = dom.getParent(e.target, 'td,th');
884                                                 startTable = dom.getParent(startCell, 'table');
885                                         }
886                                 });
887
888                                 dom.bind(ed.getDoc(), 'mouseover', function(e) {
889                                         var sel, table, target = e.target;
890
891                                         if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) {
892                                                 table = dom.getParent(target, 'table');
893                                                 if (table == startTable) {
894                                                         if (!tableGrid) {
895                                                                 tableGrid = createTableGrid(table);
896                                                                 tableGrid.setStartCell(startCell);
897
898                                                                 ed.getBody().style.webkitUserSelect = 'none';
899                                                         }
900
901                                                         tableGrid.setEndCell(target);
902                                                         hasCellSelection = true;
903                                                 }
904
905                                                 // Remove current selection
906                                                 sel = ed.selection.getSel();
907
908                                                 try {
909                                                         if (sel.removeAllRanges)
910                                                                 sel.removeAllRanges();
911                                                         else
912                                                                 sel.empty();
913                                                 } catch (ex) {
914                                                         // IE9 might throw errors here
915                                                 }
916
917                                                 e.preventDefault();
918                                         }
919                                 });
920
921                                 ed.onMouseUp.add(function(ed, e) {
922                                         var rng, sel = ed.selection, selectedCells, nativeSel = sel.getSel(), walker, node, lastNode, endNode;
923
924                                         // Move selection to startCell
925                                         if (startCell) {
926                                                 if (tableGrid)
927                                                         ed.getBody().style.webkitUserSelect = '';
928
929                                                 function setPoint(node, start) {
930                                                         var walker = new tinymce.dom.TreeWalker(node, node);
931
932                                                         do {
933                                                                 // Text node
934                                                                 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {
935                                                                         if (start)
936                                                                                 rng.setStart(node, 0);
937                                                                         else
938                                                                                 rng.setEnd(node, node.nodeValue.length);
939
940                                                                         return;
941                                                                 }
942
943                                                                 // BR element
944                                                                 if (node.nodeName == 'BR') {
945                                                                         if (start)
946                                                                                 rng.setStartBefore(node);
947                                                                         else
948                                                                                 rng.setEndBefore(node);
949
950                                                                         return;
951                                                                 }
952                                                         } while (node = (start ? walker.next() : walker.prev()));
953                                                 }
954
955                                                 // Try to expand text selection as much as we can only Gecko supports cell selection
956                                                 selectedCells = dom.select('td.mceSelected,th.mceSelected');
957                                                 if (selectedCells.length > 0) {
958                                                         rng = dom.createRng();
959                                                         node = selectedCells[0];
960                                                         endNode = selectedCells[selectedCells.length - 1];
961                                                         rng.setStartBefore(node);
962                                                         rng.setEndAfter(node);
963
964                                                         setPoint(node, 1);
965                                                         walker = new tinymce.dom.TreeWalker(node, dom.getParent(selectedCells[0], 'table'));
966
967                                                         do {
968                                                                 if (node.nodeName == 'TD' || node.nodeName == 'TH') {
969                                                                         if (!dom.hasClass(node, 'mceSelected'))
970                                                                                 break;
971
972                                                                         lastNode = node;
973                                                                 }
974                                                         } while (node = walker.next());
975
976                                                         setPoint(lastNode);
977
978                                                         sel.setRng(rng);
979                                                 }
980
981                                                 ed.nodeChanged();
982                                                 startCell = tableGrid = startTable = null;
983                                         }
984                                 });
985
986                                 ed.onKeyUp.add(function(ed, e) {
987                                         cleanup();
988                                 });
989
990                                 ed.onKeyDown.add(function (ed, e) {
991                                         fixTableCellSelection(ed);
992                                 });
993
994                                 ed.onMouseDown.add(function (ed, e) {
995                                         if (e.button != 2) {
996                                                 fixTableCellSelection(ed);
997                                         }
998                                 });
999                                 function tableCellSelected(ed, rng, n, currentCell) {
1000                                         // The decision of when a table cell is selected is somewhat involved.  The fact that this code is
1001                                         // required is actually a pointer to the root cause of this bug. A cell is selected when the start 
1002                                         // and end offsets are 0, the start container is a text, and the selection node is either a TR (most cases)
1003                                         // or the parent of the table (in the case of the selection containing the last cell of a table).
1004                                         var TEXT_NODE = 3, table = ed.dom.getParent(rng.startContainer, 'TABLE'), 
1005                                         tableParent, allOfCellSelected, tableCellSelection;
1006                                         if (table) 
1007                                         tableParent = table.parentNode;
1008                                         allOfCellSelected =rng.startContainer.nodeType == TEXT_NODE && 
1009                                                 rng.startOffset == 0 && 
1010                                                 rng.endOffset == 0 && 
1011                                                 currentCell && 
1012                                                 (n.nodeName=="TR" || n==tableParent);
1013                                         tableCellSelection = (n.nodeName=="TD"||n.nodeName=="TH")&& !currentCell;          
1014                                         return  allOfCellSelected || tableCellSelection;
1015                                         // return false;
1016                                 }
1017                                 
1018                                 // this nasty hack is here to work around some WebKit selection bugs.
1019                                 function fixTableCellSelection(ed) {
1020                                         if (!tinymce.isWebKit)
1021                                                 return;
1022
1023                                         var rng = ed.selection.getRng();
1024                                         var n = ed.selection.getNode();
1025                                         var currentCell = ed.dom.getParent(rng.startContainer, 'TD,TH');
1026                                 
1027                                         if (!tableCellSelected(ed, rng, n, currentCell))
1028                                                 return;
1029                                                 if (!currentCell) {
1030                                                         currentCell=n;
1031                                                 }
1032                                         
1033                                         // Get the very last node inside the table cell
1034                                         var end = currentCell.lastChild;
1035                                         while (end.lastChild)
1036                                                 end = end.lastChild;
1037                                         
1038                                         // Select the entire table cell. Nothing outside of the table cell should be selected.
1039                                         rng.setEnd(end, end.nodeValue.length);
1040                                         ed.selection.setRng(rng);
1041                                 }
1042                                 ed.plugins.table.fixTableCellSelection=fixTableCellSelection;
1043
1044                                 // Add context menu
1045                                 if (ed && ed.plugins.contextmenu) {
1046                                         ed.plugins.contextmenu.onContextMenu.add(function(th, m, e) {
1047                                                 var sm, se = ed.selection, el = se.getNode() || ed.getBody();
1048
1049                                                 if (ed.dom.getParent(e, 'td') || ed.dom.getParent(e, 'th') || ed.dom.select('td.mceSelected,th.mceSelected').length) {
1050                                                         m.removeAll();
1051
1052                                                         if (el.nodeName == 'A' && !ed.dom.getAttrib(el, 'name')) {
1053                                                                 m.add({title : 'advanced.link_desc', icon : 'link', cmd : ed.plugins.advlink ? 'mceAdvLink' : 'mceLink', ui : true});
1054                                                                 m.add({title : 'advanced.unlink_desc', icon : 'unlink', cmd : 'UnLink'});
1055                                                                 m.addSeparator();
1056                                                         }
1057
1058                                                         if (el.nodeName == 'IMG' && el.className.indexOf('mceItem') == -1) {
1059                                                                 m.add({title : 'advanced.image_desc', icon : 'image', cmd : ed.plugins.advimage ? 'mceAdvImage' : 'mceImage', ui : true});
1060                                                                 m.addSeparator();
1061                                                         }
1062
1063                                                         m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable', value : {action : 'insert'}});
1064                                                         m.add({title : 'table.props_desc', icon : 'table_props', cmd : 'mceInsertTable'});
1065                                                         m.add({title : 'table.del', icon : 'delete_table', cmd : 'mceTableDelete'});
1066                                                         m.addSeparator();
1067
1068                                                         // Cell menu
1069                                                         sm = m.addMenu({title : 'table.cell'});
1070                                                         sm.add({title : 'table.cell_desc', icon : 'cell_props', cmd : 'mceTableCellProps'});
1071                                                         sm.add({title : 'table.split_cells_desc', icon : 'split_cells', cmd : 'mceTableSplitCells'});
1072                                                         sm.add({title : 'table.merge_cells_desc', icon : 'merge_cells', cmd : 'mceTableMergeCells'});
1073
1074                                                         // Row menu
1075                                                         sm = m.addMenu({title : 'table.row'});
1076                                                         sm.add({title : 'table.row_desc', icon : 'row_props', cmd : 'mceTableRowProps'});
1077                                                         sm.add({title : 'table.row_before_desc', icon : 'row_before', cmd : 'mceTableInsertRowBefore'});
1078                                                         sm.add({title : 'table.row_after_desc', icon : 'row_after', cmd : 'mceTableInsertRowAfter'});
1079                                                         sm.add({title : 'table.delete_row_desc', icon : 'delete_row', cmd : 'mceTableDeleteRow'});
1080                                                         sm.addSeparator();
1081                                                         sm.add({title : 'table.cut_row_desc', icon : 'cut', cmd : 'mceTableCutRow'});
1082                                                         sm.add({title : 'table.copy_row_desc', icon : 'copy', cmd : 'mceTableCopyRow'});
1083                                                         sm.add({title : 'table.paste_row_before_desc', icon : 'paste', cmd : 'mceTablePasteRowBefore'}).setDisabled(!clipboardRows);
1084                                                         sm.add({title : 'table.paste_row_after_desc', icon : 'paste', cmd : 'mceTablePasteRowAfter'}).setDisabled(!clipboardRows);
1085
1086                                                         // Column menu
1087                                                         sm = m.addMenu({title : 'table.col'});
1088                                                         sm.add({title : 'table.col_before_desc', icon : 'col_before', cmd : 'mceTableInsertColBefore'});
1089                                                         sm.add({title : 'table.col_after_desc', icon : 'col_after', cmd : 'mceTableInsertColAfter'});
1090                                                         sm.add({title : 'table.delete_col_desc', icon : 'delete_col', cmd : 'mceTableDeleteCol'});
1091                                                 } else
1092                                                         m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable'});
1093                                         });
1094                                 }
1095
1096                                 // Fix to allow navigating up and down in a table in WebKit browsers.
1097                                 if (tinymce.isWebKit) {
1098                                         function moveSelection(ed, e) {
1099                                                 var VK = tinymce.VK;
1100                                                 var key = e.keyCode;
1101
1102                                                 function handle(upBool, sourceNode, event) {
1103                                                         var siblingDirection = upBool ? 'previousSibling' : 'nextSibling';
1104                                                         var currentRow = ed.dom.getParent(sourceNode, 'tr');
1105                                                         var siblingRow = currentRow[siblingDirection];
1106
1107                                                         if (siblingRow) {
1108                                                                 moveCursorToRow(ed, sourceNode, siblingRow, upBool);
1109                                                                 tinymce.dom.Event.cancel(event);
1110                                                                 return true;
1111                                                         } else {
1112                                                                 var tableNode = ed.dom.getParent(currentRow, 'table');
1113                                                                 var middleNode = currentRow.parentNode;
1114                                                                 var parentNodeName = middleNode.nodeName.toLowerCase();
1115                                                                 if (parentNodeName === 'tbody' || parentNodeName === (upBool ? 'tfoot' : 'thead')) {
1116                                                                         var targetParent = getTargetParent(upBool, tableNode, middleNode, 'tbody');
1117                                                                         if (targetParent !== null) {
1118                                                                                 return moveToRowInTarget(upBool, targetParent, sourceNode, event);
1119                                                                         }
1120                                                                 }
1121                                                                 return escapeTable(upBool, currentRow, siblingDirection, tableNode, event);
1122                                                         }
1123                                                 }
1124
1125                                                 function getTargetParent(upBool, topNode, secondNode, nodeName) {
1126                                                         var tbodies = ed.dom.select('>' + nodeName, topNode);
1127                                                         var position = tbodies.indexOf(secondNode);
1128                                                         if (upBool && position === 0 || !upBool && position === tbodies.length - 1) {
1129                                                                 return getFirstHeadOrFoot(upBool, topNode);
1130                                                         } else if (position === -1) {
1131                                                                 var topOrBottom = secondNode.tagName.toLowerCase() === 'thead' ? 0 : tbodies.length - 1;
1132                                                                 return tbodies[topOrBottom];
1133                                                         } else {
1134                                                                 return tbodies[position + (upBool ? -1 : 1)];
1135                                                         }
1136                                                 }
1137
1138                                                 function getFirstHeadOrFoot(upBool, parent) {
1139                                                         var tagName = upBool ? 'thead' : 'tfoot';
1140                                                         var headOrFoot = ed.dom.select('>' + tagName, parent);
1141                                                         return headOrFoot.length !== 0 ? headOrFoot[0] : null;
1142                                                 }
1143
1144                                                 function moveToRowInTarget(upBool, targetParent, sourceNode, event) {
1145                                                         var targetRow = getChildForDirection(targetParent, upBool);
1146                                                         targetRow && moveCursorToRow(ed, sourceNode, targetRow, upBool);
1147                                                         tinymce.dom.Event.cancel(event);
1148                                                         return true;
1149                                                 }
1150
1151                                                 function escapeTable(upBool, currentRow, siblingDirection, table, event) {
1152                                                         var tableSibling = table[siblingDirection];
1153                                                         if (tableSibling) {
1154                                                                 moveCursorToStartOfElement(tableSibling);
1155                                                                 return true;
1156                                                         } else {
1157                                                                 var parentCell = ed.dom.getParent(table, 'td,th');
1158                                                                 if (parentCell) {
1159                                                                         return handle(upBool, parentCell, event);
1160                                                                 } else {
1161                                                                         var backUpSibling = getChildForDirection(currentRow, !upBool);
1162                                                                         moveCursorToStartOfElement(backUpSibling);
1163                                                                         return tinymce.dom.Event.cancel(event);
1164                                                                 }
1165                                                         }
1166                                                 }
1167
1168                                                 function getChildForDirection(parent, up) {
1169                                                         var child =  parent && parent[up ? 'lastChild' : 'firstChild'];
1170                                                         // BR is not a valid table child to return in this case we return the table cell
1171                                                         return child && child.nodeName === 'BR' ? ed.dom.getParent(child, 'td,th') : child;
1172                                                 }
1173
1174                                                 function moveCursorToStartOfElement(n) {
1175                                                         ed.selection.setCursorLocation(n, 0);
1176                                                 }
1177
1178                                                 function isVerticalMovement() {
1179                                                         return key == VK.UP || key == VK.DOWN;
1180                                                 }
1181
1182                                                 function isInTable(ed) {
1183                                                         var node = ed.selection.getNode();
1184                                                         var currentRow = ed.dom.getParent(node, 'tr');
1185                                                         return currentRow !== null;
1186                                                 }
1187
1188                                                 function columnIndex(column) {
1189                                                         var colIndex = 0;
1190                                                         var c = column;
1191                                                         while (c.previousSibling) {
1192                                                                 c = c.previousSibling;
1193                                                                 colIndex = colIndex + getSpanVal(c, "colspan");
1194                                                         }
1195                                                         return colIndex;
1196                                                 }
1197
1198                                                 function findColumn(rowElement, columnIndex) {
1199                                                         var c = 0;
1200                                                         var r = 0;
1201                                                         each(rowElement.children, function(cell, i) {
1202                                                                 c = c + getSpanVal(cell, "colspan");
1203                                                                 r = i;
1204                                                                 if (c > columnIndex)
1205                                                                         return false;
1206                                                         });
1207                                                         return r;
1208                                                 }
1209
1210                                                 function moveCursorToRow(ed, node, row, upBool) {
1211                                                         var srcColumnIndex = columnIndex(ed.dom.getParent(node, 'td,th'));
1212                                                         var tgtColumnIndex = findColumn(row, srcColumnIndex);
1213                                                         var tgtNode = row.childNodes[tgtColumnIndex];
1214                                                         var rowCellTarget = getChildForDirection(tgtNode, upBool);
1215                                                         moveCursorToStartOfElement(rowCellTarget || tgtNode);
1216                                                 }
1217
1218                                                 function shouldFixCaret(preBrowserNode) {
1219                                                         var newNode = ed.selection.getNode();
1220                                                         var newParent = ed.dom.getParent(newNode, 'td,th');
1221                                                         var oldParent = ed.dom.getParent(preBrowserNode, 'td,th');
1222                                                         return newParent && newParent !== oldParent && checkSameParentTable(newParent, oldParent)
1223                                                 }
1224
1225                                                 function checkSameParentTable(nodeOne, NodeTwo) {
1226                                                         return ed.dom.getParent(nodeOne, 'TABLE') === ed.dom.getParent(NodeTwo, 'TABLE');
1227                                                 }
1228
1229                                                 if (isVerticalMovement() && isInTable(ed)) {
1230                                                         var preBrowserNode = ed.selection.getNode();
1231                                                         setTimeout(function() {
1232                                                                 if (shouldFixCaret(preBrowserNode)) {
1233                                                                         handle(!e.shiftKey && key === VK.UP, preBrowserNode, e);
1234                                                                 }
1235                                                         }, 0);
1236                                                 }
1237                                         }
1238
1239                                         ed.onKeyDown.add(moveSelection);
1240                                 }
1241
1242                                 // Fixes an issue on Gecko where it's impossible to place the caret behind a table
1243                                 // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
1244                                 function fixTableCaretPos() {
1245                                         var last;
1246
1247                                         // Skip empty text nodes form the end
1248                                         for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ;
1249
1250                                         if (last && last.nodeName == 'TABLE') {
1251                                                 if (ed.settings.forced_root_block)
1252                                                         ed.dom.add(ed.getBody(), ed.settings.forced_root_block, null, tinymce.isIE && !tinymce.isIE11 ? '&nbsp;' : '<br data-mce-bogus="1" />');
1253                                                 else
1254                                                         ed.dom.add(ed.getBody(), 'br', {'data-mce-bogus': '1'});
1255                                         }
1256                                 };
1257
1258                                 // Fixes an bug where it's impossible to place the caret before a table in Gecko
1259                                 // this fix solves it by detecting when the caret is at the beginning of such a table
1260                                 // and then manually moves the caret infront of the table
1261                                 if (tinymce.isGecko) {
1262                                         ed.onKeyDown.add(function(ed, e) {
1263                                                 var rng, table, dom = ed.dom;
1264
1265                                                 // On gecko it's not possible to place the caret before a table
1266                                                 if (e.keyCode == 37 || e.keyCode == 38) {
1267                                                         rng = ed.selection.getRng();
1268                                                         table = dom.getParent(rng.startContainer, 'table');
1269
1270                                                         if (table && ed.getBody().firstChild == table) {
1271                                                                 if (isAtStart(rng, table)) {
1272                                                                         rng = dom.createRng();
1273
1274                                                                         rng.setStartBefore(table);
1275                                                                         rng.setEndBefore(table);
1276
1277                                                                         ed.selection.setRng(rng);
1278
1279                                                                         e.preventDefault();
1280                                                                 }
1281                                                         }
1282                                                 }
1283                                         });
1284                                 }
1285
1286                                 ed.onKeyUp.add(fixTableCaretPos);
1287                                 ed.onSetContent.add(fixTableCaretPos);
1288                                 ed.onVisualAid.add(fixTableCaretPos);
1289
1290                                 ed.onPreProcess.add(function(ed, o) {
1291                                         var last = o.node.lastChild;
1292
1293                                         if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 && (last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))) && last.previousSibling && last.previousSibling.nodeName == "TABLE") {
1294                                                 ed.dom.remove(last);
1295                                         }
1296                                 });
1297
1298
1299                                 /**
1300                                  * Fixes bug in Gecko where shift-enter in table cell does not place caret on new line
1301                                  *
1302                                  * Removed: Since the new enter logic seems to fix this one.
1303                                  */
1304                                 /*
1305                                 if (tinymce.isGecko) {
1306                                         ed.onKeyDown.add(function(ed, e) {
1307                                                 if (e.keyCode === tinymce.VK.ENTER && e.shiftKey) {
1308                                                         var node = ed.selection.getRng().startContainer;
1309                                                         var tableCell = dom.getParent(node, 'td,th');
1310                                                         if (tableCell) {
1311                                                                 var zeroSizedNbsp = ed.getDoc().createTextNode("\uFEFF");
1312                                                                 dom.insertAfter(zeroSizedNbsp, node);
1313                                                         }
1314                                                 }
1315                                         });
1316                                 }
1317                                 */
1318
1319                                 fixTableCaretPos();
1320                                 ed.startContent = ed.getContent({format : 'raw'});
1321                         });
1322
1323                         // Register action commands
1324                         each({
1325                                 mceTableSplitCells : function(grid) {
1326                                         grid.split();
1327                                 },
1328
1329                                 mceTableMergeCells : function(grid) {
1330                                         var rowSpan, colSpan, cell;
1331
1332                                         cell = ed.dom.getParent(ed.selection.getNode(), 'th,td');
1333                                         if (cell) {
1334                                                 rowSpan = cell.rowSpan;
1335                                                 colSpan = cell.colSpan;
1336                                         }
1337
1338                                         if (!ed.dom.select('td.mceSelected,th.mceSelected').length) {
1339                                                 winMan.open({
1340                                                         url : url + '/merge_cells.htm',
1341                                                         width : 240 + parseInt(ed.getLang('table.merge_cells_delta_width', 0)),
1342                                                         height : 110 + parseInt(ed.getLang('table.merge_cells_delta_height', 0)),
1343                                                         inline : 1
1344                                                 }, {
1345                                                         rows : rowSpan,
1346                                                         cols : colSpan,
1347                                                         onaction : function(data) {
1348                                                                 grid.merge(cell, data.cols, data.rows);
1349                                                         },
1350                                                         plugin_url : url
1351                                                 });
1352                                         } else
1353                                                 grid.merge();
1354                                 },
1355
1356                                 mceTableInsertRowBefore : function(grid) {
1357                                         grid.insertRow(true);
1358                                 },
1359
1360                                 mceTableInsertRowAfter : function(grid) {
1361                                         grid.insertRow();
1362                                 },
1363
1364                                 mceTableInsertColBefore : function(grid) {
1365                                         grid.insertCol(true);
1366                                 },
1367
1368                                 mceTableInsertColAfter : function(grid) {
1369                                         grid.insertCol();
1370                                 },
1371
1372                                 mceTableDeleteCol : function(grid) {
1373                                         grid.deleteCols();
1374                                 },
1375
1376                                 mceTableDeleteRow : function(grid) {
1377                                         grid.deleteRows();
1378                                 },
1379
1380                                 mceTableCutRow : function(grid) {
1381                                         clipboardRows = grid.cutRows();
1382                                 },
1383
1384                                 mceTableCopyRow : function(grid) {
1385                                         clipboardRows = grid.copyRows();
1386                                 },
1387
1388                                 mceTablePasteRowBefore : function(grid) {
1389                                         grid.pasteRows(clipboardRows, true);
1390                                 },
1391
1392                                 mceTablePasteRowAfter : function(grid) {
1393                                         grid.pasteRows(clipboardRows);
1394                                 },
1395
1396                                 mceTableDelete : function(grid) {
1397                                         grid.deleteTable();
1398                                 }
1399                         }, function(func, name) {
1400                                 ed.addCommand(name, function() {
1401                                         var grid = createTableGrid();
1402
1403                                         if (grid) {
1404                                                 func(grid);
1405                                                 ed.execCommand('mceRepaint');
1406                                                 cleanup();
1407                                         }
1408                                 });
1409                         });
1410
1411                         // Register dialog commands
1412                         each({
1413                                 mceInsertTable : function(val) {
1414                                         winMan.open({
1415                                                 url : url + '/table.htm',
1416                                                 width : 400 + parseInt(ed.getLang('table.table_delta_width', 0)),
1417                                                 height : 320 + parseInt(ed.getLang('table.table_delta_height', 0)),
1418                                                 inline : 1
1419                                         }, {
1420                                                 plugin_url : url,
1421                                                 action : val ? val.action : 0
1422                                         });
1423                                 },
1424
1425                                 mceTableRowProps : function() {
1426                                         winMan.open({
1427                                                 url : url + '/row.htm',
1428                                                 width : 400 + parseInt(ed.getLang('table.rowprops_delta_width', 0)),
1429                                                 height : 295 + parseInt(ed.getLang('table.rowprops_delta_height', 0)),
1430                                                 inline : 1
1431                                         }, {
1432                                                 plugin_url : url
1433                                         });
1434                                 },
1435
1436                                 mceTableCellProps : function() {
1437                                         winMan.open({
1438                                                 url : url + '/cell.htm',
1439                                                 width : 400 + parseInt(ed.getLang('table.cellprops_delta_width', 0)),
1440                                                 height : 295 + parseInt(ed.getLang('table.cellprops_delta_height', 0)),
1441                                                 inline : 1
1442                                         }, {
1443                                                 plugin_url : url
1444                                         });
1445                                 }
1446                         }, function(func, name) {
1447                                 ed.addCommand(name, function(ui, val) {
1448                                         func(val);
1449                                 });
1450                         });
1451                 }
1452         });
1453
1454         // Register plugin
1455         tinymce.PluginManager.add('table', tinymce.plugins.TablePlugin);
1456 })(tinymce);