source: trunk/library/tiny_mce/plugins/table/editor_plugin_src.js @ 4829

Revision 4829, 30.8 KB checked in by airton, 13 years ago (diff)

Ticket #2146 - Implementacao da funcionalidade de multiplas assinaturas - Adicao da biblioteca TinyMCE

  • Property svn:executable set to *
Line 
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        /**
29         * Table Grid class.
30         */
31        function TableGrid(table, dom, selection) {
32                var grid, startPos, endPos, selectedCell;
33
34                buildGrid();
35                selectedCell = dom.getParent(selection.getStart(), 'th,td');
36                if (selectedCell) {
37                        startPos = getPos(selectedCell);
38                        endPos = findEndPos();
39                        selectedCell = getCell(startPos.x, startPos.y);
40                }
41
42                function cloneNode(node, children) {
43                        node = node.cloneNode(children);
44                        node.removeAttribute('id');
45
46                        return node;
47                }
48
49                function buildGrid() {
50                        var startY = 0;
51
52                        grid = [];
53
54                        each(['thead', 'tbody', 'tfoot'], function(part) {
55                                var rows = dom.select('> ' + part + ' tr', table);
56
57                                each(rows, function(tr, y) {
58                                        y += startY;
59
60                                        each(dom.select('> td, > th', tr), function(td, x) {
61                                                var x2, y2, rowspan, colspan;
62
63                                                // Skip over existing cells produced by rowspan
64                                                if (grid[y]) {
65                                                        while (grid[y][x])
66                                                                x++;
67                                                }
68
69                                                // Get col/rowspan from cell
70                                                rowspan = getSpanVal(td, 'rowspan');
71                                                colspan = getSpanVal(td, 'colspan');
72
73                                                // Fill out rowspan/colspan right and down
74                                                for (y2 = y; y2 < y + rowspan; y2++) {
75                                                        if (!grid[y2])
76                                                                grid[y2] = [];
77
78                                                        for (x2 = x; x2 < x + colspan; x2++) {
79                                                                grid[y2][x2] = {
80                                                                        part : part,
81                                                                        real : y2 == y && x2 == x,
82                                                                        elm : td,
83                                                                        rowspan : rowspan,
84                                                                        colspan : colspan
85                                                                };
86                                                        }
87                                                }
88                                        });
89                                });
90
91                                startY += rows.length;
92                        });
93                };
94
95                function getCell(x, y) {
96                        var row;
97
98                        row = grid[y];
99                        if (row)
100                                return row[x];
101                };
102
103                function getSpanVal(td, name) {
104                        return parseInt(td.getAttribute(name) || 1);
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 ? '&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)
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                                // Use selection
291                                startX = startPos.x;
292                                startY = startPos.y;
293                                endX = endPos.x;
294                                endY = endPos.y;
295                        }
296
297                        // Find start/end cells
298                        startCell = getCell(startX, startY);
299                        endCell = getCell(endX, endY);
300
301                        // Check if the cells exists and if they are of the same part for example tbody = tbody
302                        if (startCell && endCell && startCell.part == endCell.part) {
303                                // Split and rebuild grid
304                                split();
305                                buildGrid();
306
307                                // Set row/col span to start cell
308                                startCell = getCell(startX, startY).elm;
309                                setSpanVal(startCell, 'colSpan', (endX - startX) + 1);
310                                setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);
311
312                                // Remove other cells and add it's contents to the start cell
313                                for (y = startY; y <= endY; y++) {
314                                        for (x = startX; x <= endX; x++) {
315                                                if (!grid[y] || !grid[y][x])
316                                                        continue;
317
318                                                cell = grid[y][x].elm;
319
320                                                if (cell != startCell) {
321                                                        // Move children to startCell
322                                                        children = tinymce.grep(cell.childNodes);
323                                                        each(children, function(node) {
324                                                                startCell.appendChild(node);
325                                                        });
326
327                                                        // Remove bogus nodes if there is children in the target cell
328                                                        if (children.length) {
329                                                                children = tinymce.grep(startCell.childNodes);
330                                                                count = 0;
331                                                                each(children, function(node) {
332                                                                        if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1)
333                                                                                startCell.removeChild(node);
334                                                                });
335                                                        }
336                                                       
337                                                        // Remove cell
338                                                        dom.remove(cell);
339                                                }
340                                        }
341                                }
342
343                                // Remove empty rows etc and restore caret location
344                                cleanup();
345                        }
346                };
347
348                function insertRow(before) {
349                        var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;
350
351                        // Find first/last row
352                        each(grid, function(row, y) {
353                                each(row, function(cell, x) {
354                                        if (isCellSelected(cell)) {
355                                                cell = cell.elm;
356                                                rowElm = cell.parentNode;
357                                                newRow = cloneNode(rowElm, false);
358                                                posY = y;
359
360                                                if (before)
361                                                        return false;
362                                        }
363                                });
364
365                                if (before)
366                                        return !posY;
367                        });
368
369                        for (x = 0; x < grid[0].length; x++) {
370                                // Cell not found could be because of an invalid table structure
371                                if (!grid[posY][x])
372                                        continue;
373
374                                cell = grid[posY][x].elm;
375
376                                if (cell != lastCell) {
377                                        if (!before) {
378                                                rowSpan = getSpanVal(cell, 'rowspan');
379                                                if (rowSpan > 1) {
380                                                        setSpanVal(cell, 'rowSpan', rowSpan + 1);
381                                                        continue;
382                                                }
383                                        } else {
384                                                // Check if cell above can be expanded
385                                                if (posY > 0 && grid[posY - 1][x]) {
386                                                        otherCell = grid[posY - 1][x].elm;
387                                                        rowSpan = getSpanVal(otherCell, 'rowSpan');
388                                                        if (rowSpan > 1) {
389                                                                setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
390                                                                continue;
391                                                        }
392                                                }
393                                        }
394
395                                        // Insert new cell into new row
396                                        newCell = cloneCell(cell);
397                                        setSpanVal(newCell, 'colSpan', cell.colSpan);
398
399                                        newRow.appendChild(newCell);
400
401                                        lastCell = cell;
402                                }
403                        }
404
405                        if (newRow.hasChildNodes()) {
406                                if (!before)
407                                        dom.insertAfter(newRow, rowElm);
408                                else
409                                        rowElm.parentNode.insertBefore(newRow, rowElm);
410                        }
411                };
412
413                function insertCol(before) {
414                        var posX, lastCell;
415
416                        // Find first/last column
417                        each(grid, function(row, y) {
418                                each(row, function(cell, x) {
419                                        if (isCellSelected(cell)) {
420                                                posX = x;
421
422                                                if (before)
423                                                        return false;
424                                        }
425                                });
426
427                                if (before)
428                                        return !posX;
429                        });
430
431                        each(grid, function(row, y) {
432                                var cell, rowSpan, colSpan;
433
434                                if (!row[posX])
435                                        return;
436
437                                cell = row[posX].elm;
438                                if (cell != lastCell) {
439                                        colSpan = getSpanVal(cell, 'colspan');
440                                        rowSpan = getSpanVal(cell, 'rowspan');
441
442                                        if (colSpan == 1) {
443                                                if (!before) {
444                                                        dom.insertAfter(cloneCell(cell), cell);
445                                                        fillLeftDown(posX, y, rowSpan - 1, colSpan);
446                                                } else {
447                                                        cell.parentNode.insertBefore(cloneCell(cell), cell);
448                                                        fillLeftDown(posX, y, rowSpan - 1, colSpan);
449                                                }
450                                        } else
451                                                setSpanVal(cell, 'colSpan', cell.colSpan + 1);
452
453                                        lastCell = cell;
454                                }
455                        });
456                };
457
458                function deleteCols() {
459                        var cols = [];
460
461                        // Get selected column indexes
462                        each(grid, function(row, y) {
463                                each(row, function(cell, x) {
464                                        if (isCellSelected(cell) && tinymce.inArray(cols, x) === -1) {
465                                                each(grid, function(row) {
466                                                        var cell = row[x].elm, colSpan;
467
468                                                        colSpan = getSpanVal(cell, 'colSpan');
469
470                                                        if (colSpan > 1)
471                                                                setSpanVal(cell, 'colSpan', colSpan - 1);
472                                                        else
473                                                                dom.remove(cell);
474                                                });
475
476                                                cols.push(x);
477                                        }
478                                });
479                        });
480
481                        cleanup();
482                };
483
484                function deleteRows() {
485                        var rows;
486
487                        function deleteRow(tr) {
488                                var nextTr, pos, lastCell;
489
490                                nextTr = dom.getNext(tr, 'tr');
491
492                                // Move down row spanned cells
493                                each(tr.cells, function(cell) {
494                                        var rowSpan = getSpanVal(cell, 'rowSpan');
495
496                                        if (rowSpan > 1) {
497                                                setSpanVal(cell, 'rowSpan', rowSpan - 1);
498                                                pos = getPos(cell);
499                                                fillLeftDown(pos.x, pos.y, 1, 1);
500                                        }
501                                });
502
503                                // Delete cells
504                                pos = getPos(tr.cells[0]);
505                                each(grid[pos.y], function(cell) {
506                                        var rowSpan;
507
508                                        cell = cell.elm;
509
510                                        if (cell != lastCell) {
511                                                rowSpan = getSpanVal(cell, 'rowSpan');
512
513                                                if (rowSpan <= 1)
514                                                        dom.remove(cell);
515                                                else
516                                                        setSpanVal(cell, 'rowSpan', rowSpan - 1);
517
518                                                lastCell = cell;
519                                        }
520                                });
521                        };
522
523                        // Get selected rows and move selection out of scope
524                        rows = getSelectedRows();
525
526                        // Delete all selected rows
527                        each(rows.reverse(), function(tr) {
528                                deleteRow(tr);
529                        });
530
531                        cleanup();
532                };
533
534                function cutRows() {
535                        var rows = getSelectedRows();
536
537                        dom.remove(rows);
538                        cleanup();
539
540                        return rows;
541                };
542
543                function copyRows() {
544                        var rows = getSelectedRows();
545
546                        each(rows, function(row, i) {
547                                rows[i] = cloneNode(row, true);
548                        });
549
550                        return rows;
551                };
552
553                function pasteRows(rows, before) {
554                        var selectedRows = getSelectedRows(),
555                                targetRow = selectedRows[before ? 0 : selectedRows.length - 1],
556                                targetCellCount = targetRow.cells.length;
557
558                        // Calc target cell count
559                        each(grid, function(row) {
560                                var match;
561
562                                targetCellCount = 0;
563                                each(row, function(cell, x) {
564                                        if (cell.real)
565                                                targetCellCount += cell.colspan;
566
567                                        if (cell.elm.parentNode == targetRow)
568                                                match = 1;
569                                });
570
571                                if (match)
572                                        return false;
573                        });
574
575                        if (!before)
576                                rows.reverse();
577
578                        each(rows, function(row) {
579                                var cellCount = row.cells.length, cell;
580
581                                // Remove col/rowspans
582                                for (i = 0; i < cellCount; i++) {
583                                        cell = row.cells[i];
584                                        setSpanVal(cell, 'colSpan', 1);
585                                        setSpanVal(cell, 'rowSpan', 1);
586                                }
587
588                                // Needs more cells
589                                for (i = cellCount; i < targetCellCount; i++)
590                                        row.appendChild(cloneCell(row.cells[cellCount - 1]));
591
592                                // Needs less cells
593                                for (i = targetCellCount; i < cellCount; i++)
594                                        dom.remove(row.cells[i]);
595
596                                // Add before/after
597                                if (before)
598                                        targetRow.parentNode.insertBefore(row, targetRow);
599                                else
600                                        dom.insertAfter(row, targetRow);
601                        });
602                };
603
604                function getPos(target) {
605                        var pos;
606
607                        each(grid, function(row, y) {
608                                each(row, function(cell, x) {
609                                        if (cell.elm == target) {
610                                                pos = {x : x, y : y};
611                                                return false;
612                                        }
613                                });
614
615                                return !pos;
616                        });
617
618                        return pos;
619                };
620
621                function setStartCell(cell) {
622                        startPos = getPos(cell);
623                };
624
625                function findEndPos() {
626                        var pos, maxX, maxY;
627
628                        maxX = maxY = 0;
629
630                        each(grid, function(row, y) {
631                                each(row, function(cell, x) {
632                                        var colSpan, rowSpan;
633
634                                        if (isCellSelected(cell)) {
635                                                cell = grid[y][x];
636
637                                                if (x > maxX)
638                                                        maxX = x;
639
640                                                if (y > maxY)
641                                                        maxY = y;
642
643                                                if (cell.real) {
644                                                        colSpan = cell.colspan - 1;
645                                                        rowSpan = cell.rowspan - 1;
646
647                                                        if (colSpan) {
648                                                                if (x + colSpan > maxX)
649                                                                        maxX = x + colSpan;
650                                                        }
651
652                                                        if (rowSpan) {
653                                                                if (y + rowSpan > maxY)
654                                                                        maxY = y + rowSpan;
655                                                        }
656                                                }
657                                        }
658                                });
659                        });
660
661                        return {x : maxX, y : maxY};
662                };
663
664                function setEndCell(cell) {
665                        var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan;
666
667                        endPos = getPos(cell);
668
669                        if (startPos && endPos) {
670                                // Get start/end positions
671                                startX = Math.min(startPos.x, endPos.x);
672                                startY = Math.min(startPos.y, endPos.y);
673                                endX = Math.max(startPos.x, endPos.x);
674                                endY = Math.max(startPos.y, endPos.y);
675
676                                // Expand end positon to include spans
677                                maxX = endX;
678                                maxY = endY;
679
680                                // Expand startX
681                                for (y = startY; y <= maxY; y++) {
682                                        cell = grid[y][startX];
683
684                                        if (!cell.real) {
685                                                if (startX - (cell.colspan - 1) < startX)
686                                                        startX -= cell.colspan - 1;
687                                        }
688                                }
689
690                                // Expand startY
691                                for (x = startX; x <= maxX; x++) {
692                                        cell = grid[startY][x];
693
694                                        if (!cell.real) {
695                                                if (startY - (cell.rowspan - 1) < startY)
696                                                        startY -= cell.rowspan - 1;
697                                        }
698                                }
699
700                                // Find max X, Y
701                                for (y = startY; y <= endY; y++) {
702                                        for (x = startX; x <= endX; x++) {
703                                                cell = grid[y][x];
704
705                                                if (cell.real) {
706                                                        colSpan = cell.colspan - 1;
707                                                        rowSpan = cell.rowspan - 1;
708
709                                                        if (colSpan) {
710                                                                if (x + colSpan > maxX)
711                                                                        maxX = x + colSpan;
712                                                        }
713
714                                                        if (rowSpan) {
715                                                                if (y + rowSpan > maxY)
716                                                                        maxY = y + rowSpan;
717                                                        }
718                                                }
719                                        }
720                                }
721
722                                // Remove current selection
723                                dom.removeClass(dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
724
725                                // Add new selection
726                                for (y = startY; y <= maxY; y++) {
727                                        for (x = startX; x <= maxX; x++) {
728                                                if (grid[y][x])
729                                                        dom.addClass(grid[y][x].elm, 'mceSelected');
730                                        }
731                                }
732                        }
733                };
734
735                // Expose to public
736                tinymce.extend(this, {
737                        deleteTable : deleteTable,
738                        split : split,
739                        merge : merge,
740                        insertRow : insertRow,
741                        insertCol : insertCol,
742                        deleteCols : deleteCols,
743                        deleteRows : deleteRows,
744                        cutRows : cutRows,
745                        copyRows : copyRows,
746                        pasteRows : pasteRows,
747                        getPos : getPos,
748                        setStartCell : setStartCell,
749                        setEndCell : setEndCell
750                });
751        };
752
753        tinymce.create('tinymce.plugins.TablePlugin', {
754                init : function(ed, url) {
755                        var winMan, clipboardRows;
756
757                        function createTableGrid(node) {
758                                var selection = ed.selection, tblElm = ed.dom.getParent(node || selection.getNode(), 'table');
759
760                                if (tblElm)
761                                        return new TableGrid(tblElm, ed.dom, selection);
762                        };
763
764                        function cleanup() {
765                                // Restore selection possibilities
766                                ed.getBody().style.webkitUserSelect = '';
767                                ed.dom.removeClass(ed.dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
768                        };
769
770                        // Register buttons
771                        each([
772                                ['table', 'table.desc', 'mceInsertTable', true],
773                                ['delete_table', 'table.del', 'mceTableDelete'],
774                                ['delete_col', 'table.delete_col_desc', 'mceTableDeleteCol'],
775                                ['delete_row', 'table.delete_row_desc', 'mceTableDeleteRow'],
776                                ['col_after', 'table.col_after_desc', 'mceTableInsertColAfter'],
777                                ['col_before', 'table.col_before_desc', 'mceTableInsertColBefore'],
778                                ['row_after', 'table.row_after_desc', 'mceTableInsertRowAfter'],
779                                ['row_before', 'table.row_before_desc', 'mceTableInsertRowBefore'],
780                                ['row_props', 'table.row_desc', 'mceTableRowProps', true],
781                                ['cell_props', 'table.cell_desc', 'mceTableCellProps', true],
782                                ['split_cells', 'table.split_cells_desc', 'mceTableSplitCells', true],
783                                ['merge_cells', 'table.merge_cells_desc', 'mceTableMergeCells', true]
784                        ], function(c) {
785                                ed.addButton(c[0], {title : c[1], cmd : c[2], ui : c[3]});
786                        });
787
788                        // Select whole table is a table border is clicked
789                        if (!tinymce.isIE) {
790                                ed.onClick.add(function(ed, e) {
791                                        e = e.target;
792
793                                        if (e.nodeName === 'TABLE') {
794                                                ed.selection.select(e);
795                                                ed.nodeChanged();
796                                        }
797                                });
798                        }
799
800                        ed.onPreProcess.add(function(ed, args) {
801                                var nodes, i, node, dom = ed.dom, value;
802
803                                nodes = dom.select('table', args.node);
804                                i = nodes.length;
805                                while (i--) {
806                                        node = nodes[i];
807                                        dom.setAttrib(node, 'data-mce-style', '');
808
809                                        if ((value = dom.getAttrib(node, 'width'))) {
810                                                dom.setStyle(node, 'width', value);
811                                                dom.setAttrib(node, 'width', '');
812                                        }
813
814                                        if ((value = dom.getAttrib(node, 'height'))) {
815                                                dom.setStyle(node, 'height', value);
816                                                dom.setAttrib(node, 'height', '');
817                                        }
818                                }
819                        });
820
821                        // Handle node change updates
822                        ed.onNodeChange.add(function(ed, cm, n) {
823                                var p;
824
825                                n = ed.selection.getStart();
826                                p = ed.dom.getParent(n, 'td,th,caption');
827                                cm.setActive('table', n.nodeName === 'TABLE' || !!p);
828
829                                // Disable table tools if we are in caption
830                                if (p && p.nodeName === 'CAPTION')
831                                        p = 0;
832
833                                cm.setDisabled('delete_table', !p);
834                                cm.setDisabled('delete_col', !p);
835                                cm.setDisabled('delete_table', !p);
836                                cm.setDisabled('delete_row', !p);
837                                cm.setDisabled('col_after', !p);
838                                cm.setDisabled('col_before', !p);
839                                cm.setDisabled('row_after', !p);
840                                cm.setDisabled('row_before', !p);
841                                cm.setDisabled('row_props', !p);
842                                cm.setDisabled('cell_props', !p);
843                                cm.setDisabled('split_cells', !p);
844                                cm.setDisabled('merge_cells', !p);
845                        });
846
847                        ed.onInit.add(function(ed) {
848                                var startTable, startCell, dom = ed.dom, tableGrid;
849
850                                winMan = ed.windowManager;
851
852                                // Add cell selection logic
853                                ed.onMouseDown.add(function(ed, e) {
854                                        if (e.button != 2) {
855                                                cleanup();
856
857                                                startCell = dom.getParent(e.target, 'td,th');
858                                                startTable = dom.getParent(startCell, 'table');
859                                        }
860                                });
861
862                                dom.bind(ed.getDoc(), 'mouseover', function(e) {
863                                        var sel, table, target = e.target;
864
865                                        if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) {
866                                                table = dom.getParent(target, 'table');
867                                                if (table == startTable) {
868                                                        if (!tableGrid) {
869                                                                tableGrid = createTableGrid(table);
870                                                                tableGrid.setStartCell(startCell);
871
872                                                                ed.getBody().style.webkitUserSelect = 'none';
873                                                        }
874
875                                                        tableGrid.setEndCell(target);
876                                                }
877
878                                                // Remove current selection
879                                                sel = ed.selection.getSel();
880
881                                                try {
882                                                        if (sel.removeAllRanges)
883                                                                sel.removeAllRanges();
884                                                        else
885                                                                sel.empty();
886                                                } catch (ex) {
887                                                        // IE9 might throw errors here
888                                                }
889
890                                                e.preventDefault();
891                                        }
892                                });
893
894                                ed.onMouseUp.add(function(ed, e) {
895                                        var rng, sel = ed.selection, selectedCells, nativeSel = sel.getSel(), walker, node, lastNode, endNode;
896
897                                        // Move selection to startCell
898                                        if (startCell) {
899                                                if (tableGrid)
900                                                        ed.getBody().style.webkitUserSelect = '';
901
902                                                function setPoint(node, start) {
903                                                        var walker = new tinymce.dom.TreeWalker(node, node);
904
905                                                        do {
906                                                                // Text node
907                                                                if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {
908                                                                        if (start)
909                                                                                rng.setStart(node, 0);
910                                                                        else
911                                                                                rng.setEnd(node, node.nodeValue.length);
912
913                                                                        return;
914                                                                }
915
916                                                                // BR element
917                                                                if (node.nodeName == 'BR') {
918                                                                        if (start)
919                                                                                rng.setStartBefore(node);
920                                                                        else
921                                                                                rng.setEndBefore(node);
922
923                                                                        return;
924                                                                }
925                                                        } while (node = (start ? walker.next() : walker.prev()));
926                                                };
927
928                                                // Try to expand text selection as much as we can only Gecko supports cell selection
929                                                selectedCells = dom.select('td.mceSelected,th.mceSelected');
930                                                if (selectedCells.length > 0) {
931                                                        rng = dom.createRng();
932                                                        node = selectedCells[0];
933                                                        endNode = selectedCells[selectedCells.length - 1];
934
935                                                        setPoint(node, 1);
936                                                        walker = new tinymce.dom.TreeWalker(node, dom.getParent(selectedCells[0], 'table'));
937
938                                                        do {
939                                                                if (node.nodeName == 'TD' || node.nodeName == 'TH') {
940                                                                        if (!dom.hasClass(node, 'mceSelected'))
941                                                                                break;
942
943                                                                        lastNode = node;
944                                                                }
945                                                        } while (node = walker.next());
946
947                                                        setPoint(lastNode);
948
949                                                        sel.setRng(rng);
950                                                }
951
952                                                ed.nodeChanged();
953                                                startCell = tableGrid = startTable = null;
954                                        }
955                                });
956
957                                ed.onKeyUp.add(function(ed, e) {
958                                        cleanup();
959                                });
960
961                                // Add context menu
962                                if (ed && ed.plugins.contextmenu) {
963                                        ed.plugins.contextmenu.onContextMenu.add(function(th, m, e) {
964                                                var sm, se = ed.selection, el = se.getNode() || ed.getBody();
965
966                                                if (ed.dom.getParent(e, 'td') || ed.dom.getParent(e, 'th') || ed.dom.select('td.mceSelected,th.mceSelected').length) {
967                                                        m.removeAll();
968
969                                                        if (el.nodeName == 'A' && !ed.dom.getAttrib(el, 'name')) {
970                                                                m.add({title : 'advanced.link_desc', icon : 'link', cmd : ed.plugins.advlink ? 'mceAdvLink' : 'mceLink', ui : true});
971                                                                m.add({title : 'advanced.unlink_desc', icon : 'unlink', cmd : 'UnLink'});
972                                                                m.addSeparator();
973                                                        }
974
975                                                        if (el.nodeName == 'IMG' && el.className.indexOf('mceItem') == -1) {
976                                                                m.add({title : 'advanced.image_desc', icon : 'image', cmd : ed.plugins.advimage ? 'mceAdvImage' : 'mceImage', ui : true});
977                                                                m.addSeparator();
978                                                        }
979
980                                                        m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable', value : {action : 'insert'}});
981                                                        m.add({title : 'table.props_desc', icon : 'table_props', cmd : 'mceInsertTable'});
982                                                        m.add({title : 'table.del', icon : 'delete_table', cmd : 'mceTableDelete'});
983                                                        m.addSeparator();
984
985                                                        // Cell menu
986                                                        sm = m.addMenu({title : 'table.cell'});
987                                                        sm.add({title : 'table.cell_desc', icon : 'cell_props', cmd : 'mceTableCellProps'});
988                                                        sm.add({title : 'table.split_cells_desc', icon : 'split_cells', cmd : 'mceTableSplitCells'});
989                                                        sm.add({title : 'table.merge_cells_desc', icon : 'merge_cells', cmd : 'mceTableMergeCells'});
990
991                                                        // Row menu
992                                                        sm = m.addMenu({title : 'table.row'});
993                                                        sm.add({title : 'table.row_desc', icon : 'row_props', cmd : 'mceTableRowProps'});
994                                                        sm.add({title : 'table.row_before_desc', icon : 'row_before', cmd : 'mceTableInsertRowBefore'});
995                                                        sm.add({title : 'table.row_after_desc', icon : 'row_after', cmd : 'mceTableInsertRowAfter'});
996                                                        sm.add({title : 'table.delete_row_desc', icon : 'delete_row', cmd : 'mceTableDeleteRow'});
997                                                        sm.addSeparator();
998                                                        sm.add({title : 'table.cut_row_desc', icon : 'cut', cmd : 'mceTableCutRow'});
999                                                        sm.add({title : 'table.copy_row_desc', icon : 'copy', cmd : 'mceTableCopyRow'});
1000                                                        sm.add({title : 'table.paste_row_before_desc', icon : 'paste', cmd : 'mceTablePasteRowBefore'}).setDisabled(!clipboardRows);
1001                                                        sm.add({title : 'table.paste_row_after_desc', icon : 'paste', cmd : 'mceTablePasteRowAfter'}).setDisabled(!clipboardRows);
1002
1003                                                        // Column menu
1004                                                        sm = m.addMenu({title : 'table.col'});
1005                                                        sm.add({title : 'table.col_before_desc', icon : 'col_before', cmd : 'mceTableInsertColBefore'});
1006                                                        sm.add({title : 'table.col_after_desc', icon : 'col_after', cmd : 'mceTableInsertColAfter'});
1007                                                        sm.add({title : 'table.delete_col_desc', icon : 'delete_col', cmd : 'mceTableDeleteCol'});
1008                                                } else
1009                                                        m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable'});
1010                                        });
1011                                }
1012
1013                                // Fixes an issue on Gecko where it's impossible to place the caret behind a table
1014                                // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
1015                                if (!tinymce.isIE) {
1016                                        function fixTableCaretPos() {
1017                                                var last;
1018
1019                                                // Skip empty text nodes form the end
1020                                                for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ;
1021
1022                                                if (last && last.nodeName == 'TABLE')
1023                                                        ed.dom.add(ed.getBody(), 'p', null, '<br mce_bogus="1" />');
1024                                        };
1025
1026                                        // Fixes an bug where it's impossible to place the caret before a table in Gecko
1027                                        // this fix solves it by detecting when the caret is at the beginning of such a table
1028                                        // and then manually moves the caret infront of the table
1029                                        if (tinymce.isGecko) {
1030                                                ed.onKeyDown.add(function(ed, e) {
1031                                                        var rng, table, dom = ed.dom;
1032
1033                                                        // On gecko it's not possible to place the caret before a table
1034                                                        if (e.keyCode == 37 || e.keyCode == 38) {
1035                                                                rng = ed.selection.getRng();
1036                                                                table = dom.getParent(rng.startContainer, 'table');
1037
1038                                                                if (table && ed.getBody().firstChild == table) {
1039                                                                        if (isAtStart(rng, table)) {
1040                                                                                rng = dom.createRng();
1041
1042                                                                                rng.setStartBefore(table);
1043                                                                                rng.setEndBefore(table);
1044
1045                                                                                ed.selection.setRng(rng);
1046
1047                                                                                e.preventDefault();
1048                                                                        }
1049                                                                }
1050                                                        }
1051                                                });
1052                                        }
1053
1054                                        ed.onKeyUp.add(fixTableCaretPos);
1055                                        ed.onSetContent.add(fixTableCaretPos);
1056                                        ed.onVisualAid.add(fixTableCaretPos);
1057
1058                                        ed.onPreProcess.add(function(ed, o) {
1059                                                var last = o.node.lastChild;
1060
1061                                                if (last && last.childNodes.length == 1 && last.firstChild.nodeName == 'BR')
1062                                                        ed.dom.remove(last);
1063                                        });
1064
1065                                        fixTableCaretPos();
1066                                }
1067                        });
1068
1069                        // Register action commands
1070                        each({
1071                                mceTableSplitCells : function(grid) {
1072                                        grid.split();
1073                                },
1074
1075                                mceTableMergeCells : function(grid) {
1076                                        var rowSpan, colSpan, cell;
1077
1078                                        cell = ed.dom.getParent(ed.selection.getNode(), 'th,td');
1079                                        if (cell) {
1080                                                rowSpan = cell.rowSpan;
1081                                                colSpan = cell.colSpan;
1082                                        }
1083
1084                                        if (!ed.dom.select('td.mceSelected,th.mceSelected').length) {
1085                                                winMan.open({
1086                                                        url : url + '/merge_cells.htm',
1087                                                        width : 240 + parseInt(ed.getLang('table.merge_cells_delta_width', 0)),
1088                                                        height : 110 + parseInt(ed.getLang('table.merge_cells_delta_height', 0)),
1089                                                        inline : 1
1090                                                }, {
1091                                                        rows : rowSpan,
1092                                                        cols : colSpan,
1093                                                        onaction : function(data) {
1094                                                                grid.merge(cell, data.cols, data.rows);
1095                                                        },
1096                                                        plugin_url : url
1097                                                });
1098                                        } else
1099                                                grid.merge();
1100                                },
1101
1102                                mceTableInsertRowBefore : function(grid) {
1103                                        grid.insertRow(true);
1104                                },
1105
1106                                mceTableInsertRowAfter : function(grid) {
1107                                        grid.insertRow();
1108                                },
1109
1110                                mceTableInsertColBefore : function(grid) {
1111                                        grid.insertCol(true);
1112                                },
1113
1114                                mceTableInsertColAfter : function(grid) {
1115                                        grid.insertCol();
1116                                },
1117
1118                                mceTableDeleteCol : function(grid) {
1119                                        grid.deleteCols();
1120                                },
1121
1122                                mceTableDeleteRow : function(grid) {
1123                                        grid.deleteRows();
1124                                },
1125
1126                                mceTableCutRow : function(grid) {
1127                                        clipboardRows = grid.cutRows();
1128                                },
1129
1130                                mceTableCopyRow : function(grid) {
1131                                        clipboardRows = grid.copyRows();
1132                                },
1133
1134                                mceTablePasteRowBefore : function(grid) {
1135                                        grid.pasteRows(clipboardRows, true);
1136                                },
1137
1138                                mceTablePasteRowAfter : function(grid) {
1139                                        grid.pasteRows(clipboardRows);
1140                                },
1141
1142                                mceTableDelete : function(grid) {
1143                                        grid.deleteTable();
1144                                }
1145                        }, function(func, name) {
1146                                ed.addCommand(name, function() {
1147                                        var grid = createTableGrid();
1148
1149                                        if (grid) {
1150                                                func(grid);
1151                                                ed.execCommand('mceRepaint');
1152                                                cleanup();
1153                                        }
1154                                });
1155                        });
1156
1157                        // Register dialog commands
1158                        each({
1159                                mceInsertTable : function(val) {
1160                                        winMan.open({
1161                                                url : url + '/table.htm',
1162                                                width : 400 + parseInt(ed.getLang('table.table_delta_width', 0)),
1163                                                height : 320 + parseInt(ed.getLang('table.table_delta_height', 0)),
1164                                                inline : 1
1165                                        }, {
1166                                                plugin_url : url,
1167                                                action : val ? val.action : 0
1168                                        });
1169                                },
1170
1171                                mceTableRowProps : function() {
1172                                        winMan.open({
1173                                                url : url + '/row.htm',
1174                                                width : 400 + parseInt(ed.getLang('table.rowprops_delta_width', 0)),
1175                                                height : 295 + parseInt(ed.getLang('table.rowprops_delta_height', 0)),
1176                                                inline : 1
1177                                        }, {
1178                                                plugin_url : url
1179                                        });
1180                                },
1181
1182                                mceTableCellProps : function() {
1183                                        winMan.open({
1184                                                url : url + '/cell.htm',
1185                                                width : 400 + parseInt(ed.getLang('table.cellprops_delta_width', 0)),
1186                                                height : 295 + parseInt(ed.getLang('table.cellprops_delta_height', 0)),
1187                                                inline : 1
1188                                        }, {
1189                                                plugin_url : url
1190                                        });
1191                                }
1192                        }, function(func, name) {
1193                                ed.addCommand(name, function(ui, val) {
1194                                        func(val);
1195                                });
1196                        });
1197                }
1198        });
1199
1200        // Register plugin
1201        tinymce.PluginManager.add('table', tinymce.plugins.TablePlugin);
1202})(tinymce);
Note: See TracBrowser for help on using the repository browser.