wysihtml-rails 0.5.5 → 0.6.0.beta

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.
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
+ })();