wysihtml-rails 0.5.5 → 0.6.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -6
  3. data/lib/wysihtml/rails/version.rb +1 -1
  4. data/vendor/assets/javascripts/wysihtml.js +5944 -8553
  5. data/vendor/assets/javascripts/wysihtml/all_commands.js +23 -0
  6. data/vendor/assets/javascripts/wysihtml/extra_commands/alignCenterStyle.js +17 -0
  7. data/vendor/assets/javascripts/wysihtml/extra_commands/alignJustifyStyle.js +17 -0
  8. data/vendor/assets/javascripts/wysihtml/extra_commands/alignLeftStyle.js +17 -0
  9. data/vendor/assets/javascripts/wysihtml/extra_commands/alignRightStyle.js +17 -0
  10. data/vendor/assets/javascripts/wysihtml/extra_commands/bgColorStyle.js +48 -0
  11. data/vendor/assets/javascripts/wysihtml/extra_commands/bold.js +16 -0
  12. data/vendor/assets/javascripts/wysihtml/extra_commands/command_formatCode.js +52 -0
  13. data/vendor/assets/javascripts/wysihtml/extra_commands/command_insertImage.js +108 -0
  14. data/vendor/assets/javascripts/wysihtml/extra_commands/fontSize.js +13 -0
  15. data/vendor/assets/javascripts/wysihtml/extra_commands/fontSizeStyle.js +35 -0
  16. data/vendor/assets/javascripts/wysihtml/extra_commands/foreColor.js +13 -0
  17. data/vendor/assets/javascripts/wysihtml/extra_commands/foreColorStyle.js +52 -0
  18. data/vendor/assets/javascripts/wysihtml/extra_commands/insertBlockQuote.js +16 -0
  19. data/vendor/assets/javascripts/wysihtml/extra_commands/insertOrderedList.js +11 -0
  20. data/vendor/assets/javascripts/wysihtml/extra_commands/insertUnorderedList.js +11 -0
  21. data/vendor/assets/javascripts/wysihtml/extra_commands/italic.js +17 -0
  22. data/vendor/assets/javascripts/wysihtml/extra_commands/justifyCenter.js +18 -0
  23. data/vendor/assets/javascripts/wysihtml/extra_commands/justifyFull.js +17 -0
  24. data/vendor/assets/javascripts/wysihtml/extra_commands/justifyLeft.js +17 -0
  25. data/vendor/assets/javascripts/wysihtml/extra_commands/justifyRight.js +17 -0
  26. data/vendor/assets/javascripts/wysihtml/extra_commands/subscript.js +17 -0
  27. data/vendor/assets/javascripts/wysihtml/extra_commands/superscript.js +17 -0
  28. data/vendor/assets/javascripts/wysihtml/extra_commands/underline.js +17 -0
  29. data/vendor/assets/javascripts/{parser_rules → wysihtml/parser_rules}/advanced.js +4 -4
  30. data/vendor/assets/javascripts/{parser_rules → wysihtml/parser_rules}/advanced_and_extended.js +25 -25
  31. data/vendor/assets/javascripts/{parser_rules → wysihtml/parser_rules}/advanced_unwrap.js +5 -5
  32. data/vendor/assets/javascripts/{parser_rules → wysihtml/parser_rules}/simple.js +2 -2
  33. data/vendor/assets/javascripts/wysihtml/table_editing.js +1163 -0
  34. data/vendor/assets/javascripts/wysihtml/toolbar.js +850 -0
  35. metadata +35 -9
  36. data/vendor/assets/javascripts/wysihtml-toolbar.js +0 -19308
@@ -0,0 +1,1163 @@
1
+ wysihtml.commands.addTableCells = {
2
+ exec: function(composer, command, value) {
3
+ if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
4
+
5
+ // switches start and end if start is bigger than end (reverse selection)
6
+ var tableSelect = wysihtml.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end);
7
+ if (value == 'before' || value == 'above') {
8
+ wysihtml.dom.table.addCells(tableSelect.start, value);
9
+ } else if (value == 'after' || value == 'below') {
10
+ wysihtml.dom.table.addCells(tableSelect.end, value);
11
+ }
12
+ setTimeout(function() {
13
+ composer.tableSelection.select(tableSelect.start, tableSelect.end);
14
+ },0);
15
+ }
16
+ },
17
+
18
+ state: function(composer, command) {
19
+ return false;
20
+ }
21
+ };
22
+
23
+ wysihtml.commands.createTable = {
24
+ exec: function(composer, command, value) {
25
+ var col, row, html;
26
+ if (value && value.cols && value.rows && parseInt(value.cols, 10) > 0 && parseInt(value.rows, 10) > 0) {
27
+ if (value.tableStyle) {
28
+ html = '<table style="' + value.tableStyle + '">';
29
+ } else {
30
+ html = '<table>';
31
+ }
32
+ html += '<tbody>';
33
+ for (row = 0; row < value.rows; row ++) {
34
+ html += '<tr>';
35
+ for (col = 0; col < value.cols; col ++) {
36
+ html += '<td><br></td>';
37
+ }
38
+ html += '</tr>';
39
+ }
40
+ html += '</tbody></table>';
41
+ composer.commands.exec('insertHTML', html);
42
+ }
43
+ },
44
+
45
+ state: function(composer, command) {
46
+ return false;
47
+ }
48
+ };
49
+
50
+ wysihtml.commands.deleteTableCells = {
51
+ exec: function(composer, command, value) {
52
+ if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
53
+ var tableSelect = wysihtml.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end),
54
+ idx = wysihtml.dom.table.indexOf(tableSelect.start),
55
+ selCell,
56
+ table = composer.tableSelection.table;
57
+
58
+ wysihtml.dom.table.removeCells(tableSelect.start, value);
59
+ setTimeout(function() {
60
+ // move selection to next or previous if not present
61
+ selCell = wysihtml.dom.table.findCell(table, idx);
62
+
63
+ if (!selCell) {
64
+ if (value == 'row') {
65
+ selCell = wysihtml.dom.table.findCell(table, {
66
+ 'row': idx.row - 1,
67
+ 'col': idx.col
68
+ });
69
+ }
70
+
71
+ if (value == 'column') {
72
+ selCell = wysihtml.dom.table.findCell(table, {
73
+ 'row': idx.row,
74
+ 'col': idx.col - 1
75
+ });
76
+ }
77
+ }
78
+ if (selCell) {
79
+ composer.tableSelection.select(selCell, selCell);
80
+ }
81
+ }, 0);
82
+ }
83
+ },
84
+
85
+ state: function(composer, command) {
86
+ return false;
87
+ }
88
+ };
89
+
90
+ wysihtml.commands.mergeTableCells = {
91
+ exec: function(composer, command) {
92
+ if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
93
+ if (this.state(composer, command)) {
94
+ wysihtml.dom.table.unmergeCell(composer.tableSelection.start);
95
+ } else {
96
+ wysihtml.dom.table.mergeCellsBetween(composer.tableSelection.start, composer.tableSelection.end);
97
+ }
98
+ }
99
+ },
100
+
101
+ state: function(composer, command) {
102
+ if (composer.tableSelection) {
103
+ var start = composer.tableSelection.start,
104
+ end = composer.tableSelection.end;
105
+ if (start && end && start == end &&
106
+ ((
107
+ wysihtml.dom.getAttribute(start, 'colspan') &&
108
+ parseInt(wysihtml.dom.getAttribute(start, 'colspan'), 10) > 1
109
+ ) || (
110
+ wysihtml.dom.getAttribute(start, 'rowspan') &&
111
+ parseInt(wysihtml.dom.getAttribute(start, 'rowspan'), 10) > 1
112
+ ))
113
+ ) {
114
+ return [start];
115
+ }
116
+ }
117
+ return false;
118
+ }
119
+ };
120
+
121
+ (function() {
122
+
123
+ var api = wysihtml.dom;
124
+
125
+ var MapCell = function(cell) {
126
+ this.el = cell;
127
+ this.isColspan= false;
128
+ this.isRowspan= false;
129
+ this.firstCol= true;
130
+ this.lastCol= true;
131
+ this.firstRow= true;
132
+ this.lastRow= true;
133
+ this.isReal= true;
134
+ this.spanCollection= [];
135
+ this.modified = false;
136
+ };
137
+
138
+ var TableModifyerByCell = function (cell, table) {
139
+ if (cell) {
140
+ this.cell = cell;
141
+ this.table = api.getParentElement(cell, { query: "table" });
142
+ } else if (table) {
143
+ this.table = table;
144
+ this.cell = this.table.querySelectorAll('th, td')[0];
145
+ }
146
+ };
147
+
148
+ function queryInList(list, query) {
149
+ var ret = [],
150
+ q;
151
+ for (var e = 0, len = list.length; e < len; e++) {
152
+ q = list[e].querySelectorAll(query);
153
+ if (q) {
154
+ for(var i = q.length; i--; ret.unshift(q[i]));
155
+ }
156
+ }
157
+ return ret;
158
+ }
159
+
160
+ function removeElement(el) {
161
+ el.parentNode.removeChild(el);
162
+ }
163
+
164
+ function insertAfter(referenceNode, newNode) {
165
+ referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
166
+ }
167
+
168
+ function nextNode(node, tag) {
169
+ var element = node.nextSibling;
170
+ while (element.nodeType !=1) {
171
+ element = element.nextSibling;
172
+ if (!tag || tag == element.tagName.toLowerCase()) {
173
+ return element;
174
+ }
175
+ }
176
+ return null;
177
+ }
178
+
179
+ TableModifyerByCell.prototype = {
180
+
181
+ addSpannedCellToMap: function(cell, map, r, c, cspan, rspan) {
182
+ var spanCollect = [],
183
+ rmax = r + ((rspan) ? parseInt(rspan, 10) - 1 : 0),
184
+ cmax = c + ((cspan) ? parseInt(cspan, 10) - 1 : 0);
185
+
186
+ for (var rr = r; rr <= rmax; rr++) {
187
+ if (typeof map[rr] == "undefined") { map[rr] = []; }
188
+ for (var cc = c; cc <= cmax; cc++) {
189
+ map[rr][cc] = new MapCell(cell);
190
+ map[rr][cc].isColspan = (cspan && parseInt(cspan, 10) > 1);
191
+ map[rr][cc].isRowspan = (rspan && parseInt(rspan, 10) > 1);
192
+ map[rr][cc].firstCol = cc == c;
193
+ map[rr][cc].lastCol = cc == cmax;
194
+ map[rr][cc].firstRow = rr == r;
195
+ map[rr][cc].lastRow = rr == rmax;
196
+ map[rr][cc].isReal = cc == c && rr == r;
197
+ map[rr][cc].spanCollection = spanCollect;
198
+
199
+ spanCollect.push(map[rr][cc]);
200
+ }
201
+ }
202
+ },
203
+
204
+ setCellAsModified: function(cell) {
205
+ cell.modified = true;
206
+ if (cell.spanCollection.length > 0) {
207
+ for (var s = 0, smax = cell.spanCollection.length; s < smax; s++) {
208
+ cell.spanCollection[s].modified = true;
209
+ }
210
+ }
211
+ },
212
+
213
+ setTableMap: function() {
214
+ var map = [];
215
+ var tableRows = this.getTableRows(),
216
+ ridx, row, cells, cidx, cell,
217
+ c,
218
+ cspan, rspan;
219
+
220
+ for (ridx = 0; ridx < tableRows.length; ridx++) {
221
+ row = tableRows[ridx];
222
+ cells = this.getRowCells(row);
223
+ c = 0;
224
+ if (typeof map[ridx] == "undefined") { map[ridx] = []; }
225
+ for (cidx = 0; cidx < cells.length; cidx++) {
226
+ cell = cells[cidx];
227
+
228
+ // If cell allready set means it is set by col or rowspan,
229
+ // so increase cols index until free col is found
230
+ while (typeof map[ridx][c] != "undefined") { c++; }
231
+
232
+ cspan = api.getAttribute(cell, 'colspan');
233
+ rspan = api.getAttribute(cell, 'rowspan');
234
+
235
+ if (cspan || rspan) {
236
+ this.addSpannedCellToMap(cell, map, ridx, c, cspan, rspan);
237
+ c = c + ((cspan) ? parseInt(cspan, 10) : 1);
238
+ } else {
239
+ map[ridx][c] = new MapCell(cell);
240
+ c++;
241
+ }
242
+ }
243
+ }
244
+ this.map = map;
245
+ return map;
246
+ },
247
+
248
+ getRowCells: function(row) {
249
+ var inlineTables = this.table.querySelectorAll('table'),
250
+ inlineCells = (inlineTables) ? queryInList(inlineTables, 'th, td') : [],
251
+ allCells = row.querySelectorAll('th, td'),
252
+ tableCells = (inlineCells.length > 0) ? wysihtml.lang.array(allCells).without(inlineCells) : allCells;
253
+
254
+ return tableCells;
255
+ },
256
+
257
+ getTableRows: function() {
258
+ var inlineTables = this.table.querySelectorAll('table'),
259
+ inlineRows = (inlineTables) ? queryInList(inlineTables, 'tr') : [],
260
+ allRows = this.table.querySelectorAll('tr'),
261
+ tableRows = (inlineRows.length > 0) ? wysihtml.lang.array(allRows).without(inlineRows) : allRows;
262
+
263
+ return tableRows;
264
+ },
265
+
266
+ getMapIndex: function(cell) {
267
+ var r_length = this.map.length,
268
+ c_length = (this.map && this.map[0]) ? this.map[0].length : 0;
269
+
270
+ for (var r_idx = 0;r_idx < r_length; r_idx++) {
271
+ for (var c_idx = 0;c_idx < c_length; c_idx++) {
272
+ if (this.map[r_idx][c_idx].el === cell) {
273
+ return {'row': r_idx, 'col': c_idx};
274
+ }
275
+ }
276
+ }
277
+ return false;
278
+ },
279
+
280
+ getElementAtIndex: function(idx) {
281
+ this.setTableMap();
282
+ if (this.map[idx.row] && this.map[idx.row][idx.col] && this.map[idx.row][idx.col].el) {
283
+ return this.map[idx.row][idx.col].el;
284
+ }
285
+ return null;
286
+ },
287
+
288
+ getMapElsTo: function(to_cell) {
289
+ var els = [];
290
+ this.setTableMap();
291
+ this.idx_start = this.getMapIndex(this.cell);
292
+ this.idx_end = this.getMapIndex(to_cell);
293
+
294
+ // switch indexes if start is bigger than end
295
+ if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
296
+ var temp_idx = this.idx_start;
297
+ this.idx_start = this.idx_end;
298
+ this.idx_end = temp_idx;
299
+ }
300
+ if (this.idx_start.col > this.idx_end.col) {
301
+ var temp_cidx = this.idx_start.col;
302
+ this.idx_start.col = this.idx_end.col;
303
+ this.idx_end.col = temp_cidx;
304
+ }
305
+
306
+ if (this.idx_start != null && this.idx_end != null) {
307
+ for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
308
+ for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
309
+ els.push(this.map[row][col].el);
310
+ }
311
+ }
312
+ }
313
+ return els;
314
+ },
315
+
316
+ orderSelectionEnds: function(secondcell) {
317
+ this.setTableMap();
318
+ this.idx_start = this.getMapIndex(this.cell);
319
+ this.idx_end = this.getMapIndex(secondcell);
320
+
321
+ // switch indexes if start is bigger than end
322
+ if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
323
+ var temp_idx = this.idx_start;
324
+ this.idx_start = this.idx_end;
325
+ this.idx_end = temp_idx;
326
+ }
327
+ if (this.idx_start.col > this.idx_end.col) {
328
+ var temp_cidx = this.idx_start.col;
329
+ this.idx_start.col = this.idx_end.col;
330
+ this.idx_end.col = temp_cidx;
331
+ }
332
+
333
+ return {
334
+ "start": this.map[this.idx_start.row][this.idx_start.col].el,
335
+ "end": this.map[this.idx_end.row][this.idx_end.col].el
336
+ };
337
+ },
338
+
339
+ createCells: function(tag, nr, attrs) {
340
+ var doc = this.table.ownerDocument,
341
+ frag = doc.createDocumentFragment(),
342
+ cell;
343
+ for (var i = 0; i < nr; i++) {
344
+ cell = doc.createElement(tag);
345
+
346
+ if (attrs) {
347
+ for (var attr in attrs) {
348
+ if (attrs.hasOwnProperty(attr)) {
349
+ cell.setAttribute(attr, attrs[attr]);
350
+ }
351
+ }
352
+ }
353
+
354
+ // add non breaking space
355
+ cell.appendChild(document.createTextNode("\u00a0"));
356
+ frag.appendChild(cell);
357
+ }
358
+ return frag;
359
+ },
360
+
361
+ // Returns next real cell (not part of spanned cell unless first) on row if selected index is not real. I no real cells -1 will be returned
362
+ correctColIndexForUnreals: function(col, row) {
363
+ var r = this.map[row],
364
+ corrIdx = -1;
365
+ for (var i = 0, max = col; i < col; i++) {
366
+ if (r[i].isReal){
367
+ corrIdx++;
368
+ }
369
+ }
370
+ return corrIdx;
371
+ },
372
+
373
+ getLastNewCellOnRow: function(row, rowLimit) {
374
+ var cells = this.getRowCells(row),
375
+ cell, idx;
376
+
377
+ for (var cidx = 0, cmax = cells.length; cidx < cmax; cidx++) {
378
+ cell = cells[cidx];
379
+ idx = this.getMapIndex(cell);
380
+ if (idx === false || (typeof rowLimit != "undefined" && idx.row != rowLimit)) {
381
+ return cell;
382
+ }
383
+ }
384
+ return null;
385
+ },
386
+
387
+ removeEmptyTable: function() {
388
+ var cells = this.table.querySelectorAll('td, th');
389
+ if (!cells || cells.length == 0) {
390
+ removeElement(this.table);
391
+ return true;
392
+ } else {
393
+ return false;
394
+ }
395
+ },
396
+
397
+ // Splits merged cell on row to unique cells
398
+ splitRowToCells: function(cell) {
399
+ if (cell.isColspan) {
400
+ var colspan = parseInt(api.getAttribute(cell.el, 'colspan') || 1, 10),
401
+ cType = cell.el.tagName.toLowerCase();
402
+ if (colspan > 1) {
403
+ var newCells = this.createCells(cType, colspan -1);
404
+ insertAfter(cell.el, newCells);
405
+ }
406
+ cell.el.removeAttribute('colspan');
407
+ }
408
+ },
409
+
410
+ getRealRowEl: function(force, idx) {
411
+ var r = null,
412
+ c = null;
413
+
414
+ idx = idx || this.idx;
415
+
416
+ for (var cidx = 0, cmax = this.map[idx.row].length; cidx < cmax; cidx++) {
417
+ c = this.map[idx.row][cidx];
418
+ if (c.isReal) {
419
+ r = api.getParentElement(c.el, { query: "tr" });
420
+ if (r) {
421
+ return r;
422
+ }
423
+ }
424
+ }
425
+
426
+ if (r === null && force) {
427
+ r = api.getParentElement(this.map[idx.row][idx.col].el, { query: "tr" }) || null;
428
+ }
429
+
430
+ return r;
431
+ },
432
+
433
+ injectRowAt: function(row, col, colspan, cType, c) {
434
+ var r = this.getRealRowEl(false, {'row': row, 'col': col}),
435
+ new_cells = this.createCells(cType, colspan);
436
+
437
+ if (r) {
438
+ var n_cidx = this.correctColIndexForUnreals(col, row);
439
+ if (n_cidx >= 0) {
440
+ insertAfter(this.getRowCells(r)[n_cidx], new_cells);
441
+ } else {
442
+ r.insertBefore(new_cells, r.firstChild);
443
+ }
444
+ } else {
445
+ var rr = this.table.ownerDocument.createElement('tr');
446
+ rr.appendChild(new_cells);
447
+ insertAfter(api.getParentElement(c.el, { query: "tr" }), rr);
448
+ }
449
+ },
450
+
451
+ canMerge: function(to) {
452
+ this.to = to;
453
+ this.setTableMap();
454
+ this.idx_start = this.getMapIndex(this.cell);
455
+ this.idx_end = this.getMapIndex(this.to);
456
+
457
+ // switch indexes if start is bigger than end
458
+ if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
459
+ var temp_idx = this.idx_start;
460
+ this.idx_start = this.idx_end;
461
+ this.idx_end = temp_idx;
462
+ }
463
+ if (this.idx_start.col > this.idx_end.col) {
464
+ var temp_cidx = this.idx_start.col;
465
+ this.idx_start.col = this.idx_end.col;
466
+ this.idx_end.col = temp_cidx;
467
+ }
468
+
469
+ for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
470
+ for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
471
+ if (this.map[row][col].isColspan || this.map[row][col].isRowspan) {
472
+ return false;
473
+ }
474
+ }
475
+ }
476
+ return true;
477
+ },
478
+
479
+ decreaseCellSpan: function(cell, span) {
480
+ var nr = parseInt(api.getAttribute(cell.el, span), 10) - 1;
481
+ if (nr >= 1) {
482
+ cell.el.setAttribute(span, nr);
483
+ } else {
484
+ cell.el.removeAttribute(span);
485
+ if (span == 'colspan') {
486
+ cell.isColspan = false;
487
+ }
488
+ if (span == 'rowspan') {
489
+ cell.isRowspan = false;
490
+ }
491
+ cell.firstCol = true;
492
+ cell.lastCol = true;
493
+ cell.firstRow = true;
494
+ cell.lastRow = true;
495
+ cell.isReal = true;
496
+ }
497
+ },
498
+
499
+ removeSurplusLines: function() {
500
+ var row, cell, ridx, rmax, cidx, cmax, allRowspan;
501
+
502
+ this.setTableMap();
503
+ if (this.map) {
504
+ ridx = 0;
505
+ rmax = this.map.length;
506
+ for (;ridx < rmax; ridx++) {
507
+ row = this.map[ridx];
508
+ allRowspan = true;
509
+ cidx = 0;
510
+ cmax = row.length;
511
+ for (; cidx < cmax; cidx++) {
512
+ cell = row[cidx];
513
+ if (!(api.getAttribute(cell.el, "rowspan") && parseInt(api.getAttribute(cell.el, "rowspan"), 10) > 1 && cell.firstRow !== true)) {
514
+ allRowspan = false;
515
+ break;
516
+ }
517
+ }
518
+ if (allRowspan) {
519
+ cidx = 0;
520
+ for (; cidx < cmax; cidx++) {
521
+ this.decreaseCellSpan(row[cidx], 'rowspan');
522
+ }
523
+ }
524
+ }
525
+
526
+ // remove rows without cells
527
+ var tableRows = this.getTableRows();
528
+ ridx = 0;
529
+ rmax = tableRows.length;
530
+ for (;ridx < rmax; ridx++) {
531
+ row = tableRows[ridx];
532
+ if (row.childNodes.length == 0 && (/^\s*$/.test(row.textContent || row.innerText))) {
533
+ removeElement(row);
534
+ }
535
+ }
536
+ }
537
+ },
538
+
539
+ fillMissingCells: function() {
540
+ var r_max = 0,
541
+ c_max = 0,
542
+ prevcell = null;
543
+
544
+ this.setTableMap();
545
+ if (this.map) {
546
+
547
+ // find maximal dimensions of broken table
548
+ r_max = this.map.length;
549
+ for (var ridx = 0; ridx < r_max; ridx++) {
550
+ if (this.map[ridx].length > c_max) { c_max = this.map[ridx].length; }
551
+ }
552
+
553
+ for (var row = 0; row < r_max; row++) {
554
+ for (var col = 0; col < c_max; col++) {
555
+ if (this.map[row] && !this.map[row][col]) {
556
+ if (col > 0) {
557
+ this.map[row][col] = new MapCell(this.createCells('td', 1));
558
+ prevcell = this.map[row][col-1];
559
+ if (prevcell && prevcell.el && prevcell.el.parent) { // if parent does not exist element is removed from dom
560
+ insertAfter(this.map[row][col-1].el, this.map[row][col].el);
561
+ }
562
+ }
563
+ }
564
+ }
565
+ }
566
+ }
567
+ },
568
+
569
+ rectify: function() {
570
+ if (!this.removeEmptyTable()) {
571
+ this.removeSurplusLines();
572
+ this.fillMissingCells();
573
+ return true;
574
+ } else {
575
+ return false;
576
+ }
577
+ },
578
+
579
+ unmerge: function() {
580
+ if (this.rectify()) {
581
+ this.setTableMap();
582
+ this.idx = this.getMapIndex(this.cell);
583
+
584
+ if (this.idx) {
585
+ var thisCell = this.map[this.idx.row][this.idx.col],
586
+ colspan = (api.getAttribute(thisCell.el, "colspan")) ? parseInt(api.getAttribute(thisCell.el, "colspan"), 10) : 1,
587
+ cType = thisCell.el.tagName.toLowerCase();
588
+
589
+ if (thisCell.isRowspan) {
590
+ var rowspan = parseInt(api.getAttribute(thisCell.el, "rowspan"), 10);
591
+ if (rowspan > 1) {
592
+ for (var nr = 1, maxr = rowspan - 1; nr <= maxr; nr++){
593
+ this.injectRowAt(this.idx.row + nr, this.idx.col, colspan, cType, thisCell);
594
+ }
595
+ }
596
+ thisCell.el.removeAttribute('rowspan');
597
+ }
598
+ this.splitRowToCells(thisCell);
599
+ }
600
+ }
601
+ },
602
+
603
+ // merges cells from start cell (defined in creating obj) to "to" cell
604
+ merge: function(to) {
605
+ if (this.rectify()) {
606
+ if (this.canMerge(to)) {
607
+ var rowspan = this.idx_end.row - this.idx_start.row + 1,
608
+ colspan = this.idx_end.col - this.idx_start.col + 1;
609
+
610
+ for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
611
+ for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
612
+
613
+ if (row == this.idx_start.row && col == this.idx_start.col) {
614
+ if (rowspan > 1) {
615
+ this.map[row][col].el.setAttribute('rowspan', rowspan);
616
+ }
617
+ if (colspan > 1) {
618
+ this.map[row][col].el.setAttribute('colspan', colspan);
619
+ }
620
+ } else {
621
+ // transfer content
622
+ if (!(/^\s*<br\/?>\s*$/.test(this.map[row][col].el.innerHTML.toLowerCase()))) {
623
+ this.map[this.idx_start.row][this.idx_start.col].el.innerHTML += ' ' + this.map[row][col].el.innerHTML;
624
+ }
625
+ removeElement(this.map[row][col].el);
626
+ }
627
+
628
+ }
629
+ }
630
+ this.rectify();
631
+ } else {
632
+ if (window.console) {
633
+ console.log('Do not know how to merge allready merged cells.');
634
+ }
635
+ }
636
+ }
637
+ },
638
+
639
+ // Decreases rowspan of a cell if it is done on first cell of rowspan row (real cell)
640
+ // Cell is moved to next row (if it is real)
641
+ collapseCellToNextRow: function(cell) {
642
+ var cellIdx = this.getMapIndex(cell.el),
643
+ newRowIdx = cellIdx.row + 1,
644
+ newIdx = {'row': newRowIdx, 'col': cellIdx.col};
645
+
646
+ if (newRowIdx < this.map.length) {
647
+
648
+ var row = this.getRealRowEl(false, newIdx);
649
+ if (row !== null) {
650
+ var n_cidx = this.correctColIndexForUnreals(newIdx.col, newIdx.row);
651
+ if (n_cidx >= 0) {
652
+ insertAfter(this.getRowCells(row)[n_cidx], cell.el);
653
+ } else {
654
+ var lastCell = this.getLastNewCellOnRow(row, newRowIdx);
655
+ if (lastCell !== null) {
656
+ insertAfter(lastCell, cell.el);
657
+ } else {
658
+ row.insertBefore(cell.el, row.firstChild);
659
+ }
660
+ }
661
+ if (parseInt(api.getAttribute(cell.el, 'rowspan'), 10) > 2) {
662
+ cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) - 1);
663
+ } else {
664
+ cell.el.removeAttribute('rowspan');
665
+ }
666
+ }
667
+ }
668
+ },
669
+
670
+ // Removes a cell when removing a row
671
+ // If is rowspan cell then decreases the rowspan
672
+ // and moves cell to next row if needed (is first cell of rowspan)
673
+ removeRowCell: function(cell) {
674
+ if (cell.isReal) {
675
+ if (cell.isRowspan) {
676
+ this.collapseCellToNextRow(cell);
677
+ } else {
678
+ removeElement(cell.el);
679
+ }
680
+ } else {
681
+ if (parseInt(api.getAttribute(cell.el, 'rowspan'), 10) > 2) {
682
+ cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) - 1);
683
+ } else {
684
+ cell.el.removeAttribute('rowspan');
685
+ }
686
+ }
687
+ },
688
+
689
+ getRowElementsByCell: function() {
690
+ var cells = [];
691
+ this.setTableMap();
692
+ this.idx = this.getMapIndex(this.cell);
693
+ if (this.idx !== false) {
694
+ var modRow = this.map[this.idx.row];
695
+ for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) {
696
+ if (modRow[cidx].isReal) {
697
+ cells.push(modRow[cidx].el);
698
+ }
699
+ }
700
+ }
701
+ return cells;
702
+ },
703
+
704
+ getColumnElementsByCell: function() {
705
+ var cells = [];
706
+ this.setTableMap();
707
+ this.idx = this.getMapIndex(this.cell);
708
+ if (this.idx !== false) {
709
+ for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) {
710
+ if (this.map[ridx][this.idx.col] && this.map[ridx][this.idx.col].isReal) {
711
+ cells.push(this.map[ridx][this.idx.col].el);
712
+ }
713
+ }
714
+ }
715
+ return cells;
716
+ },
717
+
718
+ // Removes the row of selected cell
719
+ removeRow: function() {
720
+ var oldRow = api.getParentElement(this.cell, { query: "tr" });
721
+ if (oldRow) {
722
+ this.setTableMap();
723
+ this.idx = this.getMapIndex(this.cell);
724
+ if (this.idx !== false) {
725
+ var modRow = this.map[this.idx.row];
726
+ for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) {
727
+ if (!modRow[cidx].modified) {
728
+ this.setCellAsModified(modRow[cidx]);
729
+ this.removeRowCell(modRow[cidx]);
730
+ }
731
+ }
732
+ }
733
+ removeElement(oldRow);
734
+ }
735
+ },
736
+
737
+ removeColCell: function(cell) {
738
+ if (cell.isColspan) {
739
+ if (parseInt(api.getAttribute(cell.el, 'colspan'), 10) > 2) {
740
+ cell.el.setAttribute('colspan', parseInt(api.getAttribute(cell.el, 'colspan'), 10) - 1);
741
+ } else {
742
+ cell.el.removeAttribute('colspan');
743
+ }
744
+ } else if (cell.isReal) {
745
+ removeElement(cell.el);
746
+ }
747
+ },
748
+
749
+ removeColumn: function() {
750
+ this.setTableMap();
751
+ this.idx = this.getMapIndex(this.cell);
752
+ if (this.idx !== false) {
753
+ for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) {
754
+ if (!this.map[ridx][this.idx.col].modified) {
755
+ this.setCellAsModified(this.map[ridx][this.idx.col]);
756
+ this.removeColCell(this.map[ridx][this.idx.col]);
757
+ }
758
+ }
759
+ }
760
+ },
761
+
762
+ // removes row or column by selected cell element
763
+ remove: function(what) {
764
+ if (this.rectify()) {
765
+ switch (what) {
766
+ case 'row':
767
+ this.removeRow();
768
+ break;
769
+ case 'column':
770
+ this.removeColumn();
771
+ break;
772
+ }
773
+ this.rectify();
774
+ }
775
+ },
776
+
777
+ addRow: function(where) {
778
+ var doc = this.table.ownerDocument;
779
+
780
+ this.setTableMap();
781
+ this.idx = this.getMapIndex(this.cell);
782
+ if (where == "below" && api.getAttribute(this.cell, 'rowspan')) {
783
+ this.idx.row = this.idx.row + parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1;
784
+ }
785
+
786
+ if (this.idx !== false) {
787
+ var modRow = this.map[this.idx.row],
788
+ newRow = doc.createElement('tr');
789
+
790
+ for (var ridx = 0, rmax = modRow.length; ridx < rmax; ridx++) {
791
+ if (!modRow[ridx].modified) {
792
+ this.setCellAsModified(modRow[ridx]);
793
+ this.addRowCell(modRow[ridx], newRow, where);
794
+ }
795
+ }
796
+
797
+ switch (where) {
798
+ case 'below':
799
+ insertAfter(this.getRealRowEl(true), newRow);
800
+ break;
801
+ case 'above':
802
+ var cr = api.getParentElement(this.map[this.idx.row][this.idx.col].el, { query: "tr" });
803
+ if (cr) {
804
+ cr.parentNode.insertBefore(newRow, cr);
805
+ }
806
+ break;
807
+ }
808
+ }
809
+ },
810
+
811
+ addRowCell: function(cell, row, where) {
812
+ var colSpanAttr = (cell.isColspan) ? {"colspan" : api.getAttribute(cell.el, 'colspan')} : null;
813
+ if (cell.isReal) {
814
+ if (where != 'above' && cell.isRowspan) {
815
+ cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el,'rowspan'), 10) + 1);
816
+ } else {
817
+ row.appendChild(this.createCells('td', 1, colSpanAttr));
818
+ }
819
+ } else {
820
+ if (where != 'above' && cell.isRowspan && cell.lastRow) {
821
+ row.appendChild(this.createCells('td', 1, colSpanAttr));
822
+ } else if (c.isRowspan) {
823
+ cell.el.attr('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) + 1);
824
+ }
825
+ }
826
+ },
827
+
828
+ add: function(where) {
829
+ if (this.rectify()) {
830
+ if (where == 'below' || where == 'above') {
831
+ this.addRow(where);
832
+ }
833
+ if (where == 'before' || where == 'after') {
834
+ this.addColumn(where);
835
+ }
836
+ }
837
+ },
838
+
839
+ addColCell: function (cell, ridx, where) {
840
+ var doAdd,
841
+ cType = cell.el.tagName.toLowerCase();
842
+
843
+ // defines add cell vs expand cell conditions
844
+ // true means add
845
+ switch (where) {
846
+ case "before":
847
+ doAdd = (!cell.isColspan || cell.firstCol);
848
+ break;
849
+ case "after":
850
+ doAdd = (!cell.isColspan || cell.lastCol || (cell.isColspan && cell.el == this.cell));
851
+ break;
852
+ }
853
+
854
+ if (doAdd){
855
+ // adds a cell before or after current cell element
856
+ switch (where) {
857
+ case "before":
858
+ cell.el.parentNode.insertBefore(this.createCells(cType, 1), cell.el);
859
+ break;
860
+ case "after":
861
+ insertAfter(cell.el, this.createCells(cType, 1));
862
+ break;
863
+ }
864
+
865
+ // handles if cell has rowspan
866
+ if (cell.isRowspan) {
867
+ this.handleCellAddWithRowspan(cell, ridx+1, where);
868
+ }
869
+
870
+ } else {
871
+ // expands cell
872
+ cell.el.setAttribute('colspan', parseInt(api.getAttribute(cell.el, 'colspan'), 10) + 1);
873
+ }
874
+ },
875
+
876
+ addColumn: function(where) {
877
+ var row, modCell;
878
+
879
+ this.setTableMap();
880
+ this.idx = this.getMapIndex(this.cell);
881
+ if (where == "after" && api.getAttribute(this.cell, 'colspan')) {
882
+ this.idx.col = this.idx.col + parseInt(api.getAttribute(this.cell, 'colspan'), 10) - 1;
883
+ }
884
+
885
+ if (this.idx !== false) {
886
+ for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++ ) {
887
+ row = this.map[ridx];
888
+ if (row[this.idx.col]) {
889
+ modCell = row[this.idx.col];
890
+ if (!modCell.modified) {
891
+ this.setCellAsModified(modCell);
892
+ this.addColCell(modCell, ridx , where);
893
+ }
894
+ }
895
+ }
896
+ }
897
+ },
898
+
899
+ handleCellAddWithRowspan: function (cell, ridx, where) {
900
+ var addRowsNr = parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1,
901
+ crow = api.getParentElement(cell.el, { query: "tr" }),
902
+ cType = cell.el.tagName.toLowerCase(),
903
+ cidx, temp_r_cells,
904
+ doc = this.table.ownerDocument,
905
+ nrow;
906
+
907
+ for (var i = 0; i < addRowsNr; i++) {
908
+ cidx = this.correctColIndexForUnreals(this.idx.col, (ridx + i));
909
+ crow = nextNode(crow, 'tr');
910
+ if (crow) {
911
+ if (cidx > 0) {
912
+ switch (where) {
913
+ case "before":
914
+ temp_r_cells = this.getRowCells(crow);
915
+ if (cidx > 0 && this.map[ridx + i][this.idx.col].el != temp_r_cells[cidx] && cidx == temp_r_cells.length - 1) {
916
+ insertAfter(temp_r_cells[cidx], this.createCells(cType, 1));
917
+ } else {
918
+ temp_r_cells[cidx].parentNode.insertBefore(this.createCells(cType, 1), temp_r_cells[cidx]);
919
+ }
920
+
921
+ break;
922
+ case "after":
923
+ insertAfter(this.getRowCells(crow)[cidx], this.createCells(cType, 1));
924
+ break;
925
+ }
926
+ } else {
927
+ crow.insertBefore(this.createCells(cType, 1), crow.firstChild);
928
+ }
929
+ } else {
930
+ nrow = doc.createElement('tr');
931
+ nrow.appendChild(this.createCells(cType, 1));
932
+ this.table.appendChild(nrow);
933
+ }
934
+ }
935
+ }
936
+ };
937
+
938
+ api.table = {
939
+ getCellsBetween: function(cell1, cell2) {
940
+ var c1 = new TableModifyerByCell(cell1);
941
+ return c1.getMapElsTo(cell2);
942
+ },
943
+
944
+ addCells: function(cell, where) {
945
+ var c = new TableModifyerByCell(cell);
946
+ c.add(where);
947
+ },
948
+
949
+ removeCells: function(cell, what) {
950
+ var c = new TableModifyerByCell(cell);
951
+ c.remove(what);
952
+ },
953
+
954
+ mergeCellsBetween: function(cell1, cell2) {
955
+ var c1 = new TableModifyerByCell(cell1);
956
+ c1.merge(cell2);
957
+ },
958
+
959
+ unmergeCell: function(cell) {
960
+ var c = new TableModifyerByCell(cell);
961
+ c.unmerge();
962
+ },
963
+
964
+ orderSelectionEnds: function(cell, cell2) {
965
+ var c = new TableModifyerByCell(cell);
966
+ return c.orderSelectionEnds(cell2);
967
+ },
968
+
969
+ indexOf: function(cell) {
970
+ var c = new TableModifyerByCell(cell);
971
+ c.setTableMap();
972
+ return c.getMapIndex(cell);
973
+ },
974
+
975
+ findCell: function(table, idx) {
976
+ var c = new TableModifyerByCell(null, table);
977
+ return c.getElementAtIndex(idx);
978
+ },
979
+
980
+ findRowByCell: function(cell) {
981
+ var c = new TableModifyerByCell(cell);
982
+ return c.getRowElementsByCell();
983
+ },
984
+
985
+ findColumnByCell: function(cell) {
986
+ var c = new TableModifyerByCell(cell);
987
+ return c.getColumnElementsByCell();
988
+ },
989
+
990
+ canMerge: function(cell1, cell2) {
991
+ var c = new TableModifyerByCell(cell1);
992
+ return c.canMerge(cell2);
993
+ }
994
+ };
995
+
996
+ })();
997
+
998
+ (function() {
999
+
1000
+ // Keep the old composer.observe function.
1001
+ var oldObserverFunction = wysihtml.views.Composer.prototype.observe;
1002
+
1003
+ var extendedObserverFunction = function() {
1004
+ oldObserverFunction.call(this);
1005
+ // Bind the table user interaction tracking
1006
+ if (this.config.handleTables) {
1007
+ // If handleTables option is true, table handling functions are bound
1008
+ initTableHandling.call(this);
1009
+ }
1010
+ };
1011
+
1012
+ // Table management.
1013
+ // If present enableObjectResizing and enableInlineTableEditing command
1014
+ // should be called with false to prevent native table handlers.
1015
+ var initTableHandling = function() {
1016
+ var hideHandlers = function() {
1017
+ this.win.removeEventListener('load', hideHandlers);
1018
+ this.doc.execCommand('enableObjectResizing', false, 'false');
1019
+ this.doc.execCommand('enableInlineTableEditing', false, 'false');
1020
+ }.bind(this),
1021
+ iframeInitiator = (function() {
1022
+ hideHandlers.call(this);
1023
+ actions.removeListeners(this.sandbox.getIframe(), ['focus', 'mouseup', 'mouseover'], iframeInitiator);
1024
+ }).bind(this);
1025
+
1026
+ if (
1027
+ this.doc.execCommand &&
1028
+ wysihtml.browser.supportsCommand(this.doc, 'enableObjectResizing') &&
1029
+ wysihtml.browser.supportsCommand(this.doc, 'enableInlineTableEditing')
1030
+ ) {
1031
+ if (this.sandbox.getIframe) {
1032
+ actions.addListeners(this.sandbox.getIframe(), ['focus', 'mouseup', 'mouseover'], iframeInitiator);
1033
+ } else {
1034
+ this.win.addEventListener('load', hideHandlers);
1035
+ }
1036
+ }
1037
+ this.tableSelection = wysihtml.quirks.tableCellsSelection(this.element, this.parent);
1038
+ };
1039
+
1040
+ // Cell selections handling
1041
+ var tableCellsSelection = function(editable, editor) {
1042
+
1043
+ var init = function() {
1044
+ editable.addEventListener('mousedown', handleMouseDown);
1045
+ return select;
1046
+ };
1047
+
1048
+ var handleMouseDown = function(event) {
1049
+ var target = wysihtml.dom.getParentElement(event.target, {query: 'td, th'}, false, editable);
1050
+ if (target) {
1051
+ handleSelectionMousedown(target);
1052
+ }
1053
+ };
1054
+
1055
+ var handleSelectionMousedown = function(target) {
1056
+ select.start = target;
1057
+ select.end = target;
1058
+ select.cells = [target];
1059
+ select.table = dom.getParentElement(select.start, {query: 'table'}, false, editable);
1060
+
1061
+ if (select.table) {
1062
+ removeCellSelections();
1063
+ dom.addClass(target, selectionClass);
1064
+ editable.addEventListener('mousemove', handleMouseMove);
1065
+ editable.addEventListener('mouseup', handleMouseUp);
1066
+ editor.fire('tableselectstart').fire('tableselectstart:composer');
1067
+ }
1068
+ };
1069
+
1070
+ // remove all selection classes
1071
+ var removeCellSelections = function() {
1072
+ if (editable) {
1073
+ var selectedCells = editable.querySelectorAll('.' + selectionClass);
1074
+ if (selectedCells.length > 0) {
1075
+ for (var i = 0; i < selectedCells.length; i++) {
1076
+ dom.removeClass(selectedCells[i], selectionClass);
1077
+ }
1078
+ }
1079
+ }
1080
+ };
1081
+
1082
+ var addSelections = function(cells) {
1083
+ for (var i = 0; i < cells.length; i++) {
1084
+ dom.addClass(cells[i], selectionClass);
1085
+ }
1086
+ };
1087
+
1088
+ var handleMouseMove = function(event) {
1089
+ var curTable = null,
1090
+ cell = dom.getParentElement(event.target, {query: 'td, th'}, false, editable),
1091
+ oldEnd;
1092
+
1093
+ if (cell && select.table && select.start) {
1094
+ curTable = dom.getParentElement(cell, {query: 'table'}, false, editable);
1095
+ if (curTable && curTable === select.table) {
1096
+ removeCellSelections();
1097
+ oldEnd = select.end;
1098
+ select.end = cell;
1099
+ select.cells = dom.table.getCellsBetween(select.start, cell);
1100
+ if (select.cells.length > 1) {
1101
+ editor.composer.selection.deselect();
1102
+ }
1103
+ addSelections(select.cells);
1104
+ if (select.end !== oldEnd) {
1105
+ editor.fire('tableselectchange').fire('tableselectchange:composer');
1106
+ }
1107
+ }
1108
+ }
1109
+ };
1110
+
1111
+ var handleMouseUp = function(event) {
1112
+ editable.removeEventListener('mousemove', handleMouseMove);
1113
+ editable.removeEventListener('mouseup', handleMouseUp);
1114
+ editor.fire('tableselect').fire('tableselect:composer');
1115
+ setTimeout(function() {
1116
+ bindSideclick();
1117
+ }, 0);
1118
+ };
1119
+
1120
+ var sideClickHandler = function(event) {
1121
+ editable.ownerDocument.removeEventListener('click', sideClickHandler);
1122
+ if (dom.getParentElement(event.target, {query: 'table'}, false, editable) != select.table) {
1123
+ removeCellSelections();
1124
+ select.table = null;
1125
+ select.start = null;
1126
+ select.end = null;
1127
+ editor.fire('tableunselect').fire('tableunselect:composer');
1128
+ }
1129
+ };
1130
+
1131
+ var bindSideclick = function() {
1132
+ editable.ownerDocument.addEventListener('click', sideClickHandler);
1133
+ };
1134
+
1135
+ var selectCells = function(start, end) {
1136
+ select.start = start;
1137
+ select.end = end;
1138
+ select.table = dom.getParentElement(select.start, {query: 'table'}, false, editable);
1139
+ selectedCells = dom.table.getCellsBetween(select.start, select.end);
1140
+ addSelections(selectedCells);
1141
+ bindSideclick();
1142
+ editor.fire('tableselect').fire('tableselect:composer');
1143
+ };
1144
+
1145
+ var dom = wysihtml.dom,
1146
+ select = {
1147
+ table: null,
1148
+ start: null,
1149
+ end: null,
1150
+ cells: null,
1151
+ select: selectCells
1152
+ },
1153
+ selectionClass = 'wysiwyg-tmp-selected-cell';
1154
+
1155
+ return init();
1156
+ };
1157
+
1158
+ // Bind to wysihtml
1159
+ wysihtml.Editor.prototype.defaults.handleTables = true;
1160
+ wysihtml.quirks.tableCellsSelection = tableCellsSelection;
1161
+ wysihtml.views.Composer.prototype.observe = extendedObserverFunction;
1162
+
1163
+ })();