tinymce-rails 3.4.3.2 → 3.4.4

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