vue_crud 0.1.9.6 → 0.1.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/vue_crud/froala_generator.rb +12 -0
  3. data/lib/generators/vue_crud/sortable.rb +10 -0
  4. data/lib/generators/vue_crud/templates/assets/images/froala_editor/color_picker.png +0 -0
  5. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/froala_editor.js +11089 -0
  6. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/en_gb.js +178 -0
  7. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/zh_cn.js +234 -0
  8. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/languages/zh_tw.js +234 -0
  9. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/align.js +119 -0
  10. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/char_counter.js +150 -0
  11. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/code_beautifier.js +3271 -0
  12. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/code_view.js +311 -0
  13. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/colors.js +350 -0
  14. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/draggable.js +379 -0
  15. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/emoticons.js +347 -0
  16. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/entities.js +113 -0
  17. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/file.js +609 -0
  18. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/font_family.js +178 -0
  19. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/font_size.js +116 -0
  20. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/forms.js +415 -0
  21. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/fullscreen.js +209 -0
  22. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/image.js +2401 -0
  23. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/image_manager.js +921 -0
  24. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/inline_style.js +86 -0
  25. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/line_breaker.js +435 -0
  26. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/link.js +997 -0
  27. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/lists.js +382 -0
  28. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/paragraph_format.js +293 -0
  29. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/paragraph_style.js +139 -0
  30. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/quick_insert.js +342 -0
  31. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/quote.js +138 -0
  32. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/save.js +176 -0
  33. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/table.js +3123 -0
  34. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/url.js +113 -0
  35. data/lib/generators/vue_crud/templates/assets/javascripts/froala_editor/plugins/video.js +1135 -0
  36. data/lib/generators/vue_crud/templates/assets/javascripts/sortable.js +1249 -0
  37. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/froala_editor.scss +1008 -0
  38. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/froala_style.scss +273 -0
  39. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/char_counter.scss +46 -0
  40. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/code_view.scss +102 -0
  41. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/colors.scss +129 -0
  42. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/draggable.scss +32 -0
  43. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/emoticons.scss +27 -0
  44. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/file.scss +135 -0
  45. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/fullscreen.scss +28 -0
  46. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/image.scss +233 -0
  47. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/image_manager.scss +370 -0
  48. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/line_breaker.scss +26 -0
  49. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/quick_insert.scss +56 -0
  50. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/table.scss +156 -0
  51. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/plugins/video.scss +136 -0
  52. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/dark.scss +1087 -0
  53. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/gray.scss +1087 -0
  54. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/red.scss +1087 -0
  55. data/lib/generators/vue_crud/templates/assets/stylesheets/froala_editor/themes/royal.scss +1087 -0
  56. data/lib/generators/vue_crud/templates/vue_crud.html +54 -71
  57. data/lib/vue_crud/version.rb +1 -1
  58. data/vendor/assets/javascripts/vue_crud.js +212 -59
  59. metadata +56 -2
@@ -0,0 +1,3123 @@
1
+ /*!
2
+ * froala_editor v2.3.3 (https://www.froala.com/wysiwyg-editor)
3
+ * License https://froala.com/wysiwyg-editor/terms/
4
+ * Copyright 2014-2016 Froala Labs
5
+ */
6
+
7
+ (function (factory) {
8
+ if (typeof define === 'function' && define.amd) {
9
+ // AMD. Register as an anonymous module.
10
+ define(['jquery'], factory);
11
+ } else if (typeof module === 'object' && module.exports) {
12
+ // Node/CommonJS
13
+ module.exports = function( root, jQuery ) {
14
+ if ( jQuery === undefined ) {
15
+ // require('jQuery') returns a factory that requires window to
16
+ // build a jQuery instance, we normalize how we use modules
17
+ // that require this pattern but the window provided is a noop
18
+ // if it's defined (how jquery works)
19
+ if ( typeof window !== 'undefined' ) {
20
+ jQuery = require('jquery');
21
+ }
22
+ else {
23
+ jQuery = require('jquery')(root);
24
+ }
25
+ }
26
+ factory(jQuery);
27
+ return jQuery;
28
+ };
29
+ } else {
30
+ // Browser globals
31
+ factory(jQuery);
32
+ }
33
+ }(function ($) {
34
+
35
+ 'use strict';
36
+
37
+ $.extend($.FE.POPUP_TEMPLATES, {
38
+ 'table.insert': '[_BUTTONS_][_ROWS_COLUMNS_]',
39
+ 'table.edit': '[_BUTTONS_]',
40
+ 'table.colors': '[_BUTTONS_][_COLORS_]'
41
+ })
42
+
43
+ // Extend defaults.
44
+ $.extend($.FE.DEFAULTS, {
45
+ tableInsertMaxSize: 10,
46
+ tableEditButtons: ['tableHeader', 'tableRemove', '|', 'tableRows', 'tableColumns', 'tableStyle', '-', 'tableCells', 'tableCellBackground', 'tableCellVerticalAlign', 'tableCellHorizontalAlign', 'tableCellStyle'],
47
+ tableInsertButtons: ['tableBack', '|'],
48
+ tableResizer: true,
49
+ tableResizerOffset: 5,
50
+ tableResizingLimit: 30,
51
+ tableColorsButtons: ['tableBack', '|'],
52
+ tableColors: [
53
+ '#61BD6D', '#1ABC9C', '#54ACD2', '#2C82C9', '#9365B8', '#475577', '#CCCCCC',
54
+ '#41A85F', '#00A885', '#3D8EB9', '#2969B0', '#553982', '#28324E', '#000000',
55
+ '#F7DA64', '#FBA026', '#EB6B56', '#E25041', '#A38F84', '#EFEFEF', '#FFFFFF',
56
+ '#FAC51C', '#F37934', '#D14841', '#B8312F', '#7C706B', '#D1D5D8', 'REMOVE'
57
+ ],
58
+ tableColorsStep: 7,
59
+ tableCellStyles: {
60
+ 'fr-highlighted': 'Highlighted',
61
+ 'fr-thick': 'Thick'
62
+ },
63
+ tableStyles: {
64
+ 'fr-dashed-borders': 'Dashed Borders',
65
+ 'fr-alternate-rows': 'Alternate Rows'
66
+ },
67
+ tableCellMultipleStyles: true,
68
+ tableMultipleStyles: true,
69
+ tableInsertHelper: true,
70
+ tableInsertHelperOffset: 15
71
+ });
72
+
73
+ $.FE.PLUGINS.table = function (editor) {
74
+ var $resizer;
75
+ var $insert_helper;
76
+ var mouseDownCellFlag;
77
+ var mouseDownFlag;
78
+ var mouseDownCell;
79
+ var mouseMoveTimer;
80
+ var resizingFlag;
81
+
82
+ /*
83
+ * Show the insert table popup.
84
+ */
85
+ function _showInsertPopup () {
86
+ var $btn = editor.$tb.find('.fr-command[data-cmd="insertTable"]');
87
+
88
+ var $popup = editor.popups.get('table.insert');
89
+ if (!$popup) $popup = _initInsertPopup();
90
+
91
+ if (!$popup.hasClass('fr-active')) {
92
+
93
+ // Insert table popup
94
+ editor.popups.refresh('table.insert');
95
+ editor.popups.setContainer('table.insert', editor.$tb);
96
+
97
+ // Insert table left and top position.
98
+ var left = $btn.offset().left + $btn.outerWidth() / 2;
99
+ var top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
100
+ editor.popups.show('table.insert', left, top, $btn.outerHeight());
101
+ }
102
+ }
103
+
104
+ /*
105
+ * Show the table edit popup.
106
+ */
107
+ function _showEditPopup () {
108
+ // Set popup position.
109
+ var map = _tableMap();
110
+ if (map) {
111
+ var $popup = editor.popups.get('table.edit');
112
+ if (!$popup) $popup = _initEditPopup();
113
+
114
+ editor.popups.setContainer('table.edit', $(editor.opts.scrollableContainer));
115
+ var offset = _selectionOffset(map);
116
+ var left = (offset.left + offset.right) / 2;
117
+ var top = offset.bottom;
118
+
119
+ editor.popups.show('table.edit', left, top, offset.bottom - offset.top);
120
+
121
+ // Disable toolbar buttons only if there are more than one cells selected.
122
+ if (editor.edit.isDisabled()) {
123
+ // Disable toolbar.
124
+ editor.toolbar.disable();
125
+
126
+ // Allow text selection.
127
+ editor.$el.removeClass('fr-no-selection');
128
+ editor.edit.on();
129
+
130
+ editor.selection.setAtEnd(editor.$el.find('.fr-selected-cell:last').get(0));
131
+ editor.selection.restore();
132
+ editor.button.bulkRefresh();
133
+ }
134
+ }
135
+ }
136
+
137
+ /*
138
+ * Show the table colors popup.
139
+ */
140
+ function _showColorsPopup () {
141
+ // Set popup position.
142
+ var map = _tableMap();
143
+ if (map) {
144
+ var $popup = editor.popups.get('table.colors');
145
+ if (!$popup) $popup = _initColorsPopup();
146
+
147
+ editor.popups.setContainer('table.colors', $(editor.opts.scrollableContainer));
148
+ var offset = _selectionOffset(map);
149
+ var left = (offset.left + offset.right) / 2;
150
+ var top = offset.bottom;
151
+
152
+ // Refresh selected color.
153
+ _refreshColor();
154
+
155
+ editor.popups.show('table.colors', left, top, offset.bottom - offset.top);
156
+ }
157
+ }
158
+
159
+ /*
160
+ * Called on table edit popup hide.
161
+ */
162
+ function _hideEditPopup () {
163
+ // Enable toolbar.
164
+ if (selectedCells().length === 0) {
165
+ editor.toolbar.enable();
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Init the insert table popup.
171
+ */
172
+ function _initInsertPopup (delayed) {
173
+ if (delayed) {
174
+ editor.popups.onHide('table.insert', function () {
175
+ // Clear previous cell selection.
176
+ editor.popups.get('table.insert').find('.fr-table-size .fr-select-table-size > span[data-row="1"][data-col="1"]').trigger('mouseenter');
177
+ });
178
+
179
+ return true;
180
+ }
181
+
182
+ // Table buttons.
183
+ var table_buttons = '';
184
+ if (editor.opts.tableInsertButtons.length > 0) {
185
+ table_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.tableInsertButtons) + '</div>';
186
+ }
187
+
188
+ var template = {
189
+ buttons: table_buttons,
190
+ rows_columns: _insertTableHtml()
191
+ };
192
+
193
+ var $popup = editor.popups.create('table.insert', template);
194
+
195
+ // Initialize insert table grid events.
196
+ editor.events.$on($popup, 'mouseenter', '.fr-table-size .fr-select-table-size .fr-table-cell', function (e) {
197
+ var $table_cell = $(e.currentTarget);
198
+ var row = $table_cell.data('row');
199
+ var col = $table_cell.data('col');
200
+ var $select_size = $table_cell.parent();
201
+
202
+ // Update size in title.
203
+ $select_size.siblings('.fr-table-size-info').html(row + ' &times; ' + col);
204
+
205
+ // Remove hover class from all cells.
206
+ $select_size.find('> span').removeClass('hover');
207
+
208
+ // Add hover class only to the correct cells.
209
+ for (var i = 1; i <= editor.opts.tableInsertMaxSize; i++) {
210
+ for (var j = 0; j <= editor.opts.tableInsertMaxSize; j++) {
211
+ var $cell = $select_size.find('> span[data-row="' + i + '"][data-col="' + j + '"]');
212
+
213
+ if (i <= row && j <= col) {
214
+ $cell.addClass('hover');
215
+ } else if ((i <= row + 1 || (i <= 2 && !editor.helpers.isMobile()))) {
216
+ $cell.css('display', 'inline-block');
217
+ } else if (i > 2 && !editor.helpers.isMobile()) {
218
+ $cell.css('display', 'none');
219
+ }
220
+ }
221
+ }
222
+ }, true);
223
+
224
+ return $popup;
225
+ }
226
+
227
+ /*
228
+ * The HTML for insert table grid.
229
+ */
230
+ function _insertTableHtml () {
231
+ // Grid html
232
+ var rows_columns = '<div class="fr-table-size"><div class="fr-table-size-info">1 &times; 1</div><div class="fr-select-table-size">'
233
+
234
+ for (var i = 1; i <= editor.opts.tableInsertMaxSize; i++) {
235
+ for (var j = 1; j <= editor.opts.tableInsertMaxSize; j++) {
236
+ var display = 'inline-block';
237
+
238
+ // Display only first 2 rows.
239
+ if (i > 2 && !editor.helpers.isMobile()) {
240
+ display = 'none';
241
+ }
242
+
243
+ var cls = 'fr-table-cell ';
244
+ if (i == 1 && j == 1) {
245
+ cls += ' hover';
246
+ }
247
+
248
+ rows_columns += '<span class="fr-command ' + cls + '" data-cmd="tableInsert" data-row="' + i + '" data-col="' + j + '" data-param1="' + i + '" data-param2="' + j + '" style="display: ' + display + ';"><span></span></span>';
249
+ }
250
+ rows_columns += '<div class="new-line"></div>';
251
+ }
252
+
253
+ rows_columns += '</div></div>';
254
+
255
+ return rows_columns;
256
+ }
257
+
258
+ /**
259
+ * Init the table edit popup.
260
+ */
261
+ function _initEditPopup (delayed) {
262
+ if (delayed) {
263
+ editor.popups.onHide('table.edit', _hideEditPopup);
264
+
265
+ return true;
266
+ }
267
+
268
+ // Table buttons.
269
+ var table_buttons = '';
270
+ if (editor.opts.tableEditButtons.length > 0) {
271
+ table_buttons = '<div class="fr-buttons">' + editor.button.buildList(editor.opts.tableEditButtons) + '</div>';
272
+ }
273
+
274
+ var template = {
275
+ buttons: table_buttons
276
+ };
277
+
278
+ var $popup = editor.popups.create('table.edit', template);
279
+
280
+ editor.events.$on(editor.$wp, 'scroll.table-edit', function () {
281
+ if (editor.popups.isVisible('table.edit')) {
282
+ _showEditPopup();
283
+ }
284
+ });
285
+
286
+ return $popup;
287
+ }
288
+
289
+ /*
290
+ * Init the table cell background popup.
291
+ */
292
+ function _initColorsPopup () {
293
+ // Table colors buttons.
294
+ var table_buttons = '';
295
+ if (editor.opts.tableColorsButtons.length > 0) {
296
+ table_buttons = '<div class="fr-buttons fr-table-colors-buttons">' + editor.button.buildList(editor.opts.tableColorsButtons) + '</div>';
297
+ }
298
+
299
+ var template = {
300
+ buttons: table_buttons,
301
+ colors: _colorsHTML()
302
+ };
303
+
304
+ var $popup = editor.popups.create('table.colors', template);
305
+
306
+ editor.events.$on(editor.$wp, 'scroll.table-colors', function () {
307
+ if (editor.popups.isVisible('table.colors')) {
308
+ _showColorsPopup();
309
+ }
310
+ });
311
+
312
+ return $popup;
313
+ }
314
+
315
+ /*
316
+ * HTML for the table colors.
317
+ */
318
+ function _colorsHTML () {
319
+ // Create colors html.
320
+ var colors_html = '<div class="fr-table-colors">';
321
+
322
+ // Add colors.
323
+ for (var i = 0; i < editor.opts.tableColors.length; i++) {
324
+ if (i !== 0 && i % editor.opts.tableColorsStep === 0) {
325
+ colors_html += '<br>';
326
+ }
327
+
328
+ if (editor.opts.tableColors[i] != 'REMOVE') {
329
+ colors_html += '<span class="fr-command" style="background: ' + editor.opts.tableColors[i] + ';" data-cmd="tableCellBackgroundColor" data-param1="' + editor.opts.tableColors[i] + '"></span>';
330
+ }
331
+
332
+ else {
333
+ colors_html += '<span class="fr-command" data-cmd="tableCellBackgroundColor" data-param1="REMOVE" title="' + editor.language.translate('Clear Formatting') + '"><i class="fa fa-eraser"></i></span>';
334
+ }
335
+ }
336
+
337
+ colors_html += '</div>';
338
+
339
+ return colors_html;
340
+ }
341
+
342
+ /*
343
+ * Show the current selected color.
344
+ */
345
+ function _refreshColor () {
346
+ var $popup = editor.popups.get('table.colors');
347
+ var $cell = editor.$el.find('.fr-selected-cell:first');
348
+
349
+ // Remove current color selection.
350
+ $popup.find('.fr-selected-color').removeClass('fr-selected-color');
351
+
352
+ // Find the selected color.
353
+ $popup.find('span[data-param1="' + editor.helpers.RGBToHex($cell.css('background-color')) + '"]').addClass('fr-selected-color');
354
+ }
355
+
356
+ /*
357
+ * Insert table method.
358
+ */
359
+ function insert (rows, cols) {
360
+ // Create table HTML.
361
+ var table = '<table style="width: 100%;"><tbody>';
362
+ var cell_width = 100 / cols;
363
+ var i;
364
+ var j;
365
+
366
+ for (i = 0; i < rows; i++) {
367
+ table += '<tr>';
368
+
369
+ for (j = 0; j < cols; j++) {
370
+ table += '<td style="width: ' + cell_width.toFixed(4) + '%;">';
371
+ if (i === 0 && j === 0)table += $.FE.MARKERS;
372
+ table += '<br></td>';
373
+ }
374
+ table += '</tr>';
375
+ }
376
+ table += '</tbody></table>';
377
+
378
+ editor.html.insert(table);
379
+
380
+ // Update cursor position.
381
+ editor.selection.restore()
382
+ }
383
+
384
+ /*
385
+ * Delete table method.
386
+ */
387
+ function remove () {
388
+ if (selectedCells().length > 0) {
389
+ var $current_table = selectedTable();
390
+
391
+ // Update cursor position.
392
+ editor.selection.setBefore($current_table.get(0)) || editor.selection.setAfter($current_table.get(0));
393
+ editor.selection.restore();
394
+
395
+ // Hide table edit popup.
396
+ editor.popups.hide('table.edit');
397
+
398
+ // Delete table.
399
+ $current_table.remove();
400
+
401
+ // Enable toolbar.
402
+ editor.toolbar.enable();
403
+ }
404
+ }
405
+
406
+ /*
407
+ * Add table header.
408
+ */
409
+ function addHeader () {
410
+ var $table = selectedTable();
411
+
412
+ // If there is a selection in the table and the table doesn't have a header already.
413
+ if ($table.length > 0 && $table.find('th').length === 0) {
414
+ // Create header HTML.
415
+ var thead = '<thead><tr>';
416
+
417
+ var i;
418
+ var col = 0;
419
+
420
+ // Get first row and count table columns.
421
+ $table.find('tr:first > td').each (function () {
422
+ var $td = $(this);
423
+
424
+ col += parseInt($td.attr('colspan'), 10) || 1;
425
+ });
426
+
427
+ // Add cells.
428
+ for (i = 0; i < col; i++) {
429
+ thead += '<th><br></th>';
430
+ }
431
+
432
+ thead += '</tr></thead>'
433
+
434
+ $table.prepend(thead);
435
+
436
+ // Reposition table edit popup.
437
+ _showEditPopup();
438
+ }
439
+ }
440
+
441
+ /*
442
+ * Remove table header.
443
+ */
444
+ function removeHeader () {
445
+ var $current_table = selectedTable();
446
+ var $table_header = $current_table.find('thead');
447
+
448
+ // Table has a header.
449
+ if ($table_header.length > 0) {
450
+ // If table does not have any other rows then delete table.
451
+ if ($current_table.find('tbody tr').length === 0) {
452
+ // Remove table.
453
+ remove();
454
+ }
455
+
456
+ else {
457
+ $table_header.remove();
458
+
459
+ // Reposition table edit popup if there any more selected celss.
460
+ if (selectedCells().length > 0) {
461
+ _showEditPopup();
462
+ }
463
+ else {
464
+ // Hide popup.
465
+ editor.popups.hide('table.edit');
466
+
467
+ // Update cursor position.
468
+ var td = $current_table.find('tbody tr:first td:first').get(0);
469
+ if (td) {
470
+ editor.selection.setAtEnd(td);
471
+ editor.selection.restore();
472
+ }
473
+ }
474
+ }
475
+ }
476
+ }
477
+
478
+ /*
479
+ * Insert row method.
480
+ */
481
+ function insertRow (position) {
482
+ var $table = selectedTable();
483
+
484
+ // We have selection in a table.
485
+ if ($table.length > 0) {
486
+ // Cannot insert row above the table header.
487
+ if (editor.$el.find('th.fr-selected-cell').length > 0 && position == 'above') {
488
+ return;
489
+ }
490
+
491
+ var i;
492
+ var ref_row;
493
+
494
+ // Create a table map.
495
+ var map = _tableMap();
496
+
497
+ // Get selected cells from the table.
498
+ var selection = _currentSelection(map);
499
+
500
+ // Reference row.
501
+ if (position == 'above') {
502
+ ref_row = selection.min_i;
503
+ } else {
504
+ ref_row = selection.max_i;
505
+ }
506
+
507
+ // Create row HTML.
508
+ var tr = '<tr>';
509
+
510
+ // Add cells.
511
+ for (i = 0; i < map[ref_row].length; i++) {
512
+ // If cell has rowspan we should increase it.
513
+ if ((position == 'below' && ref_row < map.length - 1 && map[ref_row][i] == map[ref_row + 1][i]) ||
514
+ (position == 'above' && ref_row > 0 && map[ref_row][i] == map[ref_row - 1][i])) {
515
+
516
+ // Don't increase twice for colspan.
517
+ if (i === 0 || (i > 0 && map[ref_row][i] != map[ref_row][i - 1])) {
518
+ var $cell = $(map[ref_row][i]);
519
+ $cell.attr('rowspan', parseInt($cell.attr('rowspan'), 10) + 1);
520
+ }
521
+
522
+ } else {
523
+ tr += '<td><br></td>';
524
+ }
525
+ }
526
+
527
+ // Close row tag.
528
+ tr += '</tr>';
529
+
530
+ var $ref_row = $($table.find('tr').not($table.find('table tr')).get(ref_row));
531
+
532
+ // Insert new row.
533
+ if (position == 'below') {
534
+ // Table edit popup should not change position.
535
+ $ref_row.after(tr);
536
+ }
537
+ else if (position == 'above') {
538
+ $ref_row.before(tr);
539
+
540
+ // Reposition table edit popup.
541
+ if (editor.popups.isVisible('table.edit')) {
542
+ _showEditPopup();
543
+ }
544
+ }
545
+ }
546
+ }
547
+
548
+ /*
549
+ * Delete row method.
550
+ */
551
+ function deleteRow () {
552
+ var $table = selectedTable();
553
+
554
+ // We have selection in a table.
555
+ if ($table.length > 0) {
556
+ var i;
557
+ var j;
558
+ var $row;
559
+
560
+ // Create a table map.
561
+ var map = _tableMap();
562
+
563
+ // Get selected cells from the table.
564
+ var selection = _currentSelection(map);
565
+
566
+ // If all the rows are selected then delete the entire table.
567
+ if (selection.min_i === 0 && selection.max_i == map.length - 1) {
568
+ remove();
569
+
570
+ } else {
571
+ // We should delete selected rows.
572
+ for (i = selection.max_i; i >= selection.min_i; i--) {
573
+ $row = $($table.find('tr').not($table.find('table tr')).get(i));
574
+
575
+ // Go through the table map to check for rowspan.
576
+ for (j = 0; j < map[i].length; j++) {
577
+ // Don't do this twice if we have a colspan.
578
+ if (j === 0 || map[i][j] != map[i][j - 1]) {
579
+ var $cell = $(map[i][j]);
580
+
581
+ // We should decrease rowspan.
582
+ if (parseInt($cell.attr('rowspan'), 10) > 1) {
583
+ var rowspan = parseInt($cell.attr('rowspan'), 10) - 1;
584
+
585
+ if (rowspan == 1) {
586
+ $cell.removeAttr('rowspan');
587
+ } else {
588
+ $cell.attr('rowspan', rowspan);
589
+ }
590
+ }
591
+
592
+ // We might need to move tds on the row below if we have a rowspan that starts here.
593
+ if (i < map.length - 1 && map[i][j] == map[i + 1][j] && (i === 0 || map[i][j] != map[i - 1][j])) {
594
+ // Move td to the row below.
595
+ var td = map[i][j];
596
+ var col = j;
597
+
598
+ // Go back until we get the last element (we might have colspan).
599
+ while (col > 0 && map[i][col] == map[i][col - 1]) {
600
+ col--;
601
+ }
602
+
603
+ // Add td at the beginning of the row below.
604
+ if (col === 0) {
605
+ $($table.find('tr').not($table.find('table tr')).get(i + 1)).prepend(td);
606
+
607
+ } else {
608
+ $(map[i + 1][col - 1]).after(td);
609
+ }
610
+ }
611
+ }
612
+ }
613
+
614
+ // Remove tbody or thead if there are no more rows.
615
+ var $row_parent = $row.parent();
616
+ $row.remove();
617
+ if ($row_parent.find('tr').length === 0) {
618
+ $row_parent.remove();
619
+ }
620
+
621
+ // Table has changed.
622
+ map = _tableMap($table);
623
+ }
624
+
625
+ // Update cursor position
626
+ if (selection.min_i > 0) {
627
+ // Place cursor in the row above selection.
628
+ editor.selection.setAtEnd(map[selection.min_i - 1][0]);
629
+ }
630
+ else {
631
+ // Place cursor in the row below selection.
632
+ editor.selection.setAtEnd(map[0][0]);
633
+ }
634
+ editor.selection.restore();
635
+
636
+ // Hide table edit popup.
637
+ editor.popups.hide('table.edit');
638
+ }
639
+ }
640
+ }
641
+
642
+ /*
643
+ * Insert column method.
644
+ */
645
+ function insertColumn (position) {
646
+ var $table = selectedTable();
647
+
648
+ // We have selection in a table.
649
+ if ($table.length > 0) {
650
+ // Create a table map.
651
+ var map = _tableMap();
652
+
653
+ // Get selected cells from the table.
654
+ var selection = _currentSelection(map);
655
+
656
+ // Reference row.
657
+ var ref_col;
658
+
659
+ if (position == 'before') {
660
+ ref_col = selection.min_j;
661
+ } else {
662
+ ref_col = selection.max_j;
663
+ }
664
+
665
+ // Old and new column widths.
666
+ var old_width = 100 / map[0].length;
667
+ var new_width = 100 / (map[0].length + 1);
668
+
669
+ // Go through all cells and get their initial (old) widths.
670
+ var $cell;
671
+
672
+ $table.find('th, td').each (function () {
673
+ $cell = $(this);
674
+ $cell.data('old-width', $cell.outerWidth() / $table.outerWidth() * 100);
675
+ });
676
+
677
+ // Parse each row to insert a new td.
678
+ $table.find('tr').not($table.find('table tr')).each (function (index) {
679
+ // Get the exact td index before / after which we have to add the new td.
680
+ // ref_col means the table column number, but this is not the same with the td number in a row.
681
+ // We might have colspan or rowspan greater than 1.
682
+ var $row = $(this);
683
+ var col_no = 0;
684
+ var td_no = 0;
685
+ var ref_td;
686
+
687
+ // Start with the first td (td_no = 0) in the current row.
688
+ // Sum colspans (col_no) to see when we reach ref_col.
689
+ // Summing colspans we get the same number with the table column number.
690
+ while (col_no - 1 < ref_col) {
691
+ // Get current td.
692
+ ref_td = $row.find('> th, > td').get(td_no);
693
+
694
+ // There are no more tds in this row.
695
+ if (!ref_td) {
696
+ ref_td = null;
697
+ break;
698
+ }
699
+
700
+ // The current td is the same with the td from the table map.
701
+ if (ref_td == map[index][col_no]) {
702
+ // The current td might have colspan.
703
+ col_no += parseInt($(ref_td).attr('colspan'), 10) || 1;
704
+
705
+ // Go to the next td on the current row.
706
+ td_no++;
707
+ }
708
+
709
+ // If current td is not the same with the td from the table map.
710
+ // This means that this table cell (map[index][td_no]) has rowspan.
711
+ // There is at least one td less on this row due to rowspan (based on map[index][td_no] colspan value).
712
+ // We want to count this as a column as well.
713
+ else {
714
+ col_no += parseInt($(map[index][col_no]).attr('colspan'), 10) || 1;
715
+
716
+ // ref_td is one td ahead. Get previous td if we want to insert column after.
717
+ if (position == 'after') {
718
+ // There is a rowspan and so ref_td is the first td, but it is not in the first column.
719
+ if (td_no === 0) {
720
+ ref_td = -1;
721
+
722
+ } else {
723
+ ref_td = $row.find('> th, > td').get(td_no - 1);
724
+ }
725
+ }
726
+ }
727
+ }
728
+
729
+ var $ref_td = $(ref_td);
730
+
731
+ // If the computed column number is higher than the reference number
732
+ // then on this row we have a colspan longer than the reference column.
733
+ // When adding a column after we should increase colspan on this row.
734
+ //
735
+ // If we want to add a column before, but the td on the reference column is
736
+ // the same with the previous one then we have a td with colspan that
737
+ // starts before the column reference number.
738
+ // When adding a column before we should increase colspan on this row.
739
+ if ((position == 'after' && col_no - 1 > ref_col) ||
740
+ (position == 'before' && ref_col > 0 && map[index][ref_col] == map[index][ref_col - 1])) {
741
+
742
+ // Don't increase twice for rowspan.
743
+ if (index === 0 || (index > 0 && map[index][ref_col] != map[index - 1][ref_col])) {
744
+ var colspan = parseInt($ref_td.attr('colspan'), 10) + 1;
745
+
746
+ $ref_td.attr('colspan', colspan)
747
+
748
+ // Update this cell's width.
749
+ $ref_td.css('width', ($ref_td.data('old-width') * new_width / old_width + new_width).toFixed(4) + '%');
750
+ $ref_td.removeData('old-width');
751
+ }
752
+
753
+ }
754
+
755
+ else {
756
+ // Create HTML for a new cell.
757
+ var td;
758
+
759
+ // Might be a td or a th.
760
+ if ($row.find('th').length > 0) {
761
+ td = '<th style="width: ' + new_width.toFixed(4) + '%;"><br></th>';
762
+ }
763
+ else {
764
+ td = '<td style="width: ' + new_width.toFixed(4) + '%;"><br></td>';
765
+ }
766
+
767
+ // Insert exactly at the beginning.
768
+ if (ref_td == -1) {
769
+ $row.prepend(td);
770
+
771
+ // Insert exactly at the end.
772
+ } else if (ref_td == null) {
773
+ $row.append(td);
774
+
775
+ // Insert td on the current row.
776
+ } else {
777
+ if (position == 'before') {
778
+ $ref_td.before(td);
779
+ }
780
+
781
+ else if (position == 'after') {
782
+ $ref_td.after(td);
783
+ }
784
+ }
785
+ }
786
+ });
787
+
788
+ // Parse each row to update cells' width.
789
+ $table.find('th, td').each (function () {
790
+ $cell = $(this);
791
+
792
+ // Update width and remove data.
793
+ if ($cell.data('old-width')) {
794
+ $cell.css('width', ($cell.data('old-width') * new_width / old_width).toFixed(4) + '%');
795
+ $cell.removeData('old-width')
796
+ }
797
+ });
798
+
799
+ // Reposition table edit popup.
800
+ if (editor.popups.isVisible('table.edit')) {
801
+ _showEditPopup();
802
+ }
803
+ }
804
+ }
805
+
806
+ /*
807
+ * Delete column method.
808
+ */
809
+ function deleteColumn () {
810
+ var $table = selectedTable();
811
+
812
+ // We have selection in a table.
813
+ if ($table.length > 0) {
814
+ var i;
815
+ var j;
816
+ var $cell;
817
+
818
+ // Create a table map.
819
+ var map = _tableMap();
820
+
821
+ // Get selected cells from the table.
822
+ var selection = _currentSelection(map);
823
+
824
+ // If all the rows are selected then delete the entire table.
825
+ if (selection.min_j === 0 && selection.max_j == map[0].length - 1) {
826
+ remove();
827
+
828
+ } else {
829
+ // Old and new column widths.
830
+ var old_width = 100 / map[0].length;
831
+ var new_width = 100 / (map[0].length - selection.max_j + selection.min_j - 1);
832
+
833
+ // Go through all cells and get their initial (old) widths.
834
+ $table.find('th, td').each (function () {
835
+ $cell = $(this);
836
+
837
+ if (!$cell.hasClass('fr-selected-cell')) {
838
+ $cell.data('old-width', $cell.outerWidth() / $table.outerWidth() * 100);
839
+ }
840
+ });
841
+
842
+ // We should delete selected columns.
843
+ for (j = selection.max_j; j >= selection.min_j; j--) {
844
+ // Go through the table map to check for colspan.
845
+ for (i = 0; i < map.length; i++) {
846
+ // Don't do this twice if we have a rowspan.
847
+ if (i === 0 || map[i][j] != map[i - 1][j]) {
848
+ // We should decrease colspan.
849
+ $cell = $(map[i][j]);
850
+
851
+ if (parseInt($cell.attr('colspan'), 10) > 1) {
852
+ var colspan = parseInt($cell.attr('colspan'), 10) - 1;
853
+
854
+ if (colspan == 1) {
855
+ $cell.removeAttr('colspan');
856
+ } else {
857
+ $cell.attr('colspan', colspan);
858
+ }
859
+
860
+ // Update cell width.
861
+ $cell.css('width', (($cell.data('old-width') - _columnWidth(j, map)) * new_width / old_width).toFixed(4) + '%');
862
+ $cell.removeData('old-width');
863
+
864
+ // If there is no colspan delete the cell.
865
+ } else {
866
+ // We might need to delete the row too if it is empty.
867
+ var $row = $($cell.parent().get(0));
868
+
869
+ $cell.remove();
870
+
871
+ // Check if there are any more tds in the current row.
872
+ if ($row.find('> th, > td').length === 0) {
873
+ // Check if it is okay to delete the tr.
874
+ if ($row.prev().length === 0 || $row.next().length === 0 ||
875
+ $row.prev().find('> th[rowspan], > td[rowspan]').length < $row.prev().find('> th, > td').length) {
876
+ $row.remove();
877
+ }
878
+ }
879
+ }
880
+ }
881
+ }
882
+ }
883
+
884
+ // Update cursor position
885
+ if (selection.min_j > 0) {
886
+ // Place cursor in the column before selection.
887
+ editor.selection.setAtEnd(map[selection.min_i][selection.min_j - 1]);
888
+ }
889
+ else {
890
+ // Place cursor in the column after selection.
891
+ editor.selection.setAtEnd(map[selection.min_i][0]);
892
+ }
893
+ editor.selection.restore();
894
+
895
+ // Hide table edit popup.
896
+ editor.popups.hide('table.edit');
897
+
898
+ // Scale current cells' width after column has been deleted.
899
+ $table.find('th, td').each (function () {
900
+ $cell = $(this);
901
+
902
+ // Update width and remove data.
903
+ if ($cell.data('old-width')) {
904
+ $cell.css('width', ($cell.data('old-width') * new_width / old_width).toFixed(4) + '%');
905
+ $cell.removeData('old-width')
906
+ }
907
+ });
908
+ }
909
+ }
910
+ }
911
+
912
+ /*
913
+ * Merge selected cells method.
914
+ */
915
+ function mergeCells () {
916
+ // We have more than one cell selected in a table. Cannot merge td and th.
917
+ if (selectedCells().length > 1 && (editor.$el.find('th.fr-selected-cell').length === 0 || editor.$el.find('td.fr-selected-cell').length === 0)) {
918
+ // Create a table map.
919
+ var map = _tableMap();
920
+
921
+ // Get selected cells.
922
+ var selection = _currentSelection(map);
923
+
924
+ var i;
925
+ var $cell;
926
+ var $row;
927
+ var cells = editor.$el.find('.fr-selected-cell');
928
+ var $first_cell = $(cells[0]);
929
+ var $first_row = $first_cell.parent();
930
+ var first_row_cells = $first_row.find('.fr-selected-cell');
931
+ var $current_table = $first_cell.closest('table');
932
+ var content = $first_cell.html();
933
+ var width = 0;
934
+
935
+ // Update cell width.
936
+ for (i = 0; i < first_row_cells.length; i++) {
937
+ width += $(first_row_cells[i]).outerWidth();
938
+ }
939
+
940
+ $first_cell.css('width', (width / $first_row.outerWidth() * 100).toFixed(4) + '%');
941
+
942
+ // Set the colspan for the merged cells.
943
+ if (selection.min_j < selection.max_j) {
944
+ $first_cell.attr('colspan', selection.max_j - selection.min_j + 1);
945
+ }
946
+
947
+ // Set the rowspan for the merged cells.
948
+ if (selection.min_i < selection.max_i) {
949
+ $first_cell.attr('rowspan', selection.max_i - selection.min_i + 1);
950
+ }
951
+
952
+ // Go through all selected cells to merge their content.
953
+ for (i = 1; i < cells.length; i++) {
954
+ $cell = $(cells[i])
955
+
956
+ // If cell is empty, don't add only <br> tags.
957
+ if ($cell.html() != '<br>' && $cell.html() !== '') {
958
+ content += '<br>' + $cell.html();
959
+ }
960
+
961
+ // Remove cell.
962
+ $cell.remove();
963
+ }
964
+
965
+ // Set the HTML content.
966
+ $first_cell.html(content);
967
+ editor.selection.setAtEnd($first_cell.get(0));
968
+ editor.selection.restore();
969
+
970
+ // Enable toolbar.
971
+ editor.toolbar.enable();
972
+
973
+ // Merge is done, check if we have empty trs to clean.
974
+ var empty_trs = $current_table.find('tr:empty');
975
+
976
+ for (i = empty_trs.length - 1; i >= 0; i--) {
977
+ $row = $(empty_trs[i]);
978
+
979
+ // Check if it is okay to delete the tr.
980
+ if ($row.prev().length === 0 || $row.next().length === 0 ||
981
+ $row.prev().find('> th[rowspan], > td[rowspan]').length < $row.prev().find('> th, > td').length) {
982
+ $row.remove();
983
+ }
984
+ }
985
+
986
+ // Reposition table edit popup.
987
+ _showEditPopup();
988
+ }
989
+ }
990
+
991
+ /*
992
+ * Split cell horizontally method.
993
+ */
994
+ function splitCellHorizontally () {
995
+ // We have only one cell selected in a table.
996
+ if (selectedCells().length == 1) {
997
+ var $selected_cell = editor.$el.find('.fr-selected-cell');
998
+ var $current_row = $selected_cell.parent();
999
+ var $current_table = $selected_cell.closest('table');
1000
+ var current_rowspan = parseInt($selected_cell.attr('rowspan'), 10);
1001
+
1002
+ // Create a table map.
1003
+ var map = _tableMap();
1004
+ var cell_origin = _cellOrigin($selected_cell.get(0), map);
1005
+
1006
+ // Create new td.
1007
+ var $new_td = $selected_cell.clone().html('<br>');
1008
+
1009
+ // Cell has rowspan.
1010
+ if (current_rowspan > 1) {
1011
+ // Split current cell's rowspan.
1012
+ var new_rowspan = Math.ceil(current_rowspan / 2);
1013
+
1014
+ if (new_rowspan > 1) {
1015
+ $selected_cell.attr('rowspan', new_rowspan);
1016
+ } else {
1017
+ $selected_cell.removeAttr('rowspan');
1018
+ }
1019
+
1020
+ // Update new td's rowspan.
1021
+ if (current_rowspan - new_rowspan > 1) {
1022
+ $new_td.attr('rowspan', current_rowspan - new_rowspan);
1023
+ } else {
1024
+ $new_td.removeAttr('rowspan');
1025
+ }
1026
+
1027
+ // Find where we should insert the new td.
1028
+ var row = cell_origin.row + new_rowspan;
1029
+ var col = cell_origin.col === 0 ? cell_origin.col : cell_origin.col - 1;
1030
+
1031
+ // Go back until we find a td on this row. We might have colspans and rowspans.
1032
+ while (col >= 0 && (map[row][col] == map[row][col - 1] || (row > 0 && map[row][col] == map[row - 1][col]))) {
1033
+ col--;
1034
+ }
1035
+
1036
+ if (col == -1) {
1037
+ // We couldn't find a previous td on this row. Prepend the new td.
1038
+ $($current_table.find('tr').not($current_table.find('table tr')).get(row)).prepend($new_td);
1039
+
1040
+ } else {
1041
+ $(map[row][col]).after($new_td);
1042
+ }
1043
+
1044
+ } else {
1045
+ // Add new row bellow with only one cell.
1046
+ var $row = $('<tr>').append($new_td);
1047
+ var i;
1048
+
1049
+ // Increase rowspan for all cells on the current row.
1050
+ for (i = 0; i < map[0].length; i++) {
1051
+ // Don't do this twice if we have a colspan.
1052
+ if (i === 0 || map[cell_origin.row][i] != map[cell_origin.row][i - 1]) {
1053
+ var $cell = $(map[cell_origin.row][i]);
1054
+
1055
+ if (!$cell.is($selected_cell)) {
1056
+ $cell.attr('rowspan', (parseInt($cell.attr('rowspan'), 10) || 1) + 1);
1057
+ }
1058
+ }
1059
+ }
1060
+
1061
+ $current_row.after($row);
1062
+ }
1063
+
1064
+ // Remove selection
1065
+ _removeSelection();
1066
+
1067
+ // Hide table edit popup.
1068
+ editor.popups.hide('table.edit');
1069
+ }
1070
+ }
1071
+
1072
+ /*
1073
+ * Split cell vertically method.
1074
+ */
1075
+ function splitCellVertically () {
1076
+ // We have only one cell selected in a table.
1077
+ if (selectedCells().length == 1) {
1078
+ var $selected_cell = editor.$el.find('.fr-selected-cell');
1079
+ var current_colspan = parseInt($selected_cell.attr('colspan'), 10) || 1;
1080
+ var parent_width = $selected_cell.parent().outerWidth();
1081
+ var width = $selected_cell.outerWidth();
1082
+
1083
+ // Create new td.
1084
+ var $new_td = $selected_cell.clone().html('<br>');
1085
+
1086
+ // Create a table map.
1087
+ var map = _tableMap();
1088
+ var cell_origin = _cellOrigin($selected_cell.get(0), map);
1089
+
1090
+ if (current_colspan > 1) {
1091
+ // Split current colspan.
1092
+ var new_colspan = Math.ceil(current_colspan / 2);
1093
+
1094
+ width = _columnsWidth(cell_origin.col, cell_origin.col + new_colspan - 1, map) / parent_width * 100;
1095
+ var new_width = _columnsWidth(cell_origin.col + new_colspan, cell_origin.col + current_colspan - 1, map) / parent_width * 100;
1096
+
1097
+ // Update colspan for current cell.
1098
+ if (new_colspan > 1) {
1099
+ $selected_cell.attr('colspan', new_colspan);
1100
+ } else {
1101
+ $selected_cell.removeAttr('colspan');
1102
+ }
1103
+
1104
+ // Update new td's colspan.
1105
+ if (current_colspan - new_colspan > 1) {
1106
+ $new_td.attr('colspan', current_colspan - new_colspan);
1107
+ } else {
1108
+ $new_td.removeAttr('colspan');
1109
+ }
1110
+
1111
+ // Update cell width.
1112
+ $selected_cell.css('width', width.toFixed(4) + '%');
1113
+ $new_td.css('width', new_width.toFixed(4) + '%');
1114
+
1115
+ // Increase colspan for all cells on the current column.
1116
+ } else {
1117
+ var i;
1118
+
1119
+ for (i = 0; i < map.length; i++) {
1120
+ // Don't do this twice if we have a rowspan.
1121
+ if (i === 0 || map[i][cell_origin.col] != map[i - 1][cell_origin.col]) {
1122
+ var $cell = $(map[i][cell_origin.col]);
1123
+
1124
+ if (!$cell.is($selected_cell)) {
1125
+ var colspan = (parseInt($cell.attr('colspan'), 10) || 1) + 1;
1126
+ $cell.attr('colspan', colspan);
1127
+ }
1128
+ }
1129
+ }
1130
+
1131
+ // Update cell width.
1132
+ width = width / parent_width * 100 / 2;
1133
+
1134
+ $selected_cell.css('width', width.toFixed(4) + '%');
1135
+ $new_td.css('width', width.toFixed(4) + '%');
1136
+ }
1137
+
1138
+ // Add a new td after the current one.
1139
+ $selected_cell.after($new_td);
1140
+
1141
+ // Remove selection
1142
+ _removeSelection();
1143
+
1144
+ // Hide table edit popup.
1145
+ editor.popups.hide('table.edit');
1146
+ }
1147
+ }
1148
+
1149
+ /*
1150
+ * Set background color to selected cells.
1151
+ */
1152
+ function setBackground (color) {
1153
+ // Set background color.
1154
+ if (color != 'REMOVE') {
1155
+ editor.$el.find('.fr-selected-cell').css('background-color', editor.helpers.HEXtoRGB(color));
1156
+ }
1157
+
1158
+ // Remove background color.
1159
+ else {
1160
+ editor.$el.find('.fr-selected-cell').css('background-color', '');
1161
+ }
1162
+ }
1163
+
1164
+ /*
1165
+ * Set vertical align to selected cells.
1166
+ */
1167
+ function verticalAlign (val) {
1168
+ editor.$el.find('.fr-selected-cell').css('vertical-align', val);
1169
+ }
1170
+
1171
+ /*
1172
+ * Apply horizontal alignment to selected cells.
1173
+ */
1174
+ function horizontalAlign (val) {
1175
+ editor.$el.find('.fr-selected-cell').css('text-align', val);
1176
+ }
1177
+
1178
+ /**
1179
+ * Apply specific style to selected table or selected cells.
1180
+ * val class to apply.
1181
+ * obj table or selected cells.
1182
+ * multiple_styles editor.opts.tableStyles or editor.opts.tableCellStyles.
1183
+ * style editor.opts.tableStyles or editor.opts.tableCellStyles
1184
+ */
1185
+ function applyStyle (val, obj, multiple_styles, styles) {
1186
+ if (obj.length > 0) {
1187
+ // Remove multiple styles.
1188
+ if (!multiple_styles) {
1189
+ var classes = Object.keys(styles);
1190
+ classes.splice(classes.indexOf(val), 1);
1191
+ obj.removeClass(classes.join(' '));
1192
+ }
1193
+
1194
+ obj.toggleClass(val);
1195
+ }
1196
+ }
1197
+
1198
+ /*
1199
+ * Create a table map.
1200
+ */
1201
+ function _tableMap (table) {
1202
+ table = table || null;
1203
+
1204
+ var map = [];
1205
+
1206
+ if (table == null && selectedCells().length > 0) {
1207
+ table = selectedTable();
1208
+ }
1209
+
1210
+ if (table) {
1211
+ var $table = $(table);
1212
+ $table.find('tr').not($table.find('table tr')).each (function (row, tr) {
1213
+ var $tr = $(tr);
1214
+
1215
+ var c_index = 0;
1216
+ $tr.find('> th, > td').each (function (col, td) {
1217
+ var $td = $(td);
1218
+ var cspan = parseInt($td.attr('colspan'), 10) || 1;
1219
+ var rspan = parseInt($td.attr('rowspan'), 10) || 1;
1220
+
1221
+ for (var i = row; i < row + rspan; i++) {
1222
+ for (var j = c_index; j < c_index + cspan; j++) {
1223
+ if (!map[i]) map[i] = [];
1224
+ if (!map[i][j]) {
1225
+ map[i][j] = td;
1226
+ } else {
1227
+ c_index++;
1228
+ }
1229
+ }
1230
+ }
1231
+
1232
+ c_index += cspan;
1233
+ })
1234
+ });
1235
+
1236
+ return map;
1237
+ }
1238
+ }
1239
+
1240
+ /*
1241
+ * Get the i, j coordinates of a cell in the table map.
1242
+ * These are the coordinates where the cell starts.
1243
+ */
1244
+ function _cellOrigin (td, map) {
1245
+ for (var i = 0; i < map.length; i++) {
1246
+ for (var j = 0; j < map[i].length; j++) {
1247
+ if (map[i][j] == td) {
1248
+ return {
1249
+ row: i,
1250
+ col: j
1251
+ };
1252
+ }
1253
+ }
1254
+ }
1255
+ }
1256
+
1257
+ /*
1258
+ * Get the i, j coordinates where a cell ends in the table map.
1259
+ */
1260
+ function _cellEnds (origin_i, origin_j, map) {
1261
+ var max_i = origin_i + 1;
1262
+ var max_j = origin_j + 1;
1263
+
1264
+ // Compute max_i
1265
+ while (max_i < map.length) {
1266
+ if (map[max_i][origin_j] != map[origin_i][origin_j]) {
1267
+ max_i--;
1268
+ break;
1269
+ }
1270
+
1271
+ max_i++;
1272
+ }
1273
+
1274
+ if (max_i == map.length) {
1275
+ max_i--;
1276
+ }
1277
+
1278
+ // Compute max_j
1279
+ while (max_j < map[origin_i].length) {
1280
+ if (map[origin_i][max_j] != map[origin_i][origin_j]) {
1281
+ max_j--;
1282
+ break;
1283
+ }
1284
+
1285
+ max_j++;
1286
+ }
1287
+
1288
+ if (max_j == map[origin_i].length) {
1289
+ max_j--;
1290
+ }
1291
+
1292
+ return {
1293
+ row: max_i,
1294
+ col: max_j
1295
+ };
1296
+ }
1297
+
1298
+ /*
1299
+ * Remove selection from cells.
1300
+ */
1301
+ function _removeSelection () {
1302
+ var cells = editor.$el.find('.fr-selected-cell');
1303
+
1304
+ if (cells.length > 0) {
1305
+ // Remove selection.
1306
+ cells.each (function () {
1307
+ var $cell = $(this);
1308
+
1309
+ $cell.removeClass('fr-selected-cell');
1310
+
1311
+ if ($cell.attr('class') === '') {
1312
+ $cell.removeAttr('class');
1313
+ }
1314
+ });
1315
+ }
1316
+ }
1317
+
1318
+ /*
1319
+ * Clear selection to prevent Firefox cell selection.
1320
+ */
1321
+ function _clearSelection () {
1322
+ setTimeout(function () {
1323
+ editor.selection.clear();
1324
+
1325
+ // Prevent text selection while selecting multiple cells.
1326
+ // Happens in Chrome.
1327
+ editor.$el.addClass('fr-no-selection');
1328
+
1329
+ // Cursor will not appear if we don't make blur.
1330
+ editor.$el.blur();
1331
+ }, 0);
1332
+ }
1333
+
1334
+ /*
1335
+ * Get current selected cells coordintates.
1336
+ */
1337
+ function _currentSelection (map) {
1338
+ var min_i = map.length;
1339
+ var max_i = 0;
1340
+ var min_j = map[0].length;
1341
+ var max_j = 0;
1342
+ var i;
1343
+ var cells = editor.$el.find('.fr-selected-cell');
1344
+
1345
+ for (i = 0; i < cells.length; i++) {
1346
+ var cellOrigin = _cellOrigin(cells[i], map);
1347
+ var cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);
1348
+
1349
+ min_i = Math.min(cellOrigin.row, min_i);
1350
+ max_i = Math.max(cellEnd.row, max_i);
1351
+ min_j = Math.min(cellOrigin.col, min_j);
1352
+ max_j = Math.max(cellEnd.col, max_j);
1353
+ }
1354
+
1355
+ return {
1356
+ min_i: min_i,
1357
+ max_i: max_i,
1358
+ min_j: min_j,
1359
+ max_j: max_j
1360
+ };
1361
+ }
1362
+
1363
+ /*
1364
+ * Minimum and maximum coordinates for the selection in the table map.
1365
+ */
1366
+ function _selectionLimits (min_i, max_i, min_j, max_j, map) {
1367
+ var first_i = min_i;
1368
+ var last_i = max_i;
1369
+ var first_j = min_j;
1370
+ var last_j = max_j;
1371
+ var i;
1372
+ var j;
1373
+ var cellOrigin;
1374
+ var cellEnd;
1375
+
1376
+ // Go through first and last columns.
1377
+ for (i = first_i; i <= last_i; i++) {
1378
+ // First column.
1379
+ if ((parseInt($(map[i][first_j]).attr('rowspan'), 10) || 1) > 1 ||
1380
+ (parseInt($(map[i][first_j]).attr('colspan'), 10) || 1) > 1) {
1381
+ cellOrigin = _cellOrigin(map[i][first_j], map);
1382
+ cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);
1383
+
1384
+ first_i = Math.min(cellOrigin.row, first_i);
1385
+ last_i = Math.max(cellEnd.row, last_i);
1386
+ first_j = Math.min(cellOrigin.col, first_j);
1387
+ last_j = Math.max(cellEnd.col, last_j);
1388
+ }
1389
+
1390
+ // Last column.
1391
+ if ((parseInt($(map[i][last_j]).attr('rowspan'), 10) || 1) > 1 ||
1392
+ (parseInt($(map[i][last_j]).attr('colspan'), 10) || 1) > 1) {
1393
+ cellOrigin = _cellOrigin(map[i][last_j], map);
1394
+ cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);
1395
+
1396
+ first_i = Math.min(cellOrigin.row, first_i);
1397
+ last_i = Math.max(cellEnd.row, last_i);
1398
+ first_j = Math.min(cellOrigin.col, first_j);
1399
+ last_j = Math.max(cellEnd.col, last_j);
1400
+ }
1401
+ }
1402
+
1403
+ // Go through first and last rows.
1404
+ for (j = first_j; j <= last_j; j++) {
1405
+ // First row.
1406
+ if ((parseInt($(map[first_i][j]).attr('rowspan'), 10) || 1) > 1 ||
1407
+ (parseInt($(map[first_i][j]).attr('colspan'), 10) || 1) > 1) {
1408
+ cellOrigin = _cellOrigin(map[first_i][j], map);
1409
+ cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);
1410
+
1411
+ first_i = Math.min(cellOrigin.row, first_i);
1412
+ last_i = Math.max(cellEnd.row, last_i);
1413
+ first_j = Math.min(cellOrigin.col, first_j);
1414
+ last_j = Math.max(cellEnd.col, last_j);
1415
+ }
1416
+
1417
+ // Last column.
1418
+ if ((parseInt($(map[last_i][j]).attr('rowspan'), 10) || 1) > 1 ||
1419
+ (parseInt($(map[last_i][j]).attr('colspan'), 10) || 1) > 1) {
1420
+ cellOrigin = _cellOrigin(map[last_i][j], map);
1421
+ cellEnd = _cellEnds(cellOrigin.row, cellOrigin.col, map);
1422
+
1423
+ first_i = Math.min(cellOrigin.row, first_i);
1424
+ last_i = Math.max(cellEnd.row, last_i);
1425
+ first_j = Math.min(cellOrigin.col, first_j);
1426
+ last_j = Math.max(cellEnd.col, last_j);
1427
+ }
1428
+ }
1429
+
1430
+ if (first_i == min_i && last_i == max_i && first_j == min_j && last_j == max_j) {
1431
+ return {
1432
+ min_i: min_i,
1433
+ max_i: max_i,
1434
+ min_j: min_j,
1435
+ max_j: max_j
1436
+ };
1437
+
1438
+ } else {
1439
+ return _selectionLimits(first_i, last_i, first_j, last_j, map);
1440
+ }
1441
+ }
1442
+
1443
+ /*
1444
+ * Get the left and right offset position for the current selection.
1445
+ */
1446
+ function _selectionOffset (map) {
1447
+ var selection = _currentSelection(map);
1448
+
1449
+ // Top left cell.
1450
+ var $tl = $(map[selection.min_i][selection.min_j]);
1451
+
1452
+ // Top right cell.
1453
+ var $tr = $(map[selection.min_i][selection.max_j]);
1454
+
1455
+ // Bottom left cell.
1456
+ var $bl = $(map[selection.max_i][selection.min_j]);
1457
+
1458
+ var left = $tl.offset().left
1459
+ var right = $tr.offset().left + $tr.outerWidth();
1460
+ var top = $tl.offset().top;
1461
+ var bottom = $bl.offset().top + $bl.outerHeight();
1462
+
1463
+ return {
1464
+ left: left,
1465
+ right: right,
1466
+ top: top,
1467
+ bottom: bottom
1468
+ };
1469
+ }
1470
+
1471
+ /*
1472
+ * Select table cells
1473
+ */
1474
+ function _selectCells (firstCell, lastCell) {
1475
+ // If the first and last cells are the same then just select it.
1476
+ if ($(firstCell).is(lastCell)) {
1477
+ // Remove previous selection.
1478
+ _removeSelection();
1479
+
1480
+ // Enable editor toolbar.
1481
+ editor.edit.on();
1482
+
1483
+ $(firstCell).addClass('fr-selected-cell');
1484
+
1485
+ // Select multiple celss.
1486
+ } else {
1487
+ // Prevent Firefox cell selection.
1488
+ _clearSelection();
1489
+
1490
+ // Turn editor toolbar off.
1491
+ editor.edit.off();
1492
+
1493
+ // Create a table map.
1494
+ var map = _tableMap();
1495
+
1496
+ // Get first and last cell's i and j map coordinates.
1497
+ var firstCellOrigin = _cellOrigin(firstCell, map);
1498
+ var lastCellOrigin = _cellOrigin(lastCell, map);
1499
+
1500
+ // Some cells between these coordinates might have colspan or rowspan.
1501
+ // The selected area exceeds first and last cells' coordinates.
1502
+ var limits = _selectionLimits(Math.min(firstCellOrigin.row, lastCellOrigin.row),
1503
+ Math.max(firstCellOrigin.row, lastCellOrigin.row),
1504
+ Math.min(firstCellOrigin.col, lastCellOrigin.col),
1505
+ Math.max(firstCellOrigin.col, lastCellOrigin.col),
1506
+ map);
1507
+ // Remove previous selection.
1508
+ _removeSelection();
1509
+
1510
+ // Select all cells between the first and last cell.
1511
+ for (var i = limits.min_i; i <= limits.max_i; i++) {
1512
+ for (var j = limits.min_j; j <= limits.max_j; j++) {
1513
+ $(map[i][j]).addClass('fr-selected-cell');
1514
+ }
1515
+ }
1516
+ }
1517
+ }
1518
+
1519
+ /*
1520
+ * Get the cell under the mouse cursor.
1521
+ */
1522
+ function _getCellUnder (e) {
1523
+ var cell = null;
1524
+ var $target = $(e.target);
1525
+
1526
+ if (e.target.tagName == 'TD' || e.target.tagName == 'TH') {
1527
+ cell = e.target;
1528
+ } else if ($target.closest('td').length > 0) {
1529
+ cell = $target.closest('td').get(0);
1530
+ } else if ($target.closest('th').length > 0) {
1531
+ cell = $target.closest('th').get(0);
1532
+ }
1533
+
1534
+ // Cell should reside inside editor.
1535
+ if (editor.$el.find(cell).length === 0) return null;
1536
+
1537
+ return cell;
1538
+ }
1539
+
1540
+ /*
1541
+ * Stop table cell editing and allow text editing.
1542
+ */
1543
+ function _stopEdit () {
1544
+ // Clear previous selection.
1545
+ _removeSelection();
1546
+
1547
+ // Hide table edit popup.
1548
+ editor.popups.hide('table.edit');
1549
+ }
1550
+
1551
+ /*
1552
+ * Mark that mouse is down.
1553
+ */
1554
+ function _mouseDown (e) {
1555
+ var cell = _getCellUnder(e);
1556
+
1557
+ // Stop table editing if user clicks outside the table.
1558
+ if (selectedCells().length > 0 && !cell) {
1559
+ _stopEdit();
1560
+ }
1561
+
1562
+ // Only do mouseDown if the editor is not disabled by user.
1563
+ if (!editor.edit.isDisabled() || editor.popups.isVisible('table.edit')) {
1564
+ // On left click.
1565
+ if (e.which == 1 && !(e.which == 1 && editor.helpers.isMac() && e.ctrlKey)) {
1566
+ mouseDownFlag = true;
1567
+
1568
+ // User clicked on a table cell.
1569
+ if (cell) {
1570
+ // We always have to clear previous selection except when using shift key to select multiple cells.
1571
+ if (selectedCells().length > 0 && !e.shiftKey) {
1572
+ _stopEdit();
1573
+ }
1574
+
1575
+ e.stopPropagation();
1576
+
1577
+ editor.events.trigger('image.hideResizer');
1578
+ editor.events.trigger('video.hideResizer');
1579
+
1580
+ // Keep record of left mouse click being down
1581
+ mouseDownCellFlag = true;
1582
+
1583
+ var tag_name = cell.tagName.toLowerCase();
1584
+
1585
+ // Select multiple cells using Shift key
1586
+ if (e.shiftKey && editor.$el.find(tag_name + '.fr-selected-cell').length > 0) {
1587
+
1588
+ // Cells must be in the same table.
1589
+ if ($(editor.$el.find(tag_name + '.fr-selected-cell').closest('table')).is($(cell).closest('table'))) {
1590
+ // Select cells between.
1591
+ _selectCells(mouseDownCell, cell);
1592
+
1593
+ // Do nothing if cells are not in the same table.
1594
+ } else {
1595
+ // Prevent Firefox selection.
1596
+ _clearSelection();
1597
+ }
1598
+ }
1599
+
1600
+ else {
1601
+ // Prevent Firefox selection for ctrl / cmd key.
1602
+ // https://github.com/froala/wysiwyg-editor/issues/1323:
1603
+ // - we have more than one cell selected or
1604
+ // - selection is starting in another cell than the one we clicked on.
1605
+ if ((editor.keys.ctrlKey(e) || e.shiftKey) && (selectedCells().length > 1 || ($(cell).find(editor.selection.element()).length === 0 && !$(cell).is(editor.selection.element())))) {
1606
+ _clearSelection();
1607
+ }
1608
+
1609
+ // Save cell where mouse has been clicked
1610
+ mouseDownCell = cell;
1611
+
1612
+ // Select cell.
1613
+ _selectCells(mouseDownCell, mouseDownCell);
1614
+ }
1615
+ }
1616
+ }
1617
+
1618
+ // On right click stop table editing.
1619
+ else if ((e.which == 3 || (e.which == 1 && editor.helpers.isMac() && e.ctrlKey)) && cell) {
1620
+ _stopEdit();
1621
+ }
1622
+ }
1623
+ }
1624
+
1625
+ /*
1626
+ * Notify that mouse is no longer pressed.
1627
+ */
1628
+ function _mouseUp (e) {
1629
+ // User clicked somewhere else in the editor (except the toolbar).
1630
+ // We need this because mouse down is not triggered outside the editor.
1631
+ if (!mouseDownCellFlag && !editor.$tb.is(e.target) && !editor.$tb.is($(e.target).closest(editor.$tb.get(0)))) {
1632
+ if (selectedCells().length > 0) {
1633
+ editor.toolbar.enable();
1634
+ }
1635
+
1636
+ _removeSelection();
1637
+ }
1638
+
1639
+ // On left click.
1640
+ if (e.which == 1 && !(e.which == 1 && editor.helpers.isMac() && e.ctrlKey)) {
1641
+ mouseDownFlag = false;
1642
+
1643
+ // Mouse down was in a table cell.
1644
+ if (mouseDownCellFlag) {
1645
+ // Left click is no longer pressed.
1646
+ mouseDownCellFlag = false;
1647
+
1648
+ var cell = _getCellUnder(e);
1649
+
1650
+ // If we have one selected cell and mouse is lifted somewhere else.
1651
+ if (!cell && selectedCells().length == 1) {
1652
+ // We have a text selection and not cell selection.
1653
+ _removeSelection();
1654
+ }
1655
+
1656
+ // If there are selected cells then show table edit popup.
1657
+ else if (selectedCells().length > 0) {
1658
+ if (editor.selection.isCollapsed()) {
1659
+ _showEditPopup();
1660
+ }
1661
+
1662
+ // No text selection.
1663
+ else {
1664
+ _removeSelection();
1665
+ }
1666
+ }
1667
+ }
1668
+
1669
+ // Resizing stops.
1670
+ if (resizingFlag) {
1671
+ resizingFlag = false;
1672
+
1673
+ $resizer.removeClass('fr-moving');
1674
+
1675
+ // Allow text selection.
1676
+ editor.$el.removeClass('fr-no-selection');
1677
+ editor.edit.on();
1678
+
1679
+ // Set release Y coordinate.
1680
+ var left = parseFloat($resizer.css('left')) + editor.opts.tableResizerOffset;
1681
+ if (editor.opts.iframe) {
1682
+ left -= editor.$iframe.offset().left;
1683
+ }
1684
+ $resizer.data('release-position', left);
1685
+
1686
+ // Clear resizing limits.
1687
+ $resizer.removeData('max-left');
1688
+ $resizer.removeData('max-right');
1689
+
1690
+ // Resize.
1691
+ _resize(e);
1692
+
1693
+ // Hide resizer.
1694
+ _hideResizer();
1695
+ }
1696
+ }
1697
+ }
1698
+
1699
+ /*
1700
+ * User drags mouse over multiple cells to select them.
1701
+ */
1702
+ function _mouseEnter (e) {
1703
+ if (mouseDownCellFlag === true) {
1704
+ var $cell = $(e.currentTarget);
1705
+
1706
+ // Cells should be in the same table.
1707
+ if ($cell.closest('table').is(selectedTable())) {
1708
+ // Don't select both ths and tds.
1709
+ if (e.currentTarget.tagName == 'TD' && editor.$el.find('th.fr-selected-cell').length === 0) {
1710
+ // Select cells between.
1711
+ _selectCells(mouseDownCell, e.currentTarget);
1712
+ return;
1713
+ }
1714
+
1715
+ else if (e.currentTarget.tagName == 'TH' && editor.$el.find('td.fr-selected-cell').length === 0) {
1716
+ // Select cells between.
1717
+ _selectCells(mouseDownCell, e.currentTarget);
1718
+ return;
1719
+ }
1720
+ }
1721
+
1722
+ // Prevent firefox selection.
1723
+ _clearSelection();
1724
+ }
1725
+ }
1726
+
1727
+ /*
1728
+ * Using the arrow keys to move the cursor through the table will not select cells.
1729
+ */
1730
+ function _usingArrows (e) {
1731
+ if (e.which == 37 || e.which == 38 || e.which == 39 || e.which == 40) {
1732
+ if (selectedCells().length > 0) {
1733
+ _stopEdit();
1734
+ }
1735
+ }
1736
+ }
1737
+
1738
+ /*
1739
+ * Initilize table resizer.
1740
+ */
1741
+ function _initResizer () {
1742
+ // Append resizer HTML to editor wrapper.
1743
+ if (!editor.shared.$table_resizer) editor.shared.$table_resizer = $('<div class="fr-table-resizer"><div></div></div>');
1744
+ $resizer = editor.shared.$table_resizer;
1745
+
1746
+ // Resize table. Mousedown.
1747
+ editor.events.$on($resizer, 'mousedown', function (e) {
1748
+ if (!editor.core.sameInstance($resizer)) return true;
1749
+
1750
+ // Stop table editing.
1751
+ if (selectedCells().length > 0) {
1752
+ _stopEdit();
1753
+ }
1754
+
1755
+ // Resize table only using left click.
1756
+ if (e.which == 1) {
1757
+ resizingFlag = true;
1758
+
1759
+ $resizer.addClass('fr-moving');
1760
+
1761
+ // Prevent text selection while dragging the table resizer.
1762
+ _clearSelection();
1763
+
1764
+ // Turn editor toolbar off while resizing.
1765
+ editor.edit.off();
1766
+
1767
+ // Show resizer.
1768
+ $resizer.find('div').css('opacity', 1);
1769
+
1770
+ // Prevent selecting text when doing resize.
1771
+ return false;
1772
+ }
1773
+ });
1774
+
1775
+ // Mousemove on table resizer.
1776
+ editor.events.$on($resizer, 'mousemove', function (e) {
1777
+ if (!editor.core.sameInstance($resizer)) return true;
1778
+
1779
+ if (resizingFlag) {
1780
+ if (editor.opts.iframe) {
1781
+ e.pageX -= editor.$iframe.offset().left;
1782
+ }
1783
+
1784
+ _mouseMove(e);
1785
+ }
1786
+ })
1787
+
1788
+ // Editor destroy.
1789
+ editor.events.on('shared.destroy', function () {
1790
+ $resizer.html('').removeData().remove();
1791
+ $resizer = null;
1792
+ }, true);
1793
+
1794
+ editor.events.on('destroy', function () {
1795
+ editor.$el.find('.fr-selected-cell').removeClass('fr-selected-cell');
1796
+ $resizer.hide().appendTo($('body'));
1797
+ }, true);
1798
+ }
1799
+
1800
+ /*
1801
+ * Also clears top and left values, so it doesn't interfer with the insert helper.
1802
+ */
1803
+ function _hideResizer () {
1804
+ if ($resizer) {
1805
+ $resizer.find('div').css('opacity', 0);
1806
+ $resizer.css('top', 0);
1807
+ $resizer.css('left', 0);
1808
+ $resizer.css('height', 0);
1809
+ $resizer.find('div').css('height', 0);
1810
+ $resizer.hide();
1811
+ }
1812
+ }
1813
+
1814
+ /**
1815
+ * Hide the insert helper.
1816
+ */
1817
+ function _hideInsertHelper () {
1818
+ if ($insert_helper) $insert_helper.removeClass('fr-visible').css('left', '-9999px');
1819
+ }
1820
+
1821
+ /*
1822
+ * Place the table resizer between the columns where the mouse is.
1823
+ */
1824
+ function _placeResizer (e, tag_under) {
1825
+ var $tag_under = $(tag_under);
1826
+ var $table = $tag_under.closest('table');
1827
+
1828
+ // We might have another tag inside the table cell.
1829
+ if (tag_under && (tag_under.tagName != 'TD' && tag_under.tagName != 'TH')) {
1830
+ if ($tag_under.closest('td').length > 0) {
1831
+ tag_under = $tag_under.closest('td');
1832
+ } else if ($tag_under.closest('th').length > 0) {
1833
+ tag_under = $tag_under.closest('th');
1834
+ }
1835
+ }
1836
+
1837
+ // The tag should be a table cell (TD or TH).
1838
+ if (tag_under && (tag_under.tagName == 'TD' || tag_under.tagName == 'TH')) {
1839
+ $tag_under = $(tag_under);
1840
+
1841
+ // https://github.com/froala/wysiwyg-editor/issues/786.
1842
+ if (editor.$el.find($tag_under).length === 0) return false;
1843
+
1844
+ // Tag's left and right coordinate.
1845
+ var tag_left = $tag_under.offset().left - 1;
1846
+ var tag_right = tag_left + $tag_under.outerWidth();
1847
+
1848
+ // Only if the mouse is close enough to the left or right edges.
1849
+ if (Math.abs(e.pageX - tag_left) <= editor.opts.tableResizerOffset ||
1850
+ Math.abs(tag_right - e.pageX) <= editor.opts.tableResizerOffset) {
1851
+
1852
+ // Create a table map.
1853
+ var map = _tableMap($table);
1854
+ var tag_origin = _cellOrigin(tag_under, map);
1855
+
1856
+ var tag_end = _cellEnds(tag_origin.row, tag_origin.col, map);
1857
+
1858
+ // The column numbers from the map that have to be resized.
1859
+ var first;
1860
+ var second;
1861
+
1862
+ // Table resizer position and height.
1863
+ var resizer_top = $table.offset().top;
1864
+ var resizer_height = $table.outerHeight() - 1;
1865
+ var resizer_left;
1866
+
1867
+ // The left and right limits between which the resizer can be moved.
1868
+ var max_left;
1869
+ var max_right;
1870
+
1871
+ // Mouse is near the cells's left margin (skip left table border).
1872
+ if (tag_origin.col > 0 && e.pageX - tag_left <= editor.opts.tableResizerOffset) {
1873
+ // Table resizer's left position.
1874
+ resizer_left = tag_left;
1875
+
1876
+ // Previous table cell. (There's always a prev cell since we're skipping the left table border)
1877
+ var $prev_tag = $(map[tag_origin.row][tag_origin.col - 1]);
1878
+
1879
+ // Left limit.
1880
+ if ((parseInt($prev_tag.attr('colspan'), 10) || 1) == 1) {
1881
+ max_left = $prev_tag.offset().left - 1 + editor.opts.tableResizingLimit;
1882
+ } else {
1883
+ max_left = tag_left - _columnWidth(tag_origin.col - 1, map) + editor.opts.tableResizingLimit;
1884
+ }
1885
+
1886
+ // Right limit.
1887
+ if ((parseInt($tag_under.attr('colspan'), 10) || 1) == 1) {
1888
+ max_right = tag_left + $tag_under.outerWidth() - editor.opts.tableResizingLimit;
1889
+ } else {
1890
+ max_right = tag_left + _columnWidth(tag_origin.col, map) - editor.opts.tableResizingLimit;
1891
+ }
1892
+
1893
+ // Columns to resize.
1894
+ first = tag_origin.col - 1;
1895
+ second = tag_origin.col;
1896
+ }
1897
+
1898
+ // Mouse is near the cell's right margin.
1899
+ else if (tag_right - e.pageX <= editor.opts.tableResizerOffset) {
1900
+ // Table resizer's left possition.
1901
+ resizer_left = tag_right;
1902
+
1903
+ // Check for next td.
1904
+ if (tag_end.col < map[tag_end.row].length && map[tag_end.row][tag_end.col + 1]) {
1905
+ // Next table cell.
1906
+ var $next_tag = $(map[tag_end.row][tag_end.col + 1]);
1907
+
1908
+ // Left limit.
1909
+ if ((parseInt($tag_under.attr('colspan'), 10) || 1) == 1) {
1910
+ max_left = tag_left + editor.opts.tableResizingLimit;
1911
+ } else {
1912
+ max_left = tag_right - _columnWidth(tag_end.col, map) + editor.opts.tableResizingLimit;
1913
+ }
1914
+
1915
+ // Right limit.
1916
+ if ((parseInt($next_tag.attr('colspan'), 10) || 1) == 1) {
1917
+ max_right = tag_right + $next_tag.outerWidth() - editor.opts.tableResizingLimit;
1918
+ } else {
1919
+ max_right = tag_right + _columnWidth(tag_origin.col + 1, map) - editor.opts.tableResizingLimit;
1920
+ }
1921
+
1922
+ // Columns to resize.
1923
+ first = tag_end.col;
1924
+ second = tag_end.col + 1;
1925
+ }
1926
+
1927
+ // Resize table.
1928
+ else {
1929
+ // Columns to resize.
1930
+ first = tag_end.col;
1931
+ second = null;
1932
+
1933
+ // Resizer limits.
1934
+ var $table_parent = $table.parent();
1935
+ max_left = $table.offset().left - 1 + map[0].length * editor.opts.tableResizingLimit;
1936
+ max_right = $table_parent.offset().left - 1 + $table_parent.width() + parseFloat($table_parent.css('padding-left'));
1937
+ }
1938
+ }
1939
+
1940
+ if (!$resizer) _initResizer();
1941
+
1942
+ // Save table.
1943
+ $resizer.data('table', $table);
1944
+
1945
+ // Save columns to resize.
1946
+ $resizer.data('first', first);
1947
+ $resizer.data('second', second);
1948
+
1949
+ $resizer.data('instance', editor);
1950
+ editor.$wp.append($resizer);
1951
+
1952
+ var left = resizer_left - editor.win.pageXOffset - editor.opts.tableResizerOffset;
1953
+ var top = resizer_top - editor.win.pageYOffset;
1954
+
1955
+ if (editor.opts.iframe) {
1956
+ left += editor.$iframe.offset().left - $(editor.o_win).scrollLeft();
1957
+ top += editor.$iframe.offset().top - $(editor.o_win).scrollTop();
1958
+
1959
+ max_left += editor.$iframe.offset().left;
1960
+ max_right += editor.$iframe.offset().left;
1961
+ }
1962
+
1963
+ // Set resizing limits.
1964
+ $resizer.data('max-left', max_left);
1965
+ $resizer.data('max-right', max_right);
1966
+
1967
+ // Initial position of the resizer
1968
+ $resizer.data('origin', resizer_left - editor.win.pageXOffset);
1969
+
1970
+ // Set table resizer's top, left and height.
1971
+ $resizer.css('top', top);
1972
+ $resizer.css('left', left);
1973
+ $resizer.css('height', resizer_height);
1974
+ $resizer.find('div').css('height', resizer_height);
1975
+
1976
+ // Set padding according to tableResizerOffset.
1977
+ $resizer.css('padding-left', editor.opts.tableResizerOffset);
1978
+ $resizer.css('padding-right', editor.opts.tableResizerOffset);
1979
+
1980
+ // Show table resizer.
1981
+ $resizer.show();
1982
+ }
1983
+
1984
+ // Hide resizer when the mouse moves away from the cell's border.
1985
+ else {
1986
+ if (editor.core.sameInstance($resizer)) _hideResizer();
1987
+ }
1988
+ }
1989
+
1990
+ // Hide resizer if mouse is no longer over it.
1991
+ else if ($resizer && $tag_under.get(0) != $resizer.get(0) && $tag_under.parent().get(0) != $resizer.get(0)) {
1992
+ if (editor.core.sameInstance($resizer)) _hideResizer();
1993
+ }
1994
+ }
1995
+
1996
+ /*
1997
+ * Show the insert column helper button.
1998
+ */
1999
+ function _showInsertColHelper (e, table) {
2000
+ if (editor.$box.find('.fr-line-breaker').is(':visible')) return false;
2001
+
2002
+ // Insert Helper.
2003
+ if (!$insert_helper) _initInsertHelper();
2004
+
2005
+ editor.$box.append($insert_helper);
2006
+ $insert_helper.data('instance', editor);
2007
+
2008
+ var $table = $(table);
2009
+ var $row = $table.find('tr:first');
2010
+
2011
+ var mouseX = e.pageX;
2012
+
2013
+ var left = 0;
2014
+ var top = 0;
2015
+
2016
+ if (editor.opts.iframe) {
2017
+ left += editor.$iframe.offset().left - $(editor.o_win).scrollLeft();
2018
+ top += editor.$iframe.offset().top - $(editor.o_win).scrollTop();
2019
+ }
2020
+
2021
+ // Check where the column should be inserted.
2022
+ var btn_width;
2023
+ $row.find('th, td').each (function () {
2024
+ var $td = $(this);
2025
+
2026
+ // Insert before this td.
2027
+ if ($td.offset().left <= mouseX && mouseX < $td.offset().left + $td.outerWidth() / 2) {
2028
+ btn_width = parseInt($insert_helper.find('a').css('width'), 10);
2029
+
2030
+ $insert_helper.css('top', top + $td.offset().top - editor.win.pageYOffset - btn_width - 5);
2031
+ $insert_helper.css('left', left + $td.offset().left - editor.win.pageXOffset - btn_width / 2);
2032
+ $insert_helper.data('selected-cell', $td);
2033
+ $insert_helper.data('position', 'before');
2034
+ $insert_helper.addClass('fr-visible');
2035
+
2036
+ return false;
2037
+
2038
+ // Insert after this td.
2039
+ } else if ($td.offset().left + $td.outerWidth() / 2 <= mouseX && mouseX < $td.offset().left + $td.outerWidth()) {
2040
+ btn_width = parseInt($insert_helper.find('a').css('width'), 10);
2041
+
2042
+ $insert_helper.css('top', top + $td.offset().top - editor.win.pageYOffset - btn_width - 5);
2043
+ $insert_helper.css('left', left + $td.offset().left + $td.outerWidth() - editor.win.pageXOffset - btn_width / 2);
2044
+ $insert_helper.data('selected-cell', $td);
2045
+ $insert_helper.data('position', 'after');
2046
+ $insert_helper.addClass('fr-visible');
2047
+
2048
+ return false;
2049
+ }
2050
+ });
2051
+ }
2052
+
2053
+ /*
2054
+ * Show the insert row helper button.
2055
+ */
2056
+ function _showInsertRowHelper (e, table) {
2057
+ if (editor.$box.find('.fr-line-breaker').is(':visible')) return false;
2058
+
2059
+ if (!$insert_helper) _initInsertHelper();
2060
+
2061
+ editor.$box.append($insert_helper);
2062
+ $insert_helper.data('instance', editor);
2063
+
2064
+ var $table = $(table);
2065
+ var mouseY = e.pageY;
2066
+
2067
+ var left = 0;
2068
+ var top = 0;
2069
+ if (editor.opts.iframe) {
2070
+ left += editor.$iframe.offset().left - $(editor.o_win).scrollLeft();
2071
+ top += editor.$iframe.offset().top - $(editor.o_win).scrollTop();
2072
+ }
2073
+
2074
+ // Check where the row should be inserted.
2075
+ var btn_width;
2076
+ $table.find('tr').each (function () {
2077
+ var $tr = $(this);
2078
+
2079
+ // Insert above this tr.
2080
+ if ($tr.offset().top <= mouseY && mouseY < $tr.offset().top + $tr.outerHeight() / 2) {
2081
+ btn_width = parseInt($insert_helper.find('a').css('width'), 10);
2082
+
2083
+ $insert_helper.css('top', top + $tr.offset().top - editor.win.pageYOffset - btn_width / 2);
2084
+ $insert_helper.css('left', left + $tr.offset().left - editor.win.pageXOffset - btn_width - 5);
2085
+ $insert_helper.data('selected-cell', $tr.find('td:first'));
2086
+ $insert_helper.data('position', 'above');
2087
+ $insert_helper.addClass('fr-visible');
2088
+
2089
+ return false;
2090
+
2091
+ // Insert below this tr.
2092
+ } else if ($tr.offset().top + $tr.outerHeight() / 2 <= mouseY && mouseY < $tr.offset().top + $tr.outerHeight()) {
2093
+ btn_width = parseInt($insert_helper.find('a').css('width'), 10);
2094
+
2095
+ $insert_helper.css('top', top + $tr.offset().top + $tr.outerHeight() - editor.win.pageYOffset - btn_width / 2);
2096
+ $insert_helper.css('left', left + $tr.offset().left - editor.win.pageXOffset - btn_width - 5);
2097
+ $insert_helper.data('selected-cell', $tr.find('td:first'));
2098
+ $insert_helper.data('position', 'below');
2099
+ $insert_helper.addClass('fr-visible');
2100
+
2101
+ return false;
2102
+ }
2103
+ });
2104
+ }
2105
+
2106
+ /*
2107
+ * Check if should show the insert column / row helper button.
2108
+ */
2109
+ function _insertHelper (e, tag_under) {
2110
+ // Don't show the insert helper if there are table cells selected.
2111
+ if (selectedCells().length === 0) {
2112
+ var i;
2113
+ var tag_below;
2114
+ var tag_right;
2115
+
2116
+ // Tag is the editor element or body (inline toolbar). Look for closest tag bellow and at the right.
2117
+ if (tag_under && (tag_under.tagName == 'HTML' || tag_under.tagName == 'BODY' || editor.node.isElement(tag_under))) {
2118
+ // Look 1px down until a table tag is found or the insert helper offset is reached.
2119
+ for (i = 1; i <= editor.opts.tableInsertHelperOffset; i++) {
2120
+ // Look for tag below.
2121
+ tag_below = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset, e.pageY - editor.win.pageYOffset + i);
2122
+
2123
+ // We're on tooltip.
2124
+ if ($(tag_below).hasClass('fr-tooltip')) return true;
2125
+
2126
+ // We found a tag bellow.
2127
+ if (tag_below && ((tag_below.tagName == 'TH' || tag_below.tagName == 'TD' || tag_below.tagName == 'TABLE') && ($(tag_below).parents('.fr-wrapper').length || editor.opts.iframe))) {
2128
+ // Show the insert column helper button.
2129
+ _showInsertColHelper (e, tag_below.closest('table'));
2130
+ return true;
2131
+ }
2132
+
2133
+ // Look for tag at the right.
2134
+ tag_right = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset + i, e.pageY - editor.win.pageYOffset);
2135
+
2136
+ // We're on tooltip.
2137
+ if ($(tag_right).hasClass('fr-tooltip')) return true;
2138
+
2139
+ // We found a tag at the right.
2140
+ if (tag_right && ((tag_right.tagName == 'TH' || tag_right.tagName == 'TD' || tag_right.tagName == 'TABLE') && ($(tag_right).parents('.fr-wrapper').length || editor.opts.iframe))) {
2141
+ // Show the insert row helper button.
2142
+ _showInsertRowHelper (e, tag_right.closest('table'));
2143
+ return true;
2144
+ }
2145
+ }
2146
+ }
2147
+
2148
+ // Hide insert helper.
2149
+ if (editor.core.sameInstance($insert_helper)) {
2150
+ _hideInsertHelper();
2151
+ }
2152
+ }
2153
+ }
2154
+
2155
+ /*
2156
+ * Check tag under the mouse on mouse move.
2157
+ */
2158
+ function _tagUnder (e) {
2159
+ mouseMoveTimer = null;
2160
+
2161
+ // The tag under the mouse cursor.
2162
+ var tag_under = editor.doc.elementFromPoint(e.pageX - editor.win.pageXOffset, e.pageY - editor.win.pageYOffset);
2163
+
2164
+ // Place table resizer if necessary.
2165
+ if (editor.opts.tableResizer && (!editor.popups.areVisible() || (editor.popups.areVisible() && editor.popups.isVisible('table.edit')))) {
2166
+ _placeResizer(e, tag_under);
2167
+ }
2168
+
2169
+ // Show the insert column / row helper button.
2170
+ if (editor.opts.tableInsertHelper && !editor.popups.areVisible() && !(editor.$tb.hasClass('fr-inline') && editor.$tb.is(':visible'))) {
2171
+ _insertHelper(e, tag_under);
2172
+ }
2173
+ }
2174
+
2175
+ /*
2176
+ * Repositon the resizer if the user scrolls while resizing.
2177
+ */
2178
+ function _repositionResizer () {
2179
+ if (resizingFlag) {
2180
+ var $table = $resizer.data('table');
2181
+ var top = $table.offset().top - editor.win.pageYOffset;
2182
+
2183
+ if (editor.opts.iframe) {
2184
+ top += editor.$iframe.offset().top - $(editor.o_win).scrollTop();
2185
+ }
2186
+
2187
+ $resizer.css('top', top);
2188
+ }
2189
+ }
2190
+
2191
+ /*
2192
+ * Resize table method.
2193
+ */
2194
+ function _resize () {
2195
+ // Resizer initial position.
2196
+ var initial_positon = $resizer.data('origin');
2197
+
2198
+ // Resizer release position.
2199
+ var release_position = $resizer.data('release-position');
2200
+
2201
+ // Do resize only if the resizer's position has changed.
2202
+ if (initial_positon !== release_position) {
2203
+ // Columns that have to be resized.
2204
+ var first = $resizer.data('first');
2205
+ var second = $resizer.data('second');
2206
+
2207
+ var $table = $resizer.data('table');
2208
+ var table_width = $table.outerWidth();
2209
+
2210
+ // Resize columns and not the table.
2211
+ if (first !== null && second !== null) {
2212
+ // Create a table map.
2213
+ var map = _tableMap($table);
2214
+
2215
+ // Got through all cells on these columns and get their initial width.
2216
+ var first_widths = [];
2217
+ var first_percentages = [];
2218
+ var second_widths = [];
2219
+ var second_percentages = [];
2220
+ var i;
2221
+ var $first_cell;
2222
+ var $second_cell;
2223
+
2224
+ // We must do this before updating widths.
2225
+ for (i = 0; i < map.length; i++) {
2226
+ $first_cell = $(map[i][first]);
2227
+ $second_cell = $(map[i][second]);
2228
+
2229
+ // Widths in px.
2230
+ first_widths[i] = $first_cell.outerWidth();
2231
+ second_widths[i] = $second_cell.outerWidth();
2232
+
2233
+ // Widths in percentages.
2234
+ first_percentages[i] = first_widths[i] / table_width * 100;
2235
+ second_percentages[i] = second_widths[i] / table_width * 100;
2236
+ }
2237
+
2238
+ // Got through all cells on these columns and update their widths.
2239
+ for (i = 0; i < map.length; i++) {
2240
+ $first_cell = $(map[i][first]);
2241
+ $second_cell = $(map[i][second]);
2242
+
2243
+ // New percentage for the first cell.
2244
+ var first_cell_percentage = (first_percentages[i] * (first_widths[i] + release_position - initial_positon) / first_widths[i]).toFixed(4);
2245
+
2246
+ $first_cell.css('width', first_cell_percentage + '%');
2247
+ $second_cell.css('width', (first_percentages[i] + second_percentages[i] - first_cell_percentage).toFixed(4) + '%');
2248
+ }
2249
+ }
2250
+
2251
+ // Resize the table.
2252
+ else {
2253
+ var $table_parent = $table.parent();
2254
+ var table_percentage = table_width / $table_parent.width() * 100;
2255
+ var width;
2256
+
2257
+ if (first == null) {
2258
+ width = (table_width - release_position + initial_positon) / table_width * table_percentage;
2259
+ } else {
2260
+ width = (table_width + release_position - initial_positon) / table_width * table_percentage;
2261
+ }
2262
+
2263
+ $table.css('width', Math.round(width).toFixed(4) + '%');
2264
+ }
2265
+ }
2266
+
2267
+ // Clear resizer data.
2268
+ $resizer.removeData('origin');
2269
+ $resizer.removeData('release-position');
2270
+ $resizer.removeData('first');
2271
+ $resizer.removeData('second');
2272
+ $resizer.removeData('table');
2273
+
2274
+ editor.undo.saveStep();
2275
+ }
2276
+
2277
+ /*
2278
+ * Get the width of the column. (columns may have colspan)
2279
+ */
2280
+ function _columnWidth (col, map) {
2281
+ var i;
2282
+ var width = $(map[0][col]).outerWidth();
2283
+
2284
+ for (i = 1; i < map.length; i++) {
2285
+ width = Math.min(width, $(map[i][col]).outerWidth());
2286
+ }
2287
+
2288
+ return width;
2289
+ }
2290
+
2291
+ /*
2292
+ * Get the width of the columns between specified indexes.
2293
+ */
2294
+ function _columnsWidth(col1, col2, map) {
2295
+ var i;
2296
+ var width = 0;
2297
+
2298
+ // Sum all columns widths.
2299
+ for (i = col1; i <= col2; i++) {
2300
+ width += _columnWidth(i, map);
2301
+ }
2302
+
2303
+ return width;
2304
+ }
2305
+
2306
+ /*
2307
+ * Set mouse timer to improve performance.
2308
+ */
2309
+ function _mouseMove (e) {
2310
+ // Prevent selecting text when we have cells selected.
2311
+ if (selectedCells().length > 1 && mouseDownFlag) {
2312
+ _clearSelection();
2313
+ }
2314
+
2315
+ // Reset or set timer.
2316
+ if (mouseDownFlag === false && mouseDownCellFlag === false && resizingFlag === false) {
2317
+ if (mouseMoveTimer) {
2318
+ clearTimeout(mouseMoveTimer);
2319
+ }
2320
+
2321
+ // Only resize table if the editor is not disabled by user.
2322
+ if (!editor.edit.isDisabled() || editor.popups.isVisible('table.edit')) {
2323
+ // Check tag under in order to place the table resizer or insert helper button.
2324
+ mouseMoveTimer = setTimeout(_tagUnder, 30, e);
2325
+ }
2326
+
2327
+ // Move table resizer.
2328
+ } else if (resizingFlag) {
2329
+ // Cursor position.
2330
+ var pos = e.pageX - editor.win.pageXOffset;
2331
+
2332
+ if (editor.opts.iframe) {
2333
+ pos += editor.$iframe.offset().left;
2334
+ }
2335
+
2336
+ // Left and right limits.
2337
+ var left_limit = $resizer.data('max-left');
2338
+ var right_limit = $resizer.data('max-right');
2339
+
2340
+ // Cursor is between the left and right limits.
2341
+ if (pos >= left_limit && pos <= right_limit) {
2342
+ $resizer.css('left', pos - editor.opts.tableResizerOffset);
2343
+
2344
+ // Cursor has exceeded the left limit. Don't update if it already has the correct value.
2345
+ } else if (pos < left_limit && parseFloat($resizer.css('left'), 10) > left_limit - editor.opts.tableResizerOffset) {
2346
+ $resizer.css('left', left_limit - editor.opts.tableResizerOffset);
2347
+
2348
+ // Cursor has exceeded the right limit. Don't update if it already has the correct value.
2349
+ } else if (pos > right_limit && parseFloat($resizer.css('left'), 10) < right_limit - editor.opts.tableResizerOffset) {
2350
+ $resizer.css('left', right_limit - editor.opts.tableResizerOffset);
2351
+ }
2352
+ } else if (mouseDownFlag) {
2353
+ _hideInsertHelper();
2354
+ }
2355
+ }
2356
+
2357
+ /*
2358
+ * Place selection markers in a table cell.
2359
+ */
2360
+ function _addMarkersInCell ($cell) {
2361
+ if (editor.node.isEmpty($cell.get(0))) {
2362
+ $cell.prepend($.FE.MARKERS);
2363
+ }
2364
+ else {
2365
+ $cell.prepend($.FE.START_MARKER).append($.FE.END_MARKER);
2366
+ }
2367
+ }
2368
+
2369
+ /*
2370
+ * Use TAB key to navigate through cells.
2371
+ */
2372
+ function _useTab (e) {
2373
+ var key_code = e.which;
2374
+
2375
+ if (key_code == $.FE.KEYCODE.TAB && editor.opts.tabSpaces === 0) {
2376
+ // Get starting cell.
2377
+ var $cell;
2378
+
2379
+ if (selectedCells().length > 0) {
2380
+ $cell = editor.$el.find('.fr-selected-cell:last')
2381
+ }
2382
+ else {
2383
+ var cell = editor.selection.element();
2384
+
2385
+ if (cell.tagName == 'TD' || cell.tagName == 'TH') {
2386
+ $cell = $(cell);
2387
+ }
2388
+ else if ($(cell).closest('td').length > 0) {
2389
+ $cell = $(cell).closest('td');
2390
+ }
2391
+ else if ($(cell).closest('th').length > 0) {
2392
+ $cell = $(cell).closest('th');
2393
+ }
2394
+ }
2395
+
2396
+ if ($cell) {
2397
+ e.preventDefault();
2398
+
2399
+ _stopEdit();
2400
+
2401
+ // Go backwards.
2402
+ if (e.shiftKey) {
2403
+ // Go to previous cell.
2404
+ if ($cell.prev().length > 0) {
2405
+ _addMarkersInCell($cell.prev());
2406
+ }
2407
+
2408
+ // Go to prev row, last cell.
2409
+ else if ($cell.closest('tr').length > 0 && $cell.closest('tr').prev().length > 0) {
2410
+ _addMarkersInCell($cell.closest('tr').prev().find('td:last'));
2411
+ }
2412
+
2413
+ // Go in THEAD, last cell.
2414
+ else if ($cell.closest('tbody').length > 0 && $cell.closest('table').find('thead tr').length > 0) {
2415
+ _addMarkersInCell($cell.closest('table').find('thead tr th:last'));
2416
+ }
2417
+ }
2418
+
2419
+ // Go forward.
2420
+ else {
2421
+ // Go to next cell.
2422
+ if ($cell.next().length > 0) {
2423
+ _addMarkersInCell($cell.next());
2424
+ }
2425
+
2426
+ // Go to next row, first cell.
2427
+ else if ($cell.closest('tr').length > 0 && $cell.closest('tr').next().length > 0) {
2428
+ _addMarkersInCell($cell.closest('tr').next().find('td:first'));
2429
+ }
2430
+
2431
+ // Cursor is in THEAD. Go to next row in TBODY
2432
+ else if ($cell.closest('thead').length > 0 && $cell.closest('table').find('tbody tr').length > 0) {
2433
+ _addMarkersInCell($cell.closest('table').find('tbody tr td:first'));
2434
+ }
2435
+
2436
+ // Add new row.
2437
+ else {
2438
+ $cell.addClass('fr-selected-cell');
2439
+ insertRow('below');
2440
+ _removeSelection();
2441
+
2442
+ _addMarkersInCell($cell.closest('tr').next().find('td:first'));
2443
+ }
2444
+ }
2445
+
2446
+ // Update cursor position.
2447
+ editor.selection.restore();
2448
+ }
2449
+ }
2450
+ }
2451
+
2452
+ /*
2453
+ * Initilize insert helper.
2454
+ */
2455
+ function _initInsertHelper () {
2456
+ // Append insert helper HTML to editor wrapper.
2457
+ if (!editor.shared.$ti_helper) {
2458
+ editor.shared.$ti_helper = $('<div class="fr-insert-helper"><a class="fr-floating-btn" role="button" tabindex="-1" title="' + editor.language.translate('Insert') + '"><svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M22,16.75 L16.75,16.75 L16.75,22 L15.25,22.000 L15.25,16.75 L10,16.75 L10,15.25 L15.25,15.25 L15.25,10 L16.75,10 L16.75,15.25 L22,15.25 L22,16.75 Z"/></svg></a></div>');
2459
+
2460
+ // Click on insert helper.
2461
+ editor.events.bindClick(editor.shared.$ti_helper, 'a', function () {
2462
+ var $td = $insert_helper.data('selected-cell');
2463
+ var position = $insert_helper.data('position');
2464
+
2465
+ var inst = $insert_helper.data('instance') || editor;
2466
+
2467
+ if (position == 'before') {
2468
+ $td.addClass('fr-selected-cell');
2469
+ inst.table.insertColumn(position);
2470
+ $td.removeClass('fr-selected-cell');
2471
+
2472
+ } else if (position == 'after') {
2473
+ $td.addClass('fr-selected-cell');
2474
+ inst.table.insertColumn(position);
2475
+ $td.removeClass('fr-selected-cell');
2476
+
2477
+ } else if (position == 'above') {
2478
+ $td.addClass('fr-selected-cell');
2479
+ inst.table.insertRow(position);
2480
+ $td.removeClass('fr-selected-cell');
2481
+
2482
+ } else if (position == 'below') {
2483
+ $td.addClass('fr-selected-cell');
2484
+ inst.table.insertRow(position);
2485
+ $td.removeClass('fr-selected-cell');
2486
+ }
2487
+
2488
+ // Hide the insert helper so it will reposition.
2489
+ _hideInsertHelper();
2490
+ });
2491
+
2492
+ // Editor destroy.
2493
+ editor.events.on('shared.destroy', function () {
2494
+ editor.shared.$ti_helper.html('').removeData().remove();
2495
+ editor.shared.$ti_helper = null;
2496
+ }, true);
2497
+
2498
+ // Prevent the insert helper hide when mouse is over it.
2499
+ editor.events.$on(editor.shared.$ti_helper, 'mousemove', function (e) {
2500
+ e.stopPropagation();
2501
+ }, true);
2502
+
2503
+ // Hide the insert helper if the page is scrolled.
2504
+ editor.events.$on($(editor.o_win), 'scroll', function () {
2505
+ _hideInsertHelper();
2506
+ }, true);
2507
+
2508
+ editor.events.$on(editor.$wp, 'scroll', function () {
2509
+ _hideInsertHelper();
2510
+ }, true);
2511
+ }
2512
+
2513
+ $insert_helper = editor.shared.$ti_helper;
2514
+
2515
+ editor.events.on('destroy', function () {
2516
+ $insert_helper = null;
2517
+ });
2518
+
2519
+ // Table insert helper tooltip.
2520
+ editor.tooltip.bind(editor.$box, '.fr-insert-helper > a.fr-floating-btn');
2521
+ }
2522
+
2523
+ /**
2524
+ * Destroy
2525
+ */
2526
+ function _destroy () {
2527
+ mouseDownCell = null;
2528
+ clearTimeout(mouseMoveTimer);
2529
+ }
2530
+
2531
+ /*
2532
+ * Go back to the table edit popup.
2533
+ */
2534
+ function back () {
2535
+ if (selectedCells().length > 0) {
2536
+ _showEditPopup();
2537
+ }
2538
+ else {
2539
+ editor.popups.hide('table.insert');
2540
+ editor.toolbar.showInline();
2541
+ }
2542
+ }
2543
+
2544
+ /**
2545
+ * Return selected cells.
2546
+ */
2547
+ function selectedCells () {
2548
+ return editor.$el.get(0).querySelectorAll('.fr-selected-cell');
2549
+ }
2550
+
2551
+ /**
2552
+ * Return selected table.
2553
+ */
2554
+ function selectedTable () {
2555
+ var cells = selectedCells();
2556
+ if (cells.length) {
2557
+ var cell = cells[0];
2558
+ while (cell && cell.tagName != 'TABLE' && cell.parentNode != editor.$el.get(0)) {
2559
+ cell = cell.parentNode;
2560
+ }
2561
+
2562
+ if (cell && cell.tagName == 'TABLE') return $(cell);
2563
+ return $([]);
2564
+ }
2565
+
2566
+ return $([]);
2567
+ }
2568
+
2569
+ /*
2570
+ * Init table.
2571
+ */
2572
+ function _init () {
2573
+ if (!editor.$wp) return false;
2574
+
2575
+ // Do cell selection only on desktops (no touch devices)
2576
+ if (!editor.helpers.isMobile()) {
2577
+ // Remember if mouse is clicked.
2578
+ mouseDownFlag = false;
2579
+ mouseDownCellFlag = false;
2580
+ resizingFlag = false;
2581
+
2582
+ // Mouse is down in a table cell.
2583
+ editor.events.$on(editor.$el, 'mousedown', _mouseDown);
2584
+
2585
+ // Deselect table cells when user clicks on an image.
2586
+ editor.popups.onShow('image.edit', function () {
2587
+ _removeSelection();
2588
+ mouseDownFlag = false;
2589
+ mouseDownCellFlag = false;
2590
+ });
2591
+
2592
+ // Deselect table cells when user clicks on a link.
2593
+ editor.popups.onShow('link.edit', function () {
2594
+ _removeSelection();
2595
+ mouseDownFlag = false;
2596
+ mouseDownCellFlag = false;
2597
+ });
2598
+
2599
+ // Deselect table cells when a command is run.
2600
+ editor.events.on('commands.mousedown', function ($btn) {
2601
+ if ($btn.parents('.fr-toolbar').length > 0) {
2602
+ _removeSelection();
2603
+ }
2604
+ });
2605
+
2606
+ // Mouse enter's a table cell.
2607
+ editor.events.$on(editor.$el, 'mouseenter', 'th, td', _mouseEnter);
2608
+
2609
+ // Mouse is no longer pressed.
2610
+ editor.events.$on(editor.$win, 'mouseup', _mouseUp);
2611
+
2612
+ // Iframe mouseup.
2613
+ if (editor.opts.iframe) {
2614
+ editor.events.$on($(editor.o_win), 'mouseup', _mouseUp);
2615
+ }
2616
+
2617
+ // Moving cursor with arrow keys.
2618
+ editor.events.$on(editor.$el, 'keydown', _usingArrows);
2619
+
2620
+ // Check tags under the mouse to see if the resizer needs to be shown.
2621
+ editor.events.$on(editor.$win, 'mousemove', _mouseMove);
2622
+
2623
+ // Update resizer's position on scroll.
2624
+ editor.events.$on($(editor.o_win), 'scroll', _repositionResizer);
2625
+
2626
+ // Reposition table edit popup when table cell content changes.
2627
+ editor.events.on('contentChanged', function () {
2628
+ if (selectedCells().length > 0) {
2629
+ _showEditPopup();
2630
+
2631
+ // Make sure we reposition on image load.
2632
+ editor.$el.find('img').on('load.selected-cells', function () {
2633
+ $(this).off('load.selected-cells');
2634
+ if (selectedCells().length > 0) {
2635
+ _showEditPopup();
2636
+ }
2637
+ });
2638
+ }
2639
+ });
2640
+
2641
+ // Reposition table edit popup on window resize.
2642
+ editor.events.$on($(editor.o_win), 'resize', function () {
2643
+ _removeSelection();
2644
+ });
2645
+
2646
+ // Prevent backspace from doing browser back.
2647
+ editor.events.on('keydown', function (e) {
2648
+ var selected_cells = selectedCells();
2649
+
2650
+ if (selected_cells.length > 0) {
2651
+ // ESC clear table cell selection.
2652
+ if (e.which == $.FE.KEYCODE.ESC) {
2653
+ if (editor.popups.isVisible('table.edit')) {
2654
+ _removeSelection();
2655
+ editor.popups.hide('table.edit');
2656
+ e.preventDefault();
2657
+ e.stopPropagation();
2658
+ e.stopImmediatePropagation();
2659
+ selected_cells = [];
2660
+ return false;
2661
+ }
2662
+ }
2663
+
2664
+ // Backspace clears selected cells content.
2665
+ if (selected_cells.length > 1 && e.which == $.FE.KEYCODE.BACKSPACE) {
2666
+ editor.undo.saveStep();
2667
+
2668
+ for (var i = 0; i < selected_cells.length; i++) {
2669
+ $(selected_cells[i]).html('<br>');
2670
+
2671
+ if (i == selected_cells.length - 1) {
2672
+ $(selected_cells[i]).prepend($.FE.MARKERS);
2673
+ }
2674
+ }
2675
+
2676
+ editor.selection.restore();
2677
+ editor.undo.saveStep();
2678
+ selected_cells = [];
2679
+ return false;
2680
+ }
2681
+
2682
+ // Prevent typing if cells are selected. (Allow browser refresh using keyboard)
2683
+ if (selected_cells.length > 1 && !editor.keys.ctrlKey(e)) {
2684
+ e.preventDefault();
2685
+ selected_cells = [];
2686
+ return false;
2687
+ }
2688
+ }
2689
+
2690
+ selected_cells = [];
2691
+ }, true);
2692
+
2693
+ // Clean selected cells.
2694
+ var c_selected_cells = [];
2695
+ editor.events.on('html.beforeGet', function () {
2696
+ c_selected_cells = selectedCells();
2697
+ for (var i = 0; i < c_selected_cells.length; i++) {
2698
+ c_selected_cells[i].className = (c_selected_cells[i].className || '').replace(/fr-selected-cell/g, '');
2699
+ }
2700
+ });
2701
+
2702
+ editor.events.on('html.get', function (html) {
2703
+ html = html.replace(/<(td|th)((?:[\w\W]*?)) class=""((?:[\w\W]*?))>((?:[\w\W]*?))<\/(td|th)>/g, '<$1$2$3>$4</$5>');
2704
+
2705
+ return html;
2706
+ });
2707
+
2708
+ editor.events.on('html.afterGet', function () {
2709
+ for (var i = 0; i < c_selected_cells.length; i++) {
2710
+ c_selected_cells[i].className = (c_selected_cells[i].className ? c_selected_cells[i].className + ' ' : '') + 'fr-selected-cell';
2711
+ }
2712
+ c_selected_cells = [];
2713
+ });
2714
+
2715
+ _initInsertPopup(true);
2716
+ _initEditPopup(true);
2717
+ }
2718
+
2719
+ // Tab in cell
2720
+ editor.events.on('keydown', _useTab, true);
2721
+
2722
+ editor.events.on('destroy', _destroy);
2723
+ }
2724
+
2725
+ return {
2726
+ _init: _init,
2727
+ insert: insert,
2728
+ remove: remove,
2729
+ insertRow: insertRow,
2730
+ deleteRow: deleteRow,
2731
+ insertColumn: insertColumn,
2732
+ deleteColumn: deleteColumn,
2733
+ mergeCells: mergeCells,
2734
+ splitCellVertically: splitCellVertically,
2735
+ splitCellHorizontally: splitCellHorizontally,
2736
+ addHeader: addHeader,
2737
+ removeHeader: removeHeader,
2738
+ setBackground: setBackground,
2739
+ showInsertPopup: _showInsertPopup,
2740
+ showEditPopup: _showEditPopup,
2741
+ showColorsPopup: _showColorsPopup,
2742
+ back: back,
2743
+ verticalAlign: verticalAlign,
2744
+ horizontalAlign: horizontalAlign,
2745
+ applyStyle: applyStyle,
2746
+ selectedTable: selectedTable,
2747
+ selectedCells: selectedCells
2748
+ }
2749
+ };
2750
+
2751
+ // Insert table button.
2752
+ $.FE.DefineIcon('insertTable', { NAME: 'table' });
2753
+ $.FE.RegisterCommand('insertTable', {
2754
+ title: 'Insert Table',
2755
+ undo: false,
2756
+ focus: true,
2757
+ refreshOnCallback: false,
2758
+ popup: true,
2759
+ callback: function () {
2760
+ if (!this.popups.isVisible('table.insert')) {
2761
+ this.table.showInsertPopup();
2762
+ }
2763
+ else {
2764
+ if (this.$el.find('.fr-marker')) {
2765
+ this.events.disableBlur();
2766
+ this.selection.restore();
2767
+ }
2768
+ this.popups.hide('table.insert');
2769
+ }
2770
+ },
2771
+ plugin: 'table'
2772
+ });
2773
+
2774
+ $.FE.RegisterCommand('tableInsert', {
2775
+ callback: function (cmd, rows, cols) {
2776
+ this.table.insert(rows, cols);
2777
+ this.popups.hide('table.insert');
2778
+ }
2779
+ })
2780
+
2781
+ // Table header button.
2782
+ $.FE.DefineIcon('tableHeader', { NAME: 'header' })
2783
+ $.FE.RegisterCommand('tableHeader', {
2784
+ title: 'Table Header',
2785
+ focus: false,
2786
+ callback: function () {
2787
+ var $btn = this.popups.get('table.edit').find('.fr-command[data-cmd="tableHeader"]');
2788
+
2789
+ // If button is active the table has a header,
2790
+ if ($btn.hasClass('fr-active')) {
2791
+ this.table.removeHeader();
2792
+ }
2793
+
2794
+ // Add table header.
2795
+ else {
2796
+ this.table.addHeader();
2797
+ }
2798
+ },
2799
+ refresh: function ($btn) {
2800
+ var $table = this.table.selectedTable();
2801
+
2802
+ if ($table.length > 0) {
2803
+ // If table doesn't have a header.
2804
+ if ($table.find('th').length === 0) {
2805
+ $btn.removeClass('fr-active');
2806
+ }
2807
+
2808
+ // Header button is active if table has header.
2809
+ else {
2810
+ $btn.addClass('fr-active');
2811
+ }
2812
+ }
2813
+ }
2814
+ });
2815
+
2816
+ // Table rows action dropdown.
2817
+ $.FE.DefineIcon('tableRows', { NAME: 'bars' })
2818
+ $.FE.RegisterCommand('tableRows', {
2819
+ type: 'dropdown',
2820
+ focus: false,
2821
+ title: 'Row',
2822
+ options: {
2823
+ above: 'Insert row above',
2824
+ below: 'Insert row below',
2825
+ 'delete': 'Delete row'
2826
+ },
2827
+ html: function () {
2828
+ var c = '<ul class="fr-dropdown-list">';
2829
+ var options = $.FE.COMMANDS.tableRows.options;
2830
+ for (var val in options) {
2831
+ if (options.hasOwnProperty(val)) {
2832
+ c += '<li><a class="fr-command" data-cmd="tableRows" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
2833
+ }
2834
+ }
2835
+ c += '</ul>';
2836
+
2837
+ return c;
2838
+ },
2839
+ callback: function (cmd, val) {
2840
+ if (val == 'above' || val == 'below') {
2841
+ this.table.insertRow(val);
2842
+ } else {
2843
+ this.table.deleteRow();
2844
+ }
2845
+ }
2846
+ });
2847
+
2848
+ // Table columns action dropdown.
2849
+ $.FE.DefineIcon('tableColumns', { NAME: 'bars fa-rotate-90' })
2850
+ $.FE.RegisterCommand('tableColumns', {
2851
+ type: 'dropdown',
2852
+ focus: false,
2853
+ title: 'Column',
2854
+ options: {
2855
+ before: 'Insert column before',
2856
+ after: 'Insert column after',
2857
+ 'delete': 'Delete column'
2858
+ },
2859
+ html: function () {
2860
+ var c = '<ul class="fr-dropdown-list">';
2861
+ var options = $.FE.COMMANDS.tableColumns.options;
2862
+ for (var val in options) {
2863
+ if (options.hasOwnProperty(val)) {
2864
+ c += '<li><a class="fr-command" data-cmd="tableColumns" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
2865
+ }
2866
+ }
2867
+ c += '</ul>';
2868
+
2869
+ return c;
2870
+ },
2871
+ callback: function (cmd, val) {
2872
+ if (val == 'before' || val == 'after') {
2873
+ this.table.insertColumn(val);
2874
+ } else {
2875
+ this.table.deleteColumn();
2876
+ }
2877
+ }
2878
+ });
2879
+
2880
+ // Table cells action dropdown.
2881
+ $.FE.DefineIcon('tableCells', { NAME: 'square-o' })
2882
+ $.FE.RegisterCommand('tableCells', {
2883
+ type: 'dropdown',
2884
+ focus: false,
2885
+ title: 'Cell',
2886
+ options: {
2887
+ merge: 'Merge cells',
2888
+ 'vertical-split': 'Vertical split',
2889
+ 'horizontal-split': 'Horizontal split'
2890
+ },
2891
+ html: function () {
2892
+ var c = '<ul class="fr-dropdown-list">';
2893
+ var options = $.FE.COMMANDS.tableCells.options;
2894
+ for (var val in options) {
2895
+ if (options.hasOwnProperty(val)) {
2896
+ c += '<li><a class="fr-command" data-cmd="tableCells" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
2897
+ }
2898
+ }
2899
+ c += '</ul>';
2900
+
2901
+ return c;
2902
+ },
2903
+ callback: function (cmd, val) {
2904
+ if (val == 'merge') {
2905
+ this.table.mergeCells();
2906
+ }
2907
+ else if (val == 'vertical-split') {
2908
+ this.table.splitCellVertically();
2909
+ }
2910
+ // 'horizontal-split'
2911
+ else {
2912
+ this.table.splitCellHorizontally();
2913
+ }
2914
+ },
2915
+ refreshOnShow: function ($btn, $dropdown) {
2916
+ // More than one cell selected.
2917
+ if (this.$el.find('.fr-selected-cell').length > 1) {
2918
+ $dropdown.find('a[data-param1="vertical-split"]').addClass('fr-disabled');
2919
+ $dropdown.find('a[data-param1="horizontal-split"]').addClass('fr-disabled');
2920
+ $dropdown.find('a[data-param1="merge"]').removeClass('fr-disabled');
2921
+ }
2922
+
2923
+ // Only one selected cell.
2924
+ else {
2925
+ $dropdown.find('a[data-param1="merge"]').addClass('fr-disabled');
2926
+ $dropdown.find('a[data-param1="vertical-split"]').removeClass('fr-disabled');
2927
+ $dropdown.find('a[data-param1="horizontal-split"]').removeClass('fr-disabled');
2928
+ }
2929
+ }
2930
+ });
2931
+
2932
+ // Remove table button.
2933
+ $.FE.DefineIcon('tableRemove', { NAME: 'trash' })
2934
+ $.FE.RegisterCommand('tableRemove', {
2935
+ title: 'Remove Table',
2936
+ focus: false,
2937
+ callback: function () {
2938
+ this.table.remove();
2939
+ }
2940
+ });
2941
+
2942
+ // Table styles.
2943
+ $.FE.DefineIcon('tableStyle', { NAME: 'paint-brush' })
2944
+ $.FE.RegisterCommand('tableStyle', {
2945
+ title: 'Table Style',
2946
+ type: 'dropdown',
2947
+ focus: false,
2948
+ html: function () {
2949
+ var c = '<ul class="fr-dropdown-list">';
2950
+ var options = this.opts.tableStyles;
2951
+ for (var val in options) {
2952
+ if (options.hasOwnProperty(val)) {
2953
+ c += '<li><a class="fr-command" data-cmd="tableStyle" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
2954
+ }
2955
+ }
2956
+ c += '</ul>';
2957
+
2958
+ return c;
2959
+ },
2960
+ callback: function (cmd, val) {
2961
+ this.table.applyStyle(val, this.$el.find('.fr-selected-cell').closest('table'), this.opts.tableMultipleStyles, this.opts.tableStyles);
2962
+ },
2963
+ refreshOnShow: function ($btn, $dropdown) {
2964
+ var $table = this.$el.find('.fr-selected-cell').closest('table');
2965
+
2966
+ if ($table) {
2967
+ $dropdown.find('.fr-command').each (function () {
2968
+ var cls = $(this).data('param1');
2969
+ $(this).toggleClass('fr-active', $table.hasClass(cls));
2970
+ })
2971
+ }
2972
+ }
2973
+ });
2974
+
2975
+ // Table cell background color button.
2976
+ $.FE.DefineIcon('tableCellBackground', { NAME: 'tint' })
2977
+ $.FE.RegisterCommand('tableCellBackground', {
2978
+ title: 'Cell Background',
2979
+ focus: false,
2980
+ callback: function () {
2981
+ this.table.showColorsPopup();
2982
+ }
2983
+ });
2984
+
2985
+ // Select table cell background color command.
2986
+ $.FE.RegisterCommand('tableCellBackgroundColor', {
2987
+ undo: true,
2988
+ focus: false,
2989
+ callback: function (cmd, val) {
2990
+ this.table.setBackground(val);
2991
+ }
2992
+ });
2993
+
2994
+ // Table back.
2995
+ $.FE.DefineIcon('tableBack', { NAME: 'arrow-left' });
2996
+ $.FE.RegisterCommand('tableBack', {
2997
+ title: 'Back',
2998
+ undo: false,
2999
+ focus: false,
3000
+ back: true,
3001
+ callback: function () {
3002
+ this.table.back();
3003
+ },
3004
+ refresh: function ($btn) {
3005
+ if (this.table.selectedCells().length === 0 && !this.opts.toolbarInline) {
3006
+ $btn.addClass('fr-hidden');
3007
+ $btn.next('.fr-separator').addClass('fr-hidden');
3008
+ }
3009
+ else {
3010
+ $btn.removeClass('fr-hidden');
3011
+ $btn.next('.fr-separator').removeClass('fr-hidden');
3012
+ }
3013
+ }
3014
+ });
3015
+
3016
+ // Table vertical align dropdown.
3017
+ $.FE.DefineIcon('tableCellVerticalAlign', { NAME: 'arrows-v' })
3018
+ $.FE.RegisterCommand('tableCellVerticalAlign', {
3019
+ type: 'dropdown',
3020
+ focus: false,
3021
+ title: 'Vertical Align',
3022
+ options: {
3023
+ Top: 'Align Top',
3024
+ Middle: 'Align Middle',
3025
+ Bottom: 'Align Bottom'
3026
+ },
3027
+ html: function () {
3028
+ var c = '<ul class="fr-dropdown-list">';
3029
+ var options = $.FE.COMMANDS.tableCellVerticalAlign.options;
3030
+ for (var val in options) {
3031
+ if (options.hasOwnProperty(val)) {
3032
+ c += '<li><a class="fr-command" data-cmd="tableCellVerticalAlign" data-param1="' + val.toLowerCase() + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(val) + '</a></li>';
3033
+ }
3034
+ }
3035
+ c += '</ul>';
3036
+
3037
+ return c;
3038
+ },
3039
+ callback: function (cmd, val) {
3040
+ this.table.verticalAlign(val);
3041
+ },
3042
+ refreshOnShow: function ($btn, $dropdown) {
3043
+ $dropdown.find('.fr-command[data-param1="' + this.$el.find('.fr-selected-cell').css('vertical-align') + '"]').addClass('fr-active');
3044
+ }
3045
+ });
3046
+
3047
+ // Table horizontal align dropdown.
3048
+ $.FE.DefineIcon('tableCellHorizontalAlign', { NAME: 'align-left' });
3049
+ $.FE.DefineIcon('align-left', { NAME: 'align-left' });
3050
+ $.FE.DefineIcon('align-right', { NAME: 'align-right' });
3051
+ $.FE.DefineIcon('align-center', { NAME: 'align-center' });
3052
+ $.FE.DefineIcon('align-justify', { NAME: 'align-justify' });
3053
+ $.FE.RegisterCommand('tableCellHorizontalAlign', {
3054
+ type: 'dropdown',
3055
+ focus: false,
3056
+ title: 'Horizontal Align',
3057
+ options: {
3058
+ left: 'Align Left',
3059
+ center: 'Align Center',
3060
+ right: 'Align Right',
3061
+ justify: 'Align Justify'
3062
+ },
3063
+ html: function () {
3064
+ var c = '<ul class="fr-dropdown-list">';
3065
+ var options = $.FE.COMMANDS.tableCellHorizontalAlign.options;
3066
+ for (var val in options) {
3067
+ if (options.hasOwnProperty(val)) {
3068
+ c += '<li><a class="fr-command fr-title" data-cmd="tableCellHorizontalAlign" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.icon.create('align-' + val) + '</a></li>';
3069
+ }
3070
+ }
3071
+ c += '</ul>';
3072
+
3073
+ return c;
3074
+ },
3075
+ callback: function (cmd, val) {
3076
+ this.table.horizontalAlign(val);
3077
+ },
3078
+ refresh: function ($btn) {
3079
+ var selected_cells = this.table.selectedCells();
3080
+
3081
+ if (selected_cells.length) {
3082
+ $btn.find('> *:first').replaceWith(this.icon.create('align-' + this.helpers.getAlignment($(selected_cells[0]))));
3083
+ }
3084
+ },
3085
+ refreshOnShow: function ($btn, $dropdown) {
3086
+ $dropdown.find('.fr-command[data-param1="' + this.helpers.getAlignment(this.$el.find('.fr-selected-cell:first')) + '"]').addClass('fr-active');
3087
+ }
3088
+ });
3089
+
3090
+ // Table cell styles.
3091
+ $.FE.DefineIcon('tableCellStyle', { NAME: 'magic' })
3092
+ $.FE.RegisterCommand('tableCellStyle', {
3093
+ title: 'Cell Style',
3094
+ type: 'dropdown',
3095
+ focus: false,
3096
+ html: function () {
3097
+ var c = '<ul class="fr-dropdown-list">';
3098
+ var options = this.opts.tableCellStyles;
3099
+ for (var val in options) {
3100
+ if (options.hasOwnProperty(val)) {
3101
+ c += '<li><a class="fr-command" data-cmd="tableCellStyle" data-param1="' + val + '" title="' + this.language.translate(options[val]) + '">' + this.language.translate(options[val]) + '</a></li>';
3102
+ }
3103
+ }
3104
+ c += '</ul>';
3105
+
3106
+ return c;
3107
+ },
3108
+ callback: function (cmd, val) {
3109
+ this.table.applyStyle(val, this.$el.find('.fr-selected-cell'), this.opts.tableCellMultipleStyles, this.opts.tableCellStyles);
3110
+ },
3111
+ refreshOnShow: function ($btn, $dropdown) {
3112
+ var $cell = this.$el.find('.fr-selected-cell:first');
3113
+
3114
+ if ($cell) {
3115
+ $dropdown.find('.fr-command').each (function () {
3116
+ var cls = $(this).data('param1');
3117
+ $(this).toggleClass('fr-active', $cell.hasClass(cls));
3118
+ })
3119
+ }
3120
+ }
3121
+ });
3122
+
3123
+ }));