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.
- data/assets/vendor/tinymce/plugins/autosave/editor_plugin.js +1 -1
- data/assets/vendor/tinymce/plugins/inlinepopups/editor_plugin.js +4 -1
- data/assets/vendor/tinymce/plugins/lists/editor_plugin.js +180 -99
- data/assets/vendor/tinymce/plugins/media/editor_plugin.js +60 -15
- data/assets/vendor/tinymce/plugins/media/js/media.js +49 -15
- data/assets/vendor/tinymce/plugins/media/langs/en_dlg.js +4 -1
- data/assets/vendor/tinymce/plugins/media/media.htm +91 -6
- data/assets/vendor/tinymce/plugins/style/props.htm +3 -1
- data/assets/vendor/tinymce/plugins/tabfocus/editor_plugin.js +122 -114
- data/assets/vendor/tinymce/plugins/table/editor_plugin.js +1361 -1261
- data/assets/vendor/tinymce/plugins/wordcount/editor_plugin.js +2 -2
- data/assets/vendor/tinymce/themes/advanced/editor_template.js +1 -1
- data/assets/vendor/tinymce/themes/advanced/skins/default/content.css +1 -0
- data/assets/vendor/tinymce/themes/advanced/skins/o2k7/content.css +1 -0
- data/assets/vendor/tinymce/tiny_mce.js +242 -61
- data/lib/tinymce/version.rb +1 -1
- metadata +8 -8
@@ -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
|
-
|
27
|
-
|
28
|
-
e.
|
29
|
-
!(
|
30
|
-
!(
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
}
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
//
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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 ? ' ' : '<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 ? ' ' : '<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
|
-
//
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
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);
|