import mtweb.0.4.1
[mtweb] / web / libs / tiny_mce / plugins / table / editor_plugin_src.js
1 /**\r
2  * editor_plugin_src.js\r
3  *\r
4  * Copyright 2009, Moxiecode Systems AB\r
5  * Released under LGPL License.\r
6  *\r
7  * License: http://tinymce.moxiecode.com/license\r
8  * Contributing: http://tinymce.moxiecode.com/contributing\r
9  */\r
10 \r
11 (function(tinymce) {\r
12         var each = tinymce.each;\r
13 \r
14         /**\r
15          * Table Grid class.\r
16          */\r
17         function TableGrid(table, dom, selection) {\r
18                 var grid, startPos, endPos, selectedCell;\r
19 \r
20                 buildGrid();\r
21                 selectedCell = dom.getParent(selection.getStart(), 'th,td');\r
22                 if (selectedCell) {\r
23                         startPos = getPos(selectedCell);\r
24                         endPos = findEndPos();\r
25                         selectedCell = getCell(startPos.x, startPos.y);\r
26                 }\r
27 \r
28                 function cloneNode(node, children) {\r
29                         node = node.cloneNode(children);\r
30                         node.removeAttribute('id');\r
31 \r
32                         return node;\r
33                 }\r
34 \r
35                 function buildGrid() {\r
36                         var startY = 0;\r
37 \r
38                         grid = [];\r
39 \r
40                         each(['thead', 'tbody', 'tfoot'], function(part) {\r
41                                 var rows = dom.select(part + ' tr', table);\r
42 \r
43                                 each(rows, function(tr, y) {\r
44                                         y += startY;\r
45 \r
46                                         each(dom.select('td,th', tr), function(td, x) {\r
47                                                 var x2, y2, rowspan, colspan;\r
48 \r
49                                                 // Skip over existing cells produced by rowspan\r
50                                                 if (grid[y]) {\r
51                                                         while (grid[y][x])\r
52                                                                 x++;\r
53                                                 }\r
54 \r
55                                                 // Get col/rowspan from cell\r
56                                                 rowspan = getSpanVal(td, 'rowspan');\r
57                                                 colspan = getSpanVal(td, 'colspan');\r
58 \r
59                                                 // Fill out rowspan/colspan right and down\r
60                                                 for (y2 = y; y2 < y + rowspan; y2++) {\r
61                                                         if (!grid[y2])\r
62                                                                 grid[y2] = [];\r
63 \r
64                                                         for (x2 = x; x2 < x + colspan; x2++) {\r
65                                                                 grid[y2][x2] = {\r
66                                                                         part : part,\r
67                                                                         real : y2 == y && x2 == x,\r
68                                                                         elm : td,\r
69                                                                         rowspan : rowspan,\r
70                                                                         colspan : colspan\r
71                                                                 };\r
72                                                         }\r
73                                                 }\r
74                                         });\r
75                                 });\r
76 \r
77                                 startY += rows.length;\r
78                         });\r
79                 };\r
80 \r
81                 function getCell(x, y) {\r
82                         var row;\r
83 \r
84                         row = grid[y];\r
85                         if (row)\r
86                                 return row[x];\r
87                 };\r
88 \r
89                 function getSpanVal(td, name) {\r
90                         return parseInt(td.getAttribute(name) || 1);\r
91                 };\r
92 \r
93                 function isCellSelected(cell) {\r
94                         return dom.hasClass(cell.elm, 'mceSelected') || cell == selectedCell;\r
95                 };\r
96 \r
97                 function getSelectedRows() {\r
98                         var rows = [];\r
99 \r
100                         each(table.rows, function(row) {\r
101                                 each(row.cells, function(cell) {\r
102                                         if (dom.hasClass(cell, 'mceSelected') || cell == selectedCell.elm) {\r
103                                                 rows.push(row);\r
104                                                 return false;\r
105                                         }\r
106                                 });\r
107                         });\r
108 \r
109                         return rows;\r
110                 };\r
111 \r
112                 function deleteTable() {\r
113                         var rng = dom.createRng();\r
114 \r
115                         rng.setStartAfter(table);\r
116                         rng.setEndAfter(table);\r
117 \r
118                         selection.setRng(rng);\r
119 \r
120                         dom.remove(table);\r
121                 };\r
122 \r
123                 function cloneCell(cell) {\r
124                         var formatNode;\r
125 \r
126                         // Clone formats\r
127                         tinymce.walk(cell, function(node) {\r
128                                 var curNode;\r
129 \r
130                                 if (node.nodeType == 3) {\r
131                                         each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {\r
132                                                 node = cloneNode(node, false);\r
133 \r
134                                                 if (!formatNode)\r
135                                                         formatNode = curNode = node;\r
136                                                 else if (curNode)\r
137                                                         curNode.appendChild(node);\r
138 \r
139                                                 curNode = node;\r
140                                         });\r
141 \r
142                                         // Add something to the inner node\r
143                                         if (curNode)\r
144                                                 curNode.innerHTML = tinymce.isIE ? '&nbsp;' : '<br _mce_bogus="1" />';\r
145 \r
146                                         return false;\r
147                                 }\r
148                         }, 'childNodes');\r
149 \r
150                         cell = cloneNode(cell, false);\r
151                         cell.rowSpan = cell.colSpan = 1;\r
152 \r
153                         if (formatNode) {\r
154                                 cell.appendChild(formatNode);\r
155                         } else {\r
156                                 if (!tinymce.isIE)\r
157                                         cell.innerHTML = '<br _mce_bogus="1" />';\r
158                         }\r
159 \r
160                         return cell;\r
161                 };\r
162 \r
163                 function cleanup() {\r
164                         var rng = dom.createRng();\r
165 \r
166                         // Empty rows\r
167                         each(dom.select('tr', table), function(tr) {\r
168                                 if (tr.cells.length == 0)\r
169                                         dom.remove(tr);\r
170                         });\r
171 \r
172                         // Empty table\r
173                         if (dom.select('tr', table).length == 0) {\r
174                                 rng.setStartAfter(table);\r
175                                 rng.setEndAfter(table);\r
176                                 selection.setRng(rng);\r
177                                 dom.remove(table);\r
178                                 return;\r
179                         }\r
180 \r
181                         // Empty header/body/footer\r
182                         each(dom.select('thead,tbody,tfoot', table), function(part) {\r
183                                 if (part.rows.length == 0)\r
184                                         dom.remove(part);\r
185                         });\r
186 \r
187                         // Restore selection to start position if it still exists\r
188                         buildGrid();\r
189 \r
190                         // Restore the selection to the closest table position\r
191                         row = grid[Math.min(grid.length - 1, startPos.y)];\r
192                         if (row) {\r
193                                 selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true);\r
194                                 selection.collapse(true);\r
195                         }\r
196                 };\r
197 \r
198                 function fillLeftDown(x, y, rows, cols) {\r
199                         var tr, x2, r, c, cell;\r
200 \r
201                         tr = grid[y][x].elm.parentNode;\r
202                         for (r = 1; r <= rows; r++) {\r
203                                 tr = dom.getNext(tr, 'tr');\r
204 \r
205                                 if (tr) {\r
206                                         // Loop left to find real cell\r
207                                         for (x2 = x; x2 >= 0; x2--) {\r
208                                                 cell = grid[y + r][x2].elm;\r
209 \r
210                                                 if (cell.parentNode == tr) {\r
211                                                         // Append clones after\r
212                                                         for (c = 1; c <= cols; c++)\r
213                                                                 dom.insertAfter(cloneCell(cell), cell);\r
214 \r
215                                                         break;\r
216                                                 }\r
217                                         }\r
218 \r
219                                         if (x2 == -1) {\r
220                                                 // Insert nodes before first cell\r
221                                                 for (c = 1; c <= cols; c++)\r
222                                                         tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]);\r
223                                         }\r
224                                 }\r
225                         }\r
226                 };\r
227 \r
228                 function split() {\r
229                         each(grid, function(row, y) {\r
230                                 each(row, function(cell, x) {\r
231                                         var colSpan, rowSpan, newCell, i;\r
232 \r
233                                         if (isCellSelected(cell)) {\r
234                                                 cell = cell.elm;\r
235                                                 colSpan = getSpanVal(cell, 'colspan');\r
236                                                 rowSpan = getSpanVal(cell, 'rowspan');\r
237 \r
238                                                 if (colSpan > 1 || rowSpan > 1) {\r
239                                                         cell.colSpan = cell.rowSpan = 1;\r
240 \r
241                                                         // Insert cells right\r
242                                                         for (i = 0; i < colSpan - 1; i++)\r
243                                                                 dom.insertAfter(cloneCell(cell), cell);\r
244 \r
245                                                         fillLeftDown(x, y, rowSpan - 1, colSpan);\r
246                                                 }\r
247                                         }\r
248                                 });\r
249                         });\r
250                 };\r
251 \r
252                 function merge(cell, cols, rows) {\r
253                         var startX, startY, endX, endY, x, y, startCell, endCell, cell, children;\r
254 \r
255                         // Use specified cell and cols/rows\r
256                         if (cell) {\r
257                                 pos = getPos(cell);\r
258                                 startX = pos.x;\r
259                                 startY = pos.y;\r
260                                 endX = startX + (cols - 1);\r
261                                 endY = startY + (rows - 1);\r
262                         } else {\r
263                                 // Use selection\r
264                                 startX = startPos.x;\r
265                                 startY = startPos.y;\r
266                                 endX = endPos.x;\r
267                                 endY = endPos.y;\r
268                         }\r
269 \r
270                         // Find start/end cells\r
271                         startCell = getCell(startX, startY);\r
272                         endCell = getCell(endX, endY);\r
273 \r
274                         // Check if the cells exists and if they are of the same part for example tbody = tbody\r
275                         if (startCell && endCell && startCell.part == endCell.part) {\r
276                                 // Split and rebuild grid\r
277                                 split();\r
278                                 buildGrid();\r
279 \r
280                                 // Set row/col span to start cell\r
281                                 startCell = getCell(startX, startY).elm;\r
282                                 startCell.colSpan = (endX - startX) + 1;\r
283                                 startCell.rowSpan = (endY - startY) + 1;\r
284 \r
285                                 // Remove other cells and add it's contents to the start cell\r
286                                 for (y = startY; y <= endY; y++) {\r
287                                         for (x = startX; x <= endX; x++) {\r
288                                                 cell = grid[y][x].elm;\r
289 \r
290                                                 if (cell != startCell) {\r
291                                                         // Move children to startCell\r
292                                                         children = tinymce.grep(cell.childNodes);\r
293                                                         each(children, function(node, i) {\r
294                                                                 // Jump over last BR element\r
295                                                                 if (node.nodeName != 'BR' || i != children.length - 1)\r
296                                                                         startCell.appendChild(node);\r
297                                                         });\r
298 \r
299                                                         // Remove cell\r
300                                                         dom.remove(cell);\r
301                                                 }\r
302                                         }\r
303                                 }\r
304 \r
305                                 // Remove empty rows etc and restore caret location\r
306                                 cleanup();\r
307                         }\r
308                 };\r
309 \r
310                 function insertRow(before) {\r
311                         var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell;\r
312 \r
313                         // Find first/last row\r
314                         each(grid, function(row, y) {\r
315                                 each(row, function(cell, x) {\r
316                                         if (isCellSelected(cell)) {\r
317                                                 cell = cell.elm;\r
318                                                 rowElm = cell.parentNode;\r
319                                                 newRow = cloneNode(rowElm, false);\r
320                                                 posY = y;\r
321 \r
322                                                 if (before)\r
323                                                         return false;\r
324                                         }\r
325                                 });\r
326 \r
327                                 if (before)\r
328                                         return !posY;\r
329                         });\r
330 \r
331                         for (x = 0; x < grid[0].length; x++) {\r
332                                 cell = grid[posY][x].elm;\r
333 \r
334                                 if (cell != lastCell) {\r
335                                         if (!before) {\r
336                                                 rowSpan = getSpanVal(cell, 'rowspan');\r
337                                                 if (rowSpan > 1) {\r
338                                                         cell.rowSpan = rowSpan + 1;\r
339                                                         continue;\r
340                                                 }\r
341                                         } else {\r
342                                                 // Check if cell above can be expanded\r
343                                                 if (posY > 0 && grid[posY - 1][x]) {\r
344                                                         otherCell = grid[posY - 1][x].elm;\r
345                                                         rowSpan = getSpanVal(otherCell, 'rowspan');\r
346                                                         if (rowSpan > 1) {\r
347                                                                 otherCell.rowSpan = rowSpan + 1;\r
348                                                                 continue;\r
349                                                         }\r
350                                                 }\r
351                                         }\r
352 \r
353                                         // Insert new cell into new row\r
354                                         newCell = cloneCell(cell)\r
355                                         newCell.colSpan = cell.colSpan;\r
356                                         newRow.appendChild(newCell);\r
357 \r
358                                         lastCell = cell;\r
359                                 }\r
360                         }\r
361 \r
362                         if (newRow.hasChildNodes()) {\r
363                                 if (!before)\r
364                                         dom.insertAfter(newRow, rowElm);\r
365                                 else\r
366                                         rowElm.parentNode.insertBefore(newRow, rowElm);\r
367                         }\r
368                 };\r
369 \r
370                 function insertCol(before) {\r
371                         var posX, lastCell;\r
372 \r
373                         // Find first/last column\r
374                         each(grid, function(row, y) {\r
375                                 each(row, function(cell, x) {\r
376                                         if (isCellSelected(cell)) {\r
377                                                 posX = x;\r
378 \r
379                                                 if (before)\r
380                                                         return false;\r
381                                         }\r
382                                 });\r
383 \r
384                                 if (before)\r
385                                         return !posX;\r
386                         });\r
387 \r
388                         each(grid, function(row, y) {\r
389                                 var cell = row[posX].elm, rowSpan, colSpan;\r
390 \r
391                                 if (cell != lastCell) {\r
392                                         colSpan = getSpanVal(cell, 'colspan');\r
393                                         rowSpan = getSpanVal(cell, 'rowspan');\r
394 \r
395                                         if (colSpan == 1) {\r
396                                                 if (!before) {\r
397                                                         dom.insertAfter(cloneCell(cell), cell);\r
398                                                         fillLeftDown(posX, y, rowSpan - 1, colSpan);\r
399                                                 } else {\r
400                                                         cell.parentNode.insertBefore(cloneCell(cell), cell);\r
401                                                         fillLeftDown(posX, y, rowSpan - 1, colSpan);\r
402                                                 }\r
403                                         } else\r
404                                                 cell.colSpan++;\r
405 \r
406                                         lastCell = cell;\r
407                                 }\r
408                         });\r
409                 };\r
410 \r
411                 function deleteCols() {\r
412                         var cols = [];\r
413 \r
414                         // Get selected column indexes\r
415                         each(grid, function(row, y) {\r
416                                 each(row, function(cell, x) {\r
417                                         if (isCellSelected(cell) && tinymce.inArray(cols, x) === -1) {\r
418                                                 each(grid, function(row) {\r
419                                                         var cell = row[x].elm, colSpan;\r
420 \r
421                                                         colSpan = getSpanVal(cell, 'colspan');\r
422 \r
423                                                         if (colSpan > 1)\r
424                                                                 cell.colSpan = colSpan - 1;\r
425                                                         else\r
426                                                                 dom.remove(cell);\r
427                                                 });\r
428 \r
429                                                 cols.push(x);\r
430                                         }\r
431                                 });\r
432                         });\r
433 \r
434                         cleanup();\r
435                 };\r
436 \r
437                 function deleteRows() {\r
438                         var rows;\r
439 \r
440                         function deleteRow(tr) {\r
441                                 var nextTr, pos, lastCell;\r
442 \r
443                                 nextTr = dom.getNext(tr, 'tr');\r
444 \r
445                                 // Move down row spanned cells\r
446                                 each(tr.cells, function(cell) {\r
447                                         var rowSpan = getSpanVal(cell, 'rowspan');\r
448 \r
449                                         if (rowSpan > 1) {\r
450                                                 cell.rowSpan = rowSpan - 1;\r
451                                                 pos = getPos(cell);\r
452                                                 fillLeftDown(pos.x, pos.y, 1, 1);\r
453                                         }\r
454                                 });\r
455 \r
456                                 // Delete cells\r
457                                 pos = getPos(tr.cells[0]);\r
458                                 each(grid[pos.y], function(cell) {\r
459                                         var rowSpan;\r
460 \r
461                                         cell = cell.elm;\r
462 \r
463                                         if (cell != lastCell) {\r
464                                                 rowSpan = getSpanVal(cell, 'rowspan');\r
465 \r
466                                                 if (rowSpan <= 1)\r
467                                                         dom.remove(cell);\r
468                                                 else\r
469                                                         cell.rowSpan = rowSpan - 1;\r
470 \r
471                                                 lastCell = cell;\r
472                                         }\r
473                                 });\r
474                         };\r
475 \r
476                         // Get selected rows and move selection out of scope\r
477                         rows = getSelectedRows();\r
478 \r
479                         // Delete all selected rows\r
480                         each(rows.reverse(), function(tr) {\r
481                                 deleteRow(tr);\r
482                         });\r
483 \r
484                         cleanup();\r
485                 };\r
486 \r
487                 function cutRows() {\r
488                         var rows = getSelectedRows();\r
489 \r
490                         dom.remove(rows);\r
491                         cleanup();\r
492 \r
493                         return rows;\r
494                 };\r
495 \r
496                 function copyRows() {\r
497                         var rows = getSelectedRows();\r
498 \r
499                         each(rows, function(row, i) {\r
500                                 rows[i] = cloneNode(row, true);\r
501                         });\r
502 \r
503                         return rows;\r
504                 };\r
505 \r
506                 function pasteRows(rows, before) {\r
507                         var selectedRows = getSelectedRows(),\r
508                                 targetRow = selectedRows[before ? 0 : selectedRows.length - 1],\r
509                                 targetCellCount = targetRow.cells.length;\r
510 \r
511                         // Calc target cell count\r
512                         each(grid, function(row) {\r
513                                 var match;\r
514 \r
515                                 targetCellCount = 0;\r
516                                 each(row, function(cell, x) {\r
517                                         if (cell.real)\r
518                                                 targetCellCount += cell.colspan;\r
519 \r
520                                         if (cell.elm.parentNode == targetRow)\r
521                                                 match = 1;\r
522                                 });\r
523 \r
524                                 if (match)\r
525                                         return false;\r
526                         });\r
527 \r
528                         if (!before)\r
529                                 rows.reverse();\r
530 \r
531                         each(rows, function(row) {\r
532                                 var cellCount = row.cells.length, cell;\r
533 \r
534                                 // Remove col/rowspans\r
535                                 for (i = 0; i < cellCount; i++) {\r
536                                         cell = row.cells[i];\r
537                                         cell.colSpan = cell.rowSpan = 1;\r
538                                 }\r
539 \r
540                                 // Needs more cells\r
541                                 for (i = cellCount; i < targetCellCount; i++)\r
542                                         row.appendChild(cloneCell(row.cells[cellCount - 1]));\r
543 \r
544                                 // Needs less cells\r
545                                 for (i = targetCellCount; i < cellCount; i++)\r
546                                         dom.remove(row.cells[i]);\r
547 \r
548                                 // Add before/after\r
549                                 if (before)\r
550                                         targetRow.parentNode.insertBefore(row, targetRow);\r
551                                 else\r
552                                         dom.insertAfter(row, targetRow);\r
553                         });\r
554                 };\r
555 \r
556                 function getPos(target) {\r
557                         var pos;\r
558 \r
559                         each(grid, function(row, y) {\r
560                                 each(row, function(cell, x) {\r
561                                         if (cell.elm == target) {\r
562                                                 pos = {x : x, y : y};\r
563                                                 return false;\r
564                                         }\r
565                                 });\r
566 \r
567                                 return !pos;\r
568                         });\r
569 \r
570                         return pos;\r
571                 };\r
572 \r
573                 function setStartCell(cell) {\r
574                         startPos = getPos(cell);\r
575                 };\r
576 \r
577                 function findEndPos() {\r
578                         var pos, maxX, maxY;\r
579 \r
580                         maxX = maxY = 0;\r
581 \r
582                         each(grid, function(row, y) {\r
583                                 each(row, function(cell, x) {\r
584                                         var colSpan, rowSpan;\r
585 \r
586                                         if (isCellSelected(cell)) {\r
587                                                 cell = grid[y][x];\r
588 \r
589                                                 if (x > maxX)\r
590                                                         maxX = x;\r
591 \r
592                                                 if (y > maxY)\r
593                                                         maxY = y;\r
594 \r
595                                                 if (cell.real) {\r
596                                                         colSpan = cell.colspan - 1;\r
597                                                         rowSpan = cell.rowspan - 1;\r
598 \r
599                                                         if (colSpan) {\r
600                                                                 if (x + colSpan > maxX)\r
601                                                                         maxX = x + colSpan;\r
602                                                         }\r
603 \r
604                                                         if (rowSpan) {\r
605                                                                 if (y + rowSpan > maxY)\r
606                                                                         maxY = y + rowSpan;\r
607                                                         }\r
608                                                 }\r
609                                         }\r
610                                 });\r
611                         });\r
612 \r
613                         return {x : maxX, y : maxY};\r
614                 };\r
615 \r
616                 function setEndCell(cell) {\r
617                         var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan;\r
618 \r
619                         endPos = getPos(cell);\r
620 \r
621                         if (startPos && endPos) {\r
622                                 // Get start/end positions\r
623                                 startX = Math.min(startPos.x, endPos.x);\r
624                                 startY = Math.min(startPos.y, endPos.y);\r
625                                 endX = Math.max(startPos.x, endPos.x);\r
626                                 endY = Math.max(startPos.y, endPos.y);\r
627 \r
628                                 // Expand end positon to include spans\r
629                                 maxX = endX;\r
630                                 maxY = endY;\r
631 \r
632                                 // Expand startX\r
633                                 for (y = startY; y <= maxY; y++) {\r
634                                         cell = grid[y][startX];\r
635 \r
636                                         if (!cell.real) {\r
637                                                 if (startX - (cell.colspan - 1) < startX)\r
638                                                         startX -= cell.colspan - 1;\r
639                                         }\r
640                                 }\r
641 \r
642                                 // Expand startY\r
643                                 for (x = startX; x <= maxX; x++) {\r
644                                         cell = grid[startY][x];\r
645 \r
646                                         if (!cell.real) {\r
647                                                 if (startY - (cell.rowspan - 1) < startY)\r
648                                                         startY -= cell.rowspan - 1;\r
649                                         }\r
650                                 }\r
651 \r
652                                 // Find max X, Y\r
653                                 for (y = startY; y <= endY; y++) {\r
654                                         for (x = startX; x <= endX; x++) {\r
655                                                 cell = grid[y][x];\r
656 \r
657                                                 if (cell.real) {\r
658                                                         colSpan = cell.colspan - 1;\r
659                                                         rowSpan = cell.rowspan - 1;\r
660 \r
661                                                         if (colSpan) {\r
662                                                                 if (x + colSpan > maxX)\r
663                                                                         maxX = x + colSpan;\r
664                                                         }\r
665 \r
666                                                         if (rowSpan) {\r
667                                                                 if (y + rowSpan > maxY)\r
668                                                                         maxY = y + rowSpan;\r
669                                                         }\r
670                                                 }\r
671                                         }\r
672                                 }\r
673 \r
674                                 // Remove current selection\r
675                                 dom.removeClass(dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');\r
676 \r
677                                 // Add new selection\r
678                                 for (y = startY; y <= maxY; y++) {\r
679                                         for (x = startX; x <= maxX; x++)\r
680                                                 dom.addClass(grid[y][x].elm, 'mceSelected');\r
681                                 }\r
682                         }\r
683                 };\r
684 \r
685                 // Expose to public\r
686                 tinymce.extend(this, {\r
687                         deleteTable : deleteTable,\r
688                         split : split,\r
689                         merge : merge,\r
690                         insertRow : insertRow,\r
691                         insertCol : insertCol,\r
692                         deleteCols : deleteCols,\r
693                         deleteRows : deleteRows,\r
694                         cutRows : cutRows,\r
695                         copyRows : copyRows,\r
696                         pasteRows : pasteRows,\r
697                         getPos : getPos,\r
698                         setStartCell : setStartCell,\r
699                         setEndCell : setEndCell\r
700                 });\r
701         };\r
702 \r
703         tinymce.create('tinymce.plugins.TablePlugin', {\r
704                 init : function(ed, url) {\r
705                         var winMan, clipboardRows;\r
706 \r
707                         function createTableGrid(node) {\r
708                                 var selection = ed.selection, tblElm = ed.dom.getParent(node || selection.getNode(), 'table');\r
709 \r
710                                 if (tblElm)\r
711                                         return new TableGrid(tblElm, ed.dom, selection);\r
712                         };\r
713 \r
714                         function cleanup() {\r
715                                 // Restore selection possibilities\r
716                                 ed.getBody().style.webkitUserSelect = '';\r
717                                 ed.dom.removeClass(ed.dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');\r
718                         };\r
719 \r
720                         // Register buttons\r
721                         each([\r
722                                 ['table', 'table.desc', 'mceInsertTable', true],\r
723                                 ['delete_table', 'table.del', 'mceTableDelete'],\r
724                                 ['delete_col', 'table.delete_col_desc', 'mceTableDeleteCol'],\r
725                                 ['delete_row', 'table.delete_row_desc', 'mceTableDeleteRow'],\r
726                                 ['col_after', 'table.col_after_desc', 'mceTableInsertColAfter'],\r
727                                 ['col_before', 'table.col_before_desc', 'mceTableInsertColBefore'],\r
728                                 ['row_after', 'table.row_after_desc', 'mceTableInsertRowAfter'],\r
729                                 ['row_before', 'table.row_before_desc', 'mceTableInsertRowBefore'],\r
730                                 ['row_props', 'table.row_desc', 'mceTableRowProps', true],\r
731                                 ['cell_props', 'table.cell_desc', 'mceTableCellProps', true],\r
732                                 ['split_cells', 'table.split_cells_desc', 'mceTableSplitCells', true],\r
733                                 ['merge_cells', 'table.merge_cells_desc', 'mceTableMergeCells', true]\r
734                         ], function(c) {\r
735                                 ed.addButton(c[0], {title : c[1], cmd : c[2], ui : c[3]});\r
736                         });\r
737 \r
738                         // Select whole table is a table border is clicked\r
739                         if (!tinymce.isIE) {\r
740                                 ed.onClick.add(function(ed, e) {\r
741                                         e = e.target;\r
742 \r
743                                         if (e.nodeName === 'TABLE')\r
744                                                 ed.selection.select(e);\r
745                                 });\r
746                         }\r
747 \r
748                         // Handle node change updates\r
749                         ed.onNodeChange.add(function(ed, cm, n) {\r
750                                 var p;\r
751 \r
752                                 n = ed.selection.getStart();\r
753                                 p = ed.dom.getParent(n, 'td,th,caption');\r
754                                 cm.setActive('table', n.nodeName === 'TABLE' || !!p);\r
755 \r
756                                 // Disable table tools if we are in caption\r
757                                 if (p && p.nodeName === 'CAPTION')\r
758                                         p = 0;\r
759 \r
760                                 cm.setDisabled('delete_table', !p);\r
761                                 cm.setDisabled('delete_col', !p);\r
762                                 cm.setDisabled('delete_table', !p);\r
763                                 cm.setDisabled('delete_row', !p);\r
764                                 cm.setDisabled('col_after', !p);\r
765                                 cm.setDisabled('col_before', !p);\r
766                                 cm.setDisabled('row_after', !p);\r
767                                 cm.setDisabled('row_before', !p);\r
768                                 cm.setDisabled('row_props', !p);\r
769                                 cm.setDisabled('cell_props', !p);\r
770                                 cm.setDisabled('split_cells', !p);\r
771                                 cm.setDisabled('merge_cells', !p);\r
772                         });\r
773 \r
774                         ed.onInit.add(function(ed) {\r
775                                 var startTable, startCell, dom = ed.dom, tableGrid;\r
776 \r
777                                 winMan = ed.windowManager;\r
778 \r
779                                 // Add cell selection logic\r
780                                 ed.onMouseDown.add(function(ed, e) {\r
781                                         if (e.button != 2) {\r
782                                                 cleanup();\r
783 \r
784                                                 startCell = dom.getParent(e.target, 'td,th');\r
785                                                 startTable = dom.getParent(startCell, 'table');\r
786                                         }\r
787                                 });\r
788 \r
789                                 dom.bind(ed.getDoc(), 'mouseover', function(e) {\r
790                                         var sel, table, target = e.target;\r
791 \r
792                                         if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) {\r
793                                                 table = dom.getParent(target, 'table');\r
794                                                 if (table == startTable) {\r
795                                                         if (!tableGrid) {\r
796                                                                 tableGrid = createTableGrid(table);\r
797                                                                 tableGrid.setStartCell(startCell);\r
798 \r
799                                                                 ed.getBody().style.webkitUserSelect = 'none';\r
800                                                         }\r
801 \r
802                                                         tableGrid.setEndCell(target);\r
803                                                 }\r
804 \r
805                                                 // Remove current selection\r
806                                                 sel = ed.selection.getSel();\r
807 \r
808                                                 if (sel.removeAllRanges)\r
809                                                         sel.removeAllRanges();\r
810                                                 else\r
811                                                         sel.empty();\r
812 \r
813                                                 e.preventDefault();\r
814                                         }\r
815                                 });\r
816 \r
817                                 ed.onMouseUp.add(function(ed, e) {\r
818                                         var rng, sel = ed.selection, selectedCells, nativeSel = sel.getSel(), walker, node, lastNode, endNode;\r
819 \r
820                                         // Move selection to startCell\r
821                                         if (startCell) {\r
822                                                 if (tableGrid)\r
823                                                         ed.getBody().style.webkitUserSelect = '';\r
824 \r
825                                                 function setPoint(node, start) {\r
826                                                         var walker = new tinymce.dom.TreeWalker(node, node);\r
827 \r
828                                                         do {\r
829                                                                 // Text node\r
830                                                                 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {\r
831                                                                         if (start)\r
832                                                                                 rng.setStart(node, 0);\r
833                                                                         else\r
834                                                                                 rng.setEnd(node, node.nodeValue.length);\r
835 \r
836                                                                         return;\r
837                                                                 }\r
838 \r
839                                                                 // BR element\r
840                                                                 if (node.nodeName == 'BR') {\r
841                                                                         if (start)\r
842                                                                                 rng.setStartBefore(node);\r
843                                                                         else\r
844                                                                                 rng.setEndBefore(node);\r
845 \r
846                                                                         return;\r
847                                                                 }\r
848                                                         } while (node = (start ? walker.next() : walker.prev()));\r
849                                                 };\r
850 \r
851                                                 // Try to expand text selection as much as we can only Gecko supports cell selection\r
852                                                 selectedCells = dom.select('td.mceSelected,th.mceSelected');\r
853                                                 if (selectedCells.length > 0) {\r
854                                                         rng = dom.createRng();\r
855                                                         node = selectedCells[0];\r
856                                                         endNode = selectedCells[selectedCells.length - 1];\r
857 \r
858                                                         setPoint(node, 1);\r
859                                                         walker = new tinymce.dom.TreeWalker(node, dom.getParent(selectedCells[0], 'table'));\r
860 \r
861                                                         do {\r
862                                                                 if (node.nodeName == 'TD' || node.nodeName == 'TH') {\r
863                                                                         if (!dom.hasClass(node, 'mceSelected'))\r
864                                                                                 break;\r
865 \r
866                                                                         lastNode = node;\r
867                                                                 }\r
868                                                         } while (node = walker.next());\r
869 \r
870                                                         setPoint(lastNode);\r
871 \r
872                                                         sel.setRng(rng);\r
873                                                 }\r
874 \r
875                                                 ed.nodeChanged();\r
876                                                 startCell = tableGrid = startTable = null;\r
877                                         }\r
878                                 });\r
879 \r
880                                 ed.onKeyUp.add(function(ed, e) {\r
881                                         cleanup();\r
882                                 });\r
883 \r
884                                 // Add context menu\r
885                                 if (ed && ed.plugins.contextmenu) {\r
886                                         ed.plugins.contextmenu.onContextMenu.add(function(th, m, e) {\r
887                                                 var sm, se = ed.selection, el = se.getNode() || ed.getBody();\r
888 \r
889                                                 if (ed.dom.getParent(e, 'td') || ed.dom.getParent(e, 'th') || ed.dom.select('td.mceSelected,th.mceSelected').length) {\r
890                                                         m.removeAll();\r
891 \r
892                                                         if (el.nodeName == 'A' && !ed.dom.getAttrib(el, 'name')) {\r
893                                                                 m.add({title : 'advanced.link_desc', icon : 'link', cmd : ed.plugins.advlink ? 'mceAdvLink' : 'mceLink', ui : true});\r
894                                                                 m.add({title : 'advanced.unlink_desc', icon : 'unlink', cmd : 'UnLink'});\r
895                                                                 m.addSeparator();\r
896                                                         }\r
897 \r
898                                                         if (el.nodeName == 'IMG' && el.className.indexOf('mceItem') == -1) {\r
899                                                                 m.add({title : 'advanced.image_desc', icon : 'image', cmd : ed.plugins.advimage ? 'mceAdvImage' : 'mceImage', ui : true});\r
900                                                                 m.addSeparator();\r
901                                                         }\r
902 \r
903                                                         m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable', value : {action : 'insert'}});\r
904                                                         m.add({title : 'table.props_desc', icon : 'table_props', cmd : 'mceInsertTable'});\r
905                                                         m.add({title : 'table.del', icon : 'delete_table', cmd : 'mceTableDelete'});\r
906                                                         m.addSeparator();\r
907 \r
908                                                         // Cell menu\r
909                                                         sm = m.addMenu({title : 'table.cell'});\r
910                                                         sm.add({title : 'table.cell_desc', icon : 'cell_props', cmd : 'mceTableCellProps'});\r
911                                                         sm.add({title : 'table.split_cells_desc', icon : 'split_cells', cmd : 'mceTableSplitCells'});\r
912                                                         sm.add({title : 'table.merge_cells_desc', icon : 'merge_cells', cmd : 'mceTableMergeCells'});\r
913 \r
914                                                         // Row menu\r
915                                                         sm = m.addMenu({title : 'table.row'});\r
916                                                         sm.add({title : 'table.row_desc', icon : 'row_props', cmd : 'mceTableRowProps'});\r
917                                                         sm.add({title : 'table.row_before_desc', icon : 'row_before', cmd : 'mceTableInsertRowBefore'});\r
918                                                         sm.add({title : 'table.row_after_desc', icon : 'row_after', cmd : 'mceTableInsertRowAfter'});\r
919                                                         sm.add({title : 'table.delete_row_desc', icon : 'delete_row', cmd : 'mceTableDeleteRow'});\r
920                                                         sm.addSeparator();\r
921                                                         sm.add({title : 'table.cut_row_desc', icon : 'cut', cmd : 'mceTableCutRow'});\r
922                                                         sm.add({title : 'table.copy_row_desc', icon : 'copy', cmd : 'mceTableCopyRow'});\r
923                                                         sm.add({title : 'table.paste_row_before_desc', icon : 'paste', cmd : 'mceTablePasteRowBefore'}).setDisabled(!clipboardRows);\r
924                                                         sm.add({title : 'table.paste_row_after_desc', icon : 'paste', cmd : 'mceTablePasteRowAfter'}).setDisabled(!clipboardRows);\r
925 \r
926                                                         // Column menu\r
927                                                         sm = m.addMenu({title : 'table.col'});\r
928                                                         sm.add({title : 'table.col_before_desc', icon : 'col_before', cmd : 'mceTableInsertColBefore'});\r
929                                                         sm.add({title : 'table.col_after_desc', icon : 'col_after', cmd : 'mceTableInsertColAfter'});\r
930                                                         sm.add({title : 'table.delete_col_desc', icon : 'delete_col', cmd : 'mceTableDeleteCol'});\r
931                                                 } else\r
932                                                         m.add({title : 'table.desc', icon : 'table', cmd : 'mceInsertTable'});\r
933                                         });\r
934                                 }\r
935 \r
936                                 // Fixes an issue on Gecko where it's impossible to place the caret behind a table\r
937                                 // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled\r
938                                 if (!tinymce.isIE) {\r
939                                         function fixTableCaretPos() {\r
940                                                 var last;\r
941 \r
942                                                 // Skip empty text nodes form the end\r
943                                                 for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ;\r
944 \r
945                                                 if (last && last.nodeName == 'TABLE')\r
946                                                         ed.dom.add(ed.getBody(), 'p', null, '<br mce_bogus="1" />');\r
947                                         };\r
948 \r
949                                         // Fixes an bug where it's impossible to place the caret before a table in Gecko\r
950                                         // this fix solves it by detecting when the caret is at the beginning of such a table\r
951                                         // and then manually moves the caret infront of the table\r
952                                         if (tinymce.isGecko) {\r
953                                                 ed.onKeyDown.add(function(ed, e) {\r
954                                                         var rng, table, dom = ed.dom;\r
955 \r
956                                                         // On gecko it's not possible to place the caret before a table\r
957                                                         if (e.keyCode == 37 || e.keyCode == 38) {\r
958                                                                 rng = ed.selection.getRng();\r
959                                                                 table = dom.getParent(rng.startContainer, 'table');\r
960 \r
961                                                                 if (table && ed.getBody().firstChild == table) {\r
962                                                                         if (isAtStart(rng, table)) {\r
963                                                                                 rng = dom.createRng();\r
964 \r
965                                                                                 rng.setStartBefore(table);\r
966                                                                                 rng.setEndBefore(table);\r
967 \r
968                                                                                 ed.selection.setRng(rng);\r
969 \r
970                                                                                 e.preventDefault();\r
971                                                                         }\r
972                                                                 }\r
973                                                         }\r
974                                                 });\r
975                                         }\r
976 \r
977                                         ed.onKeyUp.add(fixTableCaretPos);\r
978                                         ed.onSetContent.add(fixTableCaretPos);\r
979                                         ed.onVisualAid.add(fixTableCaretPos);\r
980 \r
981                                         ed.onPreProcess.add(function(ed, o) {\r
982                                                 var last = o.node.lastChild;\r
983 \r
984                                                 if (last && last.childNodes.length == 1 && last.firstChild.nodeName == 'BR')\r
985                                                         ed.dom.remove(last);\r
986                                         });\r
987 \r
988                                         fixTableCaretPos();\r
989                                 }\r
990                         });\r
991 \r
992                         // Register action commands\r
993                         each({\r
994                                 mceTableSplitCells : function(grid) {\r
995                                         grid.split();\r
996                                 },\r
997 \r
998                                 mceTableMergeCells : function(grid) {\r
999                                         var rowSpan, colSpan, cell;\r
1000 \r
1001                                         cell = ed.dom.getParent(ed.selection.getNode(), 'th,td');\r
1002                                         if (cell) {\r
1003                                                 rowSpan = cell.rowSpan;\r
1004                                                 colSpan = cell.colSpan;\r
1005                                         }\r
1006 \r
1007                                         if (!ed.dom.select('td.mceSelected,th.mceSelected').length) {\r
1008                                                 winMan.open({\r
1009                                                         url : url + '/merge_cells.htm',\r
1010                                                         width : 240 + parseInt(ed.getLang('table.merge_cells_delta_width', 0)),\r
1011                                                         height : 110 + parseInt(ed.getLang('table.merge_cells_delta_height', 0)),\r
1012                                                         inline : 1\r
1013                                                 }, {\r
1014                                                         rows : rowSpan,\r
1015                                                         cols : colSpan,\r
1016                                                         onaction : function(data) {\r
1017                                                                 grid.merge(cell, data.cols, data.rows);\r
1018                                                         },\r
1019                                                         plugin_url : url\r
1020                                                 });\r
1021                                         } else\r
1022                                                 grid.merge();\r
1023                                 },\r
1024 \r
1025                                 mceTableInsertRowBefore : function(grid) {\r
1026                                         grid.insertRow(true);\r
1027                                 },\r
1028 \r
1029                                 mceTableInsertRowAfter : function(grid) {\r
1030                                         grid.insertRow();\r
1031                                 },\r
1032 \r
1033                                 mceTableInsertColBefore : function(grid) {\r
1034                                         grid.insertCol(true);\r
1035                                 },\r
1036 \r
1037                                 mceTableInsertColAfter : function(grid) {\r
1038                                         grid.insertCol();\r
1039                                 },\r
1040 \r
1041                                 mceTableDeleteCol : function(grid) {\r
1042                                         grid.deleteCols();\r
1043                                 },\r
1044 \r
1045                                 mceTableDeleteRow : function(grid) {\r
1046                                         grid.deleteRows();\r
1047                                 },\r
1048 \r
1049                                 mceTableCutRow : function(grid) {\r
1050                                         clipboardRows = grid.cutRows();\r
1051                                 },\r
1052 \r
1053                                 mceTableCopyRow : function(grid) {\r
1054                                         clipboardRows = grid.copyRows();\r
1055                                 },\r
1056 \r
1057                                 mceTablePasteRowBefore : function(grid) {\r
1058                                         grid.pasteRows(clipboardRows, true);\r
1059                                 },\r
1060 \r
1061                                 mceTablePasteRowAfter : function(grid) {\r
1062                                         grid.pasteRows(clipboardRows);\r
1063                                 },\r
1064 \r
1065                                 mceTableDelete : function(grid) {\r
1066                                         grid.deleteTable();\r
1067                                 }\r
1068                         }, function(func, name) {\r
1069                                 ed.addCommand(name, function() {\r
1070                                         var grid = createTableGrid();\r
1071 \r
1072                                         if (grid) {\r
1073                                                 func(grid);\r
1074                                                 ed.execCommand('mceRepaint');\r
1075                                                 cleanup();\r
1076                                         }\r
1077                                 });\r
1078                         });\r
1079 \r
1080                         // Register dialog commands\r
1081                         each({\r
1082                                 mceInsertTable : function(val) {\r
1083                                         winMan.open({\r
1084                                                 url : url + '/table.htm',\r
1085                                                 width : 400 + parseInt(ed.getLang('table.table_delta_width', 0)),\r
1086                                                 height : 320 + parseInt(ed.getLang('table.table_delta_height', 0)),\r
1087                                                 inline : 1\r
1088                                         }, {\r
1089                                                 plugin_url : url,\r
1090                                                 action : val ? val.action : 0\r
1091                                         });\r
1092                                 },\r
1093 \r
1094                                 mceTableRowProps : function() {\r
1095                                         winMan.open({\r
1096                                                 url : url + '/row.htm',\r
1097                                                 width : 400 + parseInt(ed.getLang('table.rowprops_delta_width', 0)),\r
1098                                                 height : 295 + parseInt(ed.getLang('table.rowprops_delta_height', 0)),\r
1099                                                 inline : 1\r
1100                                         }, {\r
1101                                                 plugin_url : url\r
1102                                         });\r
1103                                 },\r
1104 \r
1105                                 mceTableCellProps : function() {\r
1106                                         winMan.open({\r
1107                                                 url : url + '/cell.htm',\r
1108                                                 width : 400 + parseInt(ed.getLang('table.cellprops_delta_width', 0)),\r
1109                                                 height : 295 + parseInt(ed.getLang('table.cellprops_delta_height', 0)),\r
1110                                                 inline : 1\r
1111                                         }, {\r
1112                                                 plugin_url : url\r
1113                                         });\r
1114                                 }\r
1115                         }, function(func, name) {\r
1116                                 ed.addCommand(name, function(ui, val) {\r
1117                                         func(val);\r
1118                                 });\r
1119                         });\r
1120                 }\r
1121         });\r
1122 \r
1123         // Register plugin\r
1124         tinymce.PluginManager.add('table', tinymce.plugins.TablePlugin);\r
1125 })(tinymce);