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