activeadmin_trumbowyg 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -5
  3. data/Rakefile +11 -0
  4. data/app/assets/fonts/trumbowyg/icons.svg +1 -1
  5. data/app/assets/javascripts/activeadmin/trumbowyg/langs/az.js +63 -0
  6. data/app/assets/javascripts/activeadmin/trumbowyg/langs/az.min.js +9 -0
  7. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ca.js +30 -13
  8. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ca.min.js +8 -5
  9. data/app/assets/javascripts/activeadmin/trumbowyg/langs/cs.js +11 -2
  10. data/app/assets/javascripts/activeadmin/trumbowyg/langs/cs.min.js +1 -1
  11. data/app/assets/javascripts/activeadmin/trumbowyg/langs/da.js +1 -1
  12. data/app/assets/javascripts/activeadmin/trumbowyg/langs/de.js +11 -2
  13. data/app/assets/javascripts/activeadmin/trumbowyg/langs/de.min.js +3 -3
  14. data/app/assets/javascripts/activeadmin/trumbowyg/langs/es_ar.js +1 -1
  15. data/app/assets/javascripts/activeadmin/trumbowyg/langs/et.js +7 -7
  16. data/app/assets/javascripts/activeadmin/trumbowyg/langs/fa.js +1 -1
  17. data/app/assets/javascripts/activeadmin/trumbowyg/langs/fi.js +1 -1
  18. data/app/assets/javascripts/activeadmin/trumbowyg/langs/fr.js +3 -0
  19. data/app/assets/javascripts/activeadmin/trumbowyg/langs/fr.min.js +1 -1
  20. data/app/assets/javascripts/activeadmin/trumbowyg/langs/he.js +1 -1
  21. data/app/assets/javascripts/activeadmin/trumbowyg/langs/hu.js +3 -0
  22. data/app/assets/javascripts/activeadmin/trumbowyg/langs/hu.min.js +1 -1
  23. data/app/assets/javascripts/activeadmin/trumbowyg/langs/id.js +1 -1
  24. data/app/assets/javascripts/activeadmin/trumbowyg/langs/it.js +4 -1
  25. data/app/assets/javascripts/activeadmin/trumbowyg/langs/it.min.js +1 -1
  26. data/app/assets/javascripts/activeadmin/trumbowyg/langs/lt.js +1 -1
  27. data/app/assets/javascripts/activeadmin/trumbowyg/langs/mn.js +39 -39
  28. data/app/assets/javascripts/activeadmin/trumbowyg/langs/{no_nb.js → nb.js} +22 -8
  29. data/app/assets/javascripts/activeadmin/trumbowyg/langs/nb.min.js +15 -0
  30. data/app/assets/javascripts/activeadmin/trumbowyg/langs/pl.js +1 -1
  31. data/app/assets/javascripts/activeadmin/trumbowyg/langs/pt.js +2 -2
  32. data/app/assets/javascripts/activeadmin/trumbowyg/langs/pt_br.js +2 -2
  33. data/app/assets/javascripts/activeadmin/trumbowyg/langs/rs.js +1 -1
  34. data/app/assets/javascripts/activeadmin/trumbowyg/langs/rs_latin.js +1 -1
  35. data/app/assets/javascripts/activeadmin/trumbowyg/langs/sl.js +10 -6
  36. data/app/assets/javascripts/activeadmin/trumbowyg/langs/sl.min.js +2 -1
  37. data/app/assets/javascripts/activeadmin/trumbowyg/langs/sq.js +2 -2
  38. data/app/assets/javascripts/activeadmin/trumbowyg/langs/sv.js +1 -0
  39. data/app/assets/javascripts/activeadmin/trumbowyg/langs/sv.min.js +1 -1
  40. data/app/assets/javascripts/activeadmin/trumbowyg/langs/th.js +33 -33
  41. data/app/assets/javascripts/activeadmin/trumbowyg/langs/tr.js +5 -3
  42. data/app/assets/javascripts/activeadmin/trumbowyg/langs/tr.min.js +1 -1
  43. data/app/assets/javascripts/activeadmin/trumbowyg/langs/zh_cn.js +1 -1
  44. data/app/assets/javascripts/activeadmin/trumbowyg/langs/zh_tw.js +1 -1
  45. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/allowtagsfrompaste/trumbowyg.allowtagsfrompaste.min.js +11 -0
  46. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/base64/trumbowyg.base64.js +28 -1
  47. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/base64/trumbowyg.base64.min.js +8 -1
  48. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/cleanpaste/trumbowyg.cleanpaste.min.js +11 -0
  49. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/trumbowyg.colors.js +39 -2
  50. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/trumbowyg.colors.min.js +10 -1
  51. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/ui/sass/trumbowyg.colors.scss +3 -3
  52. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/ui/trumbowyg.colors.css +45 -40
  53. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/ui/trumbowyg.colors.min.css +1 -1
  54. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/trumbowyg.emoji.js +916 -848
  55. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/trumbowyg.emoji.min.js +9 -1
  56. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/ui/sass/trumbowyg.emoji.scss +3 -3
  57. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/ui/trumbowyg.emoji.css +23 -18
  58. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/ui/trumbowyg.emoji.min.css +1 -1
  59. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/fontfamily/trumbowyg.fontfamily.js +22 -7
  60. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/fontfamily/trumbowyg.fontfamily.min.js +1 -1
  61. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/fontsize/trumbowyg.fontsize.js +100 -8
  62. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/fontsize/trumbowyg.fontsize.min.js +1 -1
  63. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/giphy/trumbowyg.giphy.js +251 -172
  64. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/giphy/trumbowyg.giphy.min.js +1 -1
  65. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/giphy/ui/sass/trumbowyg.giphy.scss +17 -16
  66. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/giphy/ui/trumbowyg.giphy.css +60 -47
  67. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/giphy/ui/trumbowyg.giphy.min.css +2 -2
  68. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/highlight/trumbowyg.highlight.js +24 -4
  69. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/highlight/trumbowyg.highlight.min.js +1 -1
  70. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/highlight/ui/sass/trumbowyg.highlight.scss +12 -12
  71. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/highlight/ui/trumbowyg.highlight.css +13 -10
  72. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/highlight/ui/trumbowyg.highlight.min.css +2 -2
  73. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/history/trumbowyg.history.js +6 -61
  74. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/history/trumbowyg.history.min.js +8 -1
  75. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/indent/trumbowyg.indent.js +31 -2
  76. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/indent/trumbowyg.indent.min.js +9 -1
  77. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/insertaudio/trumbowyg.insertaudio.js +18 -0
  78. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/insertaudio/trumbowyg.insertaudio.min.js +8 -1
  79. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/lineheight/trumbowyg.lineheight.js +50 -5
  80. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/lineheight/trumbowyg.lineheight.min.js +1 -1
  81. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mathml/trumbowyg.mathml.js +97 -50
  82. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mathml/trumbowyg.mathml.min.js +8 -1
  83. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mathml/ui/sass/trumbowyg.mathml.scss +31 -26
  84. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mathml/ui/trumbowyg.mathml.css +29 -21
  85. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mathml/ui/trumbowyg.mathml.min.css +2 -2
  86. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mention/trumbowyg.mention.js +12 -0
  87. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mention/trumbowyg.mention.min.js +10 -1
  88. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mention/ui/sass/trumbowyg.mention.scss +3 -3
  89. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mention/ui/trumbowyg.mention.css +16 -17
  90. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/mention/ui/trumbowyg.mention.min.css +2 -2
  91. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/noembed/trumbowyg.noembed.js +51 -20
  92. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/noembed/trumbowyg.noembed.min.js +8 -1
  93. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/pasteembed/trumbowyg.pasteembed.js +36 -53
  94. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/pasteembed/trumbowyg.pasteembed.min.js +10 -1
  95. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/pasteimage/trumbowyg.pasteimage.min.js +9 -0
  96. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/preformatted/trumbowyg.preformatted.js +12 -0
  97. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/preformatted/trumbowyg.preformatted.min.js +8 -1
  98. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/resizimg/trumbowyg.resizimg.min.js +3 -1
  99. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/ruby/trumbowyg.ruby.js +20 -0
  100. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/ruby/trumbowyg.ruby.min.js +10 -1
  101. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/specialchars/trumbowyg.specialchars.js +18 -0
  102. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/specialchars/trumbowyg.specialchars.min.js +8 -1
  103. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/specialchars/ui/sass/trumbowyg.specialchars.scss +3 -3
  104. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/specialchars/ui/trumbowyg.specialchars.css +25 -19
  105. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/specialchars/ui/trumbowyg.specialchars.min.css +1 -1
  106. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/speechrecognition/trumbowyg.speechrecognition.js +198 -0
  107. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/speechrecognition/trumbowyg.speechrecognition.min.js +11 -0
  108. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/table/trumbowyg.table.js +1436 -158
  109. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/table/trumbowyg.table.min.js +11 -1
  110. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/table/ui/sass/trumbowyg.table.scss +157 -32
  111. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/table/ui/trumbowyg.table.css +143 -22
  112. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/table/ui/trumbowyg.table.min.css +2 -2
  113. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/template/trumbowyg.template.js +9 -0
  114. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/template/trumbowyg.template.min.js +1 -1
  115. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/tenor/trumbowyg.tenor.js +319 -0
  116. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/tenor/trumbowyg.tenor.min.js +1 -0
  117. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/tenor/ui/sass/trumbowyg.tenor.scss +124 -0
  118. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/tenor/ui/trumbowyg.tenor.css +119 -0
  119. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/tenor/ui/trumbowyg.tenor.min.css +2 -0
  120. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/upload/trumbowyg.upload.js +119 -76
  121. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/upload/trumbowyg.upload.min.js +13 -1
  122. data/app/assets/javascripts/activeadmin/trumbowyg/trumbowyg.js +126 -80
  123. data/app/assets/javascripts/activeadmin/trumbowyg/trumbowyg.min.js +2 -12
  124. data/app/assets/stylesheets/activeadmin/_trumbowyg_input.scss +3 -1
  125. data/app/assets/stylesheets/activeadmin/trumbowyg/trumbowyg.css +776 -0
  126. data/app/assets/stylesheets/activeadmin/trumbowyg/trumbowyg.min.css +3 -0
  127. data/app/assets/stylesheets/activeadmin/trumbowyg/trumbowyg.min.css.map +1 -0
  128. data/lib/activeadmin/trumbowyg/version.rb +1 -1
  129. data/lib/tasks/trumbowyg.rake +2 -2
  130. metadata +23 -26
  131. data/app/assets/javascripts/activeadmin/trumbowyg/langs/no_nb.min.js +0 -10
  132. data/app/assets/stylesheets/activeadmin/trumbowyg/trumbowyg.scss +0 -836
@@ -1,32 +1,163 @@
1
1
  /* ===========================================================
2
- * trumbowyg.table.custom.js v2.0
2
+ * trumbowyg.table.js v3.0
3
3
  * Table plugin for Trumbowyg
4
4
  * http://alex-d.github.com/Trumbowyg
5
5
  * ===========================================================
6
- * Author : Sven Dunemann [dunemann@forelabs.eu]
6
+ * Author : Alexandre Demode (Alex-D)
7
+ * Twitter : @AlexandreDemode
8
+ * Website : alex-d.fr
9
+ * Original Author : Sven Dunemann [dunemann@forelabs.eu]
7
10
  */
8
11
 
9
12
  (function ($) {
10
13
  'use strict';
11
14
 
15
+ // Throttle helper
16
+ function trumbowygThrottle(callback, delay) {
17
+ var last;
18
+ var timer;
19
+
20
+ return function () {
21
+ var context = this;
22
+ var now = new Date().getTime();
23
+ var args = arguments;
24
+
25
+ if (last && now < last + delay) {
26
+ clearTimeout(timer);
27
+ timer = setTimeout(function () {
28
+ last = now;
29
+ callback.apply(context, args);
30
+ }, delay);
31
+ return;
32
+ }
33
+
34
+ last = now;
35
+ callback.apply(context, args);
36
+ };
37
+ }
38
+
12
39
  var defaultOptions = {
13
40
  rows: 8,
14
41
  columns: 8,
15
- styler: 'table'
42
+ allowHorizontalResize: true,
43
+ colorList: [
44
+ 'ffffff', '000000', 'eeece1', '1f497d', '4f81bd', 'c0504d', '9bbb59', '8064a2', '4bacc6', 'f79646', 'ffff00',
45
+ 'f2f2f2', '7f7f7f', 'ddd9c3', 'c6d9f0', 'dbe5f1', 'f2dcdb', 'ebf1dd', 'e5e0ec', 'dbeef3', 'fdeada', 'fff2ca',
46
+ 'd8d8d8', '595959', 'c4bd97', '8db3e2', 'b8cce4', 'e5b9b7', 'd7e3bc', 'ccc1d9', 'b7dde8', 'fbd5b5', 'ffe694',
47
+ 'bfbfbf', '3f3f3f', '938953', '548dd4', '95b3d7', 'd99694', 'c3d69b', 'b2a2c7', 'b7dde8', 'fac08f', 'f2c314',
48
+ 'a5a5a5', '262626', '494429', '17365d', '366092', '953734', '76923c', '5f497a', '92cddc', 'e36c09', 'c09100',
49
+ '7f7f7f', '0c0c0c', '1d1b10', '0f243e', '244061', '632423', '4f6128', '3f3151', '31859b', '974806', '7f6000'
50
+ ],
51
+ backgroundColorList: null, // fallbacks on colorList
52
+ allowCustomBackgroundColor: true,
53
+ displayBackgroundColorsAsList: false,
54
+ borderColorList: null, // fallbacks on colorList
55
+ allowCustomBorderColor: true,
56
+ displayBorderColorsAsList: false,
57
+ dropdown: [
58
+ {
59
+ title: 'tableRows',
60
+ buttons: [
61
+ 'tableAddHeaderRow',
62
+ 'tableAddRowAbove',
63
+ 'tableAddRow',
64
+ 'tableDeleteRow',
65
+ ],
66
+ },
67
+ {
68
+ title: 'tableColumns',
69
+ buttons: [
70
+ 'tableAddColumnLeft',
71
+ 'tableAddColumn',
72
+ 'tableDeleteColumn',
73
+ ],
74
+ },
75
+ {
76
+ title: 'tableVerticalAlign',
77
+ buttons: [
78
+ 'tableVerticalAlignTop',
79
+ 'tableVerticalAlignMiddle',
80
+ 'tableVerticalAlignBottom',
81
+ ],
82
+ },
83
+ {
84
+ title: 'tableOthers',
85
+ buttons: [
86
+ // Cell merge/split
87
+ 'tableMergeCells',
88
+ 'tableUnmergeCells',
89
+ 'tableDestroy',
90
+ ]
91
+ }
92
+ ],
16
93
  };
17
94
 
95
+ function ucFirst(value) {
96
+ return value[0].toUpperCase() + value.slice(1);
97
+ }
98
+
99
+ function hex(x) {
100
+ return ('0' + parseInt(x).toString(16)).slice(-2);
101
+ }
102
+
103
+ function colorToHex(rgb) {
104
+ if (rgb.search('rgb') === -1) {
105
+ return rgb.replace('#', '');
106
+ } else if (rgb === 'rgba(0, 0, 0, 0)') {
107
+ return 'transparent';
108
+ } else {
109
+ rgb = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d?(.\d+)))?\)$/);
110
+ if (rgb == null) {
111
+ return 'transparent'; // No match, return transparent as unkown color
112
+ }
113
+ return hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
114
+ }
115
+ }
116
+
18
117
  $.extend(true, $.trumbowyg, {
19
118
  langs: {
20
119
  // jshint camelcase:false
21
120
  en: {
22
121
  table: 'Insert table',
23
- tableAddRow: 'Add row',
24
- tableAddRowAbove: 'Add row above',
25
- tableAddColumnLeft: 'Add column to the left',
26
- tableAddColumn: 'Add column to the right',
122
+ tableRows: 'Rows',
123
+ tableColumns: 'Columns',
124
+ tableVerticalAlign: 'Vertical align',
125
+ tableOthers: 'Others',
126
+ tableAddHeaderRow: 'Insert head row',
127
+ tableAddRowAbove: 'Insert row above',
128
+ tableAddRow: 'Insert row below',
129
+ tableAddColumnLeft: 'Insert column to the left',
130
+ tableAddColumn: 'Insert column to the right',
27
131
  tableDeleteRow: 'Delete row',
28
132
  tableDeleteColumn: 'Delete column',
29
133
  tableDestroy: 'Delete table',
134
+ tableMergeCells: 'Merge cells',
135
+ tableUnmergeCells: 'Unmerge cells',
136
+ tableVerticalAlignTop: 'Align text to top',
137
+ tableVerticalAlignMiddle: 'Center text vertically',
138
+ tableVerticalAlignBottom: 'Align text to bottom',
139
+ tableCellBackgroundColor: 'Cell background color',
140
+ tableBorderColor: 'Table border color'
141
+ },
142
+ az: {
143
+ table: 'Cədvəl yerləşdir',
144
+ tableAddRow: 'Sətir əlavə et',
145
+ tableAddRowAbove: 'Yuxarı sətir əlavə et',
146
+ tableAddColumnLeft: 'Sola sütun əlavə et',
147
+ tableAddColumn: 'Sağa sütun əlavə et',
148
+ tableDeleteRow: 'Sətri sil',
149
+ tableDeleteColumn: 'Sütunu sil',
150
+ tableDestroy: 'Cədvəli sil',
151
+ },
152
+ ca: {
153
+ table: 'Inserir taula',
154
+ tableAddRow: 'Afegir fila',
155
+ tableAddRowAbove: 'Afegir fila a dalt',
156
+ tableAddColumnLeft: 'Afegir columna a l\'esquerra',
157
+ tableAddColumn: 'Afegir columna a la dreta',
158
+ tableDeleteRow: 'Esborrar fila',
159
+ tableDeleteColumn: 'Esborrar columna',
160
+ tableDestroy: 'Esborrar taula',
30
161
  error: 'Error'
31
162
  },
32
163
  cs: {
@@ -35,7 +166,6 @@
35
166
  tableAddRowAbove: 'Přidat řádek',
36
167
  tableAddColumnLeft: 'Přidat sloupec',
37
168
  tableAddColumn: 'Přidat sloupec',
38
- error: 'Chyba'
39
169
  },
40
170
  da: {
41
171
  table: 'Indsæt tabel',
@@ -46,18 +176,28 @@
46
176
  tableDeleteRow: 'Slet række',
47
177
  tableDeleteColumn: 'Slet kolonne',
48
178
  tableDestroy: 'Slet tabel',
49
- error: 'Fejl'
50
179
  },
51
180
  de: {
52
181
  table: 'Tabelle einfügen',
53
- tableAddRow: 'Zeile hinzufügen',
54
- tableAddRowAbove: 'Zeile hinzufügen',
55
- tableAddColumnLeft: 'Spalte hinzufügen',
56
- tableAddColumn: 'Spalte hinzufügen',
182
+ tableRows: 'Zeilen',
183
+ tableColumns: 'Spalten',
184
+ tableVerticalAlign: 'Vertikal ausrichten',
185
+ tableOthers: 'Andere',
186
+ tableAddHeaderRow: 'Kopfzeile einfügen',
187
+ tableAddRowAbove: 'Zeile oberhalb einfügen',
188
+ tableAddRow: 'Zeile unterhalb einfügen',
189
+ tableAddColumnLeft: 'Spalte links einfügen',
190
+ tableAddColumn: 'Spalte rechts einfügen',
57
191
  tableDeleteRow: 'Zeile löschen',
58
192
  tableDeleteColumn: 'Spalte löschen',
59
193
  tableDestroy: 'Tabelle löschen',
60
- error: 'Error'
194
+ tableMergeCells: 'Zellen zusammenführen',
195
+ tableUnmergeCells: 'Zellen trennen',
196
+ tableVerticalAlignTop: 'Text nach oben ausrichten',
197
+ tableVerticalAlignMiddle: 'Text vertikal zentrieren',
198
+ tableVerticalAlignBottom: 'Text nach unten ausrichten',
199
+ tableCellBackgroundColor: 'Hintergrundfarbe der Zelle',
200
+ tableBorderColor: 'Farbe des Tabellenrahmens'
61
201
  },
62
202
  et: {
63
203
  table: 'Sisesta tabel',
@@ -68,18 +208,28 @@
68
208
  tableDeleteRow: 'Kustuta rida',
69
209
  tableDeleteColumn: 'Kustuta tulp',
70
210
  tableDestroy: 'Kustuta tabel',
71
- error: 'Viga'
72
211
  },
73
212
  fr: {
74
213
  table: 'Insérer un tableau',
75
- tableAddRow: 'Ajouter des lignes',
76
- tableAddRowAbove: 'Ajouter des lignes',
77
- tableAddColumnLeft: 'Ajouter des colonnes',
78
- tableAddColumn: 'Ajouter des colonnes',
79
- tableDeleteRow: 'Effacer la ligne',
80
- tableDeleteColumn: 'Effacer la colonne',
81
- tableDestroy: 'Effacer le tableau',
82
- error: 'Erreur'
214
+ tableRows: 'Lignes',
215
+ tableColumns: 'Colonnes',
216
+ tableVerticalAlign: 'Alignement vertical',
217
+ tableOthers: 'Autres',
218
+ tableAddHeaderRow: 'Insérer une line d\'en-tête',
219
+ tableAddRowAbove: 'Insérer une ligne au dessus',
220
+ tableAddRow: 'Insérer une ligne en dessous',
221
+ tableAddColumnLeft: 'Insérer une colonne à gauche',
222
+ tableAddColumn: 'Insérer une colonne à droite',
223
+ tableDeleteRow: 'Supprimer la ligne',
224
+ tableDeleteColumn: 'Supprimer la colonne',
225
+ tableDestroy: 'Supprimer le tableau',
226
+ tableMergeCells: 'Fusionner les cellules',
227
+ tableUnmergeCells: 'Dissocier les cellules',
228
+ tableVerticalAlignTop: 'Aligner en haut',
229
+ tableVerticalAlignMiddle: 'Aligner au milieu',
230
+ tableVerticalAlignBottom: 'Aligner en bas',
231
+ tableCellBackgroundColor: 'Couleur de fond des cellules',
232
+ tableBorderColor: 'Couleur de la bordure du tableau'
83
233
  },
84
234
  hu: {
85
235
  table: 'Táblázat beszúrás',
@@ -90,7 +240,6 @@
90
240
  tableDeleteRow: 'Sor törlés',
91
241
  tableDeleteColumn: 'Oszlop törlés',
92
242
  tableDestroy: 'Táblázat törlés',
93
- error: 'Hiba'
94
243
  },
95
244
  id: {
96
245
  table: 'Sisipkan tabel',
@@ -101,7 +250,6 @@
101
250
  tableDeleteRow: 'Hapus baris',
102
251
  tableDeleteColumn: 'Hapus kolom',
103
252
  tableDestroy: 'Hapus tabel',
104
- error: 'Galat'
105
253
  },
106
254
  ja: {
107
255
  table: '表の挿入',
@@ -109,7 +257,6 @@
109
257
  tableAddRowAbove: '行の追加',
110
258
  tableAddColumnLeft: '列の追加',
111
259
  tableAddColumn: '列の追加',
112
- error: 'エラー'
113
260
  },
114
261
  ko: {
115
262
  table: '표 넣기',
@@ -120,18 +267,28 @@
120
267
  tableDeleteRow: '줄 삭제',
121
268
  tableDeleteColumn: '칸 삭제',
122
269
  tableDestroy: '표 지우기',
123
- error: '에러'
124
270
  },
125
271
  pt_br: {
126
272
  table: 'Inserir tabela',
127
- tableAddRow: 'Adicionar linha',
128
- tableAddRowAbove: 'Adicionar linha',
129
- tableAddColumnLeft: 'Adicionar coluna',
130
- tableAddColumn: 'Adicionar coluna',
131
- tableDeleteRow: 'Deletar linha',
132
- tableDeleteColumn: 'Deletar coluna',
133
- tableDestroy: 'Deletar tabela',
134
- error: 'Erro'
273
+ tableRows: 'Linhas',
274
+ tableColumns: 'Colunas',
275
+ tableVerticalAlign: 'Alinhamento',
276
+ tableOthers: 'Outros',
277
+ tableAddHeaderRow: 'Inserir cabeçalho',
278
+ tableAddRowAbove: 'Inserir linha',
279
+ tableAddRow: 'Inserir linha',
280
+ tableAddColumnLeft: 'Inserir coluna',
281
+ tableAddColumn: 'Inserir coluna',
282
+ tableDeleteRow: 'Excluir linha',
283
+ tableDeleteColumn: 'Excluir coluna',
284
+ tableDestroy: 'Excluir tabela',
285
+ tableMergeCells: 'Mesclar células',
286
+ tableUnmergeCells: 'Dividir células',
287
+ tableVerticalAlignTop: 'Alinhar acima',
288
+ tableVerticalAlignMiddle: 'Alinhar ao centro',
289
+ tableVerticalAlignBottom: 'Alinhar abaixo',
290
+ tableCellBackgroundColor: 'Cor da célula',
291
+ tableBorderColor: 'Cor da borda da tabela'
135
292
  },
136
293
  ru: {
137
294
  table: 'Вставить таблицу',
@@ -142,7 +299,16 @@
142
299
  tableDeleteRow: 'Удалить строку',
143
300
  tableDeleteColumn: 'Удалить столбец',
144
301
  tableDestroy: 'Удалить таблицу',
145
- error: 'Ошибка'
302
+ },
303
+ sl: {
304
+ table: 'Dodaj tabelo',
305
+ tableAddRow: 'Dodaj vrstico',
306
+ tableAddRowAbove: 'Vrini vrstico',
307
+ tableAddColumnLeft: 'Vrini stolpec',
308
+ tableAddColumn: 'Dodaj stolpec',
309
+ tableDeleteRow: 'Izbriši vrstico',
310
+ tableDeleteColumn: 'Izbriši stolpec',
311
+ tableDestroy: 'Izbriši tabelo',
146
312
  },
147
313
  sk: {
148
314
  table: 'Vytvoriť tabuľky',
@@ -150,7 +316,6 @@
150
316
  tableAddRowAbove: 'Pridať riadok',
151
317
  tableAddColumnLeft: 'Pridať stĺpec',
152
318
  tableAddColumn: 'Pridať stĺpec',
153
- error: 'Chyba'
154
319
  },
155
320
  tr: {
156
321
  table: 'Tablo ekle',
@@ -161,7 +326,6 @@
161
326
  tableDeleteRow: 'Satırı sil',
162
327
  tableDeleteColumn: 'Sütunu sil',
163
328
  tableDestroy: 'Tabloyu sil',
164
- error: 'Hata'
165
329
  },
166
330
  zh_tw: {
167
331
  table: '插入表格',
@@ -172,7 +336,6 @@
172
336
  tableDeleteRow: '刪除行',
173
337
  tableDeleteColumn: '刪除列',
174
338
  tableDestroy: '刪除表格',
175
- error: '錯誤'
176
339
  },
177
340
  es: {
178
341
  table: 'Insertar tabla',
@@ -183,15 +346,21 @@
183
346
  tableDeleteRow: 'Borrar fila',
184
347
  tableDeleteColumn: 'Borrar columna',
185
348
  tableDestroy: 'Borrar tabla',
186
- error: 'Error'
187
349
  }// jshint camelcase:true
188
350
  },
189
351
 
190
352
  plugins: {
191
353
  table: {
354
+ // jshint maxstatements:false
192
355
  init: function (t) {
193
356
  t.o.plugins.table = $.extend(true, {}, defaultOptions, t.o.plugins.table || {});
194
357
 
358
+ // State
359
+ var tableSelectedCells;
360
+
361
+ ////////////////////////////////////////////////////
362
+ // Dropdown
363
+
195
364
  var buildButtonDef = {
196
365
  fn: function () {
197
366
  t.saveRange();
@@ -216,36 +385,73 @@
216
385
 
217
386
  // when active table show AddRow / AddColumn
218
387
  if (t.$box.find('.' + t.o.prefix + 'table-button').hasClass(t.o.prefix + 'active-button')) {
219
- $dropdown.append(t.buildSubBtn('tableAddRowAbove'));
220
- $dropdown.append(t.buildSubBtn('tableAddRow'));
221
- $dropdown.append(t.buildSubBtn('tableAddColumnLeft'));
222
- $dropdown.append(t.buildSubBtn('tableAddColumn'));
223
- $dropdown.append(t.buildSubBtn('tableDeleteRow'));
224
- $dropdown.append(t.buildSubBtn('tableDeleteColumn'));
225
- $dropdown.append(t.buildSubBtn('tableDestroy'));
388
+ var $table = $(t.doc.getSelection().anchorNode).closest('table', t.$ed[0]);
389
+ var tableState = getTableState($table);
390
+ var hasSelectedCells = tableSelectedCells !== undefined;
391
+ $(t.o.plugins.table.dropdown).each(function (_, buttonGroup) {
392
+ $dropdown.append($('<div/>', {
393
+ html: t.lang[buttonGroup.title] ? t.lang[buttonGroup.title] : buttonGroup.title,
394
+ class: t.o.prefix + 'table-dropdown-title'
395
+ })).text();
396
+ var $buttonGroup = $('<div/>', {
397
+ class: t.o.prefix + 'dropdown-button-group'
398
+ });
399
+
400
+ $(buttonGroup.buttons).each(function (_, buttonName) {
401
+ // Conditional thead button
402
+ if (buttonName === 'tableAddHeaderRow') {
403
+ var hasThead = $('thead', $table).length !== 0;
404
+ if (hasThead) {
405
+ return;
406
+ }
407
+ }
408
+
409
+ // Conditional merge button
410
+ if (buttonName === 'tableMergeCells' && !hasSelectedCells) {
411
+ return;
412
+ }
413
+
414
+ // Conditional unmerge button
415
+ if (buttonName === 'tableUnmergeCells') {
416
+ var hasAtLeastOneMergedCell = false;
417
+ foreachSelectedCell(function ($cell) {
418
+ var isMergedCell = $cell.is('[colspan]') || $cell.is('[rowspan]');
419
+ hasAtLeastOneMergedCell = hasAtLeastOneMergedCell || isMergedCell;
420
+ }, tableState);
421
+ if (!hasAtLeastOneMergedCell) {
422
+ return;
423
+ }
424
+ }
425
+
426
+ $buttonGroup.append(t.buildSubBtn(buttonName));
427
+ });
428
+
429
+ $dropdown.append($buttonGroup);
430
+ });
226
431
  } else {
227
- var tableSelect = $('<table/>');
228
- $('<tbody/>').appendTo(tableSelect);
432
+ var $tableSelect = $('<table/>');
433
+ $('<tbody/>').appendTo($tableSelect);
229
434
  for (var i = 0; i < t.o.plugins.table.rows; i += 1) {
230
- var row = $('<tr/>').appendTo(tableSelect);
435
+ var row = $('<tr/>').appendTo($tableSelect);
231
436
  for (var j = 0; j < t.o.plugins.table.columns; j += 1) {
232
437
  $('<td/>').appendTo(row);
233
438
  }
234
439
  }
235
- tableSelect.find('td').on('mouseover', tableAnimate);
236
- tableSelect.find('td').on('mousedown', tableBuild);
440
+ $tableSelect.find('td').on('mouseover', toggleActiveDropdownCells);
441
+ $tableSelect.find('td').on('mousedown', tableBuild);
237
442
 
238
- $dropdown.append(tableSelect);
443
+ $dropdown.append($tableSelect);
239
444
  $dropdown.append($('<div class="trumbowyg-table-size">1x1</div>'));
240
445
  }
241
446
 
242
447
  t.dropdown(btnName);
243
- }
448
+ },
449
+ class: t.o.prefix + 'open-dropdown'
244
450
  };
245
451
 
246
- var tableAnimate = function(columnEvent) {
452
+ var toggleActiveDropdownCells = function (columnEvent) {
247
453
  var column = $(columnEvent.target),
248
- table = column.closest('table'),
454
+ table = column.closest('table', t.$ed[0]),
249
455
  colIndex = this.cellIndex,
250
456
  rowIndex = this.parentNode.rowIndex;
251
457
 
@@ -254,62 +460,227 @@
254
460
 
255
461
  for (var i = 0; i <= rowIndex; i += 1) {
256
462
  for (var j = 0; j <= colIndex; j += 1) {
257
- table.find('tr:nth-of-type('+(i+1)+')').find('td:nth-of-type('+(j+1)+')').addClass('active');
463
+ table.find('tr:nth-of-type(' + (i + 1) + ')').find('td:nth-of-type(' + (j + 1) + ')').addClass('active');
258
464
  }
259
465
  }
260
466
 
261
467
  // set label
262
- table.next('.trumbowyg-table-size').html((colIndex+1) + 'x' + (rowIndex+1));
468
+ table.next('.trumbowyg-table-size').html((colIndex + 1) + 'x' + (rowIndex + 1));
263
469
  };
264
470
 
265
- var tableBuild = function() {
471
+ var applyTagClassesToElement = function (element) {
472
+ var tagClasses = t.o.tagClasses[element.tagName.toLowerCase()];
473
+ if (!tagClasses) {
474
+ return;
475
+ }
476
+ $(element).addClass(tagClasses);
477
+ };
478
+ var applyTagClasses = function ($table) {
479
+ applyTagClassesToElement($table[0]);
480
+ $('*', $table).each(function (_, element) {
481
+ applyTagClassesToElement(element);
482
+ });
483
+ };
484
+ var tableBuild = function () {
266
485
  t.saveRange();
267
486
 
268
- var tabler = $('<table/>');
269
- $('<tbody/>').appendTo(tabler);
270
- if (t.o.plugins.table.styler) {
271
- tabler.attr('class', t.o.plugins.table.styler);
487
+ var $newTable = $('<table/>');
488
+
489
+ // Build thead
490
+ var $thead = $('<thead/>');
491
+ var $theadTr = $('<tr/>');
492
+ $theadTr.appendTo($thead);
493
+ for (var th = 0; th <= this.cellIndex; th += 1) {
494
+ $('<th/>', {scope: 'col'}).appendTo($theadTr);
272
495
  }
496
+ $thead.appendTo($newTable);
497
+
498
+ // Build tbody
499
+ var $tbody = $('<tbody/>');
273
500
 
274
501
  var colIndex = this.cellIndex,
275
502
  rowIndex = this.parentNode.rowIndex;
276
503
 
277
504
  for (var i = 0; i <= rowIndex; i += 1) {
278
- var row = $('<tr></tr>').appendTo(tabler);
505
+ var row = $('<tr/>').appendTo($tbody);
279
506
  for (var j = 0; j <= colIndex; j += 1) {
280
507
  $('<td/>').appendTo(row);
281
508
  }
282
509
  }
283
510
 
284
- t.range.deleteContents();
285
- t.range.insertNode(tabler[0]);
286
- t.$c.trigger('tbwchange');
511
+ $tbody.appendTo($newTable);
512
+
513
+ // Apply tag classes
514
+ applyTagClasses($newTable);
515
+
516
+ // Find first parent element
517
+ var rangeNode = t.range.endContainer;
518
+ while (rangeNode.nodeType !== Node.ELEMENT_NODE) {
519
+ rangeNode = rangeNode.parentNode;
520
+ }
521
+
522
+ // Put range after the parent of the selected element
523
+ if (rangeNode !== t.$ed[0]) {
524
+ t.range.setEndAfter(rangeNode);
525
+ }
526
+
527
+ // Insert table after the range
528
+ t.range.collapse();
529
+ t.range.insertNode($newTable[0]);
530
+
531
+ // Remove empty paragraph
532
+ if (rangeNode.nodeName === 'P' && rangeNode.textContent.trim().length === 0) {
533
+ rangeNode.remove();
534
+ }
535
+
536
+ t.syncCode();
537
+
538
+ rebuildResizeLayers();
287
539
  };
288
540
 
289
- var addRow = {
290
- title: t.lang.tableAddRow,
291
- text: t.lang.tableAddRow,
292
- ico: 'row-below',
541
+ var getTableState = function ($table) {
542
+ var $tableRows = $('tr', $table);
543
+ var tableState = [];
544
+ for (var i = 0; i < $tableRows.length; i += 1) {
545
+ tableState.push([]);
546
+ }
293
547
 
294
- fn: function () {
295
- t.saveRange();
548
+ $tableRows.each(function (rowIndex, row) {
549
+ var columnIndex = 0;
550
+ $('td, th', $(row)).each(function (cellIndex, cell) {
551
+ var $cell = $(cell);
552
+ var colspanAttr = $cell.attr('colspan');
553
+ var rowspanAttr = $cell.attr('rowspan');
296
554
 
297
- var node = t.doc.getSelection().focusNode;
298
- var focusedRow = $(node).closest('tr');
299
- var table = $(node).closest('table');
555
+ var colspan = parseInt(colspanAttr > 0 ? colspanAttr : 1, 10);
556
+ var rowspan = parseInt(rowspanAttr > 0 ? rowspanAttr : 1, 10);
300
557
 
301
- if(table.length > 0) {
302
- var row = $('<tr/>');
303
- // add columns according to current columns count
304
- for (var i = 0; i < table.find('tr')[0].childElementCount; i += 1) {
305
- $('<td/>').appendTo(row);
558
+ while (tableState[rowIndex][columnIndex] !== undefined) {
559
+ columnIndex += 1;
306
560
  }
307
- // add row to table
308
- focusedRow.after(row);
561
+
562
+ tableState[rowIndex][columnIndex] = {
563
+ tag: cell.tagName,
564
+ element: cell,
565
+ colspan: colspan,
566
+ rowspan: rowspan,
567
+ };
568
+
569
+ for (var cols = 1; cols < colspan; cols += 1) {
570
+ tableState[rowIndex][columnIndex + cols] = {
571
+ mergedIn: [rowIndex, columnIndex]
572
+ };
573
+ }
574
+
575
+ for (var rows = 1; rows < rowspan; rows += 1) {
576
+ tableState[rowIndex + rows][columnIndex] = {
577
+ mergedIn: [rowIndex, columnIndex]
578
+ };
579
+
580
+ for (var colsInRow = 1; colsInRow < colspan; colsInRow += 1) {
581
+ tableState[rowIndex + rows][columnIndex + colsInRow] = {
582
+ mergedIn: [rowIndex, columnIndex]
583
+ };
584
+ }
585
+ }
586
+
587
+ columnIndex += colspan;
588
+ });
589
+ });
590
+
591
+ return tableState;
592
+ };
593
+
594
+
595
+ ////////////////////////////////////////////////////
596
+ // Buttons
597
+
598
+ var tableButtonAction = function (callback) {
599
+ return function () {
600
+ t.saveRange();
601
+
602
+ var node = t.doc.getSelection().anchorNode;
603
+ var $table = $(node).closest('table', t.$ed[0]);
604
+
605
+ if ($table.length === 0) {
606
+ return;
607
+ }
608
+
609
+ if (node.tagName === 'TR') {
610
+ node = $('td, th', node)[0];
309
611
  }
612
+ var $focusedRow = $(node).closest('tr', t.$ed[0]);
613
+
614
+ var tableState = getTableState($table);
615
+
616
+ callback($table, $focusedRow, node, tableState);
310
617
 
311
618
  t.syncCode();
312
- }
619
+ };
620
+ };
621
+
622
+
623
+ ////// Rows
624
+
625
+ var addRowButtonAction = function (isBefore = false) {
626
+ return tableButtonAction(function ($table, $focusedRow, node, tableState) {
627
+ var $rows = $('tr', $table);
628
+ var $newRow = $('<tr/>');
629
+
630
+ // Shift one row before if insert before
631
+ var focusedRowIndex = $rows.index($focusedRow);
632
+ if (isBefore) {
633
+ focusedRowIndex = Math.max(0, focusedRowIndex - 1);
634
+ $focusedRow = $($rows[focusedRowIndex]);
635
+ } else {
636
+ var rawCellRowspan = $(node).closest('td, th', t.$ed[0]).attr('rowspan');
637
+ var cellRowspan = parseInt(rawCellRowspan ? rawCellRowspan : 1, 10);
638
+ focusedRowIndex += cellRowspan - 1;
639
+ $focusedRow = $($rows[focusedRowIndex]);
640
+ }
641
+
642
+ // Cannot add line to thead, so move to first tbody row
643
+ var $tbodyRows = $('tbody tr', $table);
644
+ var isFocusInHead = $focusedRow.closest('thead').length !== 0;
645
+ if (isFocusInHead) {
646
+ $focusedRow = $tbodyRows.first();
647
+ }
648
+
649
+ // add columns according to current columns count
650
+ var focusedRowState = tableState[focusedRowIndex];
651
+ var nextRowState = tableState[focusedRowIndex + 1];
652
+ var columnCount = tableState[0].length;
653
+ for (var columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {
654
+ if (nextRowState !== undefined) {
655
+ var originCellState = focusedRowState[columnIndex];
656
+ var originCellMergedInState = getCellState(tableState, originCellState.mergedIn);
657
+ var nextCellState = nextRowState[columnIndex];
658
+ var nextCellMergedInState = getCellState(tableState, nextCellState.mergedIn);
659
+
660
+ var realOriginCellState = originCellState.element ? originCellState : originCellMergedInState;
661
+ var originCellElement = realOriginCellState.element;
662
+ var nextCellElement = nextCellState.element ? nextCellState.element : nextCellMergedInState.element;
663
+
664
+ if (originCellElement === nextCellElement) {
665
+ originCellElement.setAttribute('rowspan', realOriginCellState.rowspan + 1);
666
+
667
+ continue;
668
+ }
669
+ }
670
+
671
+ $('<td/>').appendTo($newRow);
672
+ }
673
+
674
+ // add row to table
675
+ if (focusedRowIndex === 0 && (isBefore || isFocusInHead)) {
676
+ $focusedRow.before($newRow);
677
+ } else {
678
+ $focusedRow.after($newRow);
679
+ }
680
+
681
+ applyTagClasses($table);
682
+ rebuildResizeLayers();
683
+ });
313
684
  };
314
685
 
315
686
  var addRowAbove = {
@@ -317,48 +688,114 @@
317
688
  text: t.lang.tableAddRowAbove,
318
689
  ico: 'row-above',
319
690
 
320
- fn: function () {
321
- t.saveRange();
691
+ fn: addRowButtonAction(true),
692
+ };
322
693
 
323
- var node = t.doc.getSelection().focusNode;
324
- var focusedRow = $(node).closest('tr');
325
- var table = $(node).closest('table');
694
+ var addRowBelow = {
695
+ title: t.lang.tableAddRow,
696
+ text: t.lang.tableAddRow,
697
+ ico: 'row-below',
326
698
 
327
- if(table.length > 0) {
328
- var row = $('<tr/>');
329
- // add columns according to current columns count
330
- for (var i = 0; i < table.find('tr')[0].childElementCount; i += 1) {
331
- $('<td/>').appendTo(row);
332
- }
333
- // add row to table
334
- focusedRow.before(row);
699
+ fn: addRowButtonAction(false),
700
+ };
701
+
702
+ var addHeaderRow = {
703
+ title: t.lang.tableAddHeaderRow,
704
+ text: t.lang.tableAddHeaderRow,
705
+ ico: 'header-row',
706
+
707
+ fn: tableButtonAction(function ($table, $focusedRow, node, tableState) {
708
+ var hasThead = $('thead', $table).length !== 0;
709
+ if (hasThead) {
710
+ return false;
335
711
  }
336
712
 
337
- t.syncCode();
338
- }
713
+ var columnCount = tableState[0].length;
714
+
715
+ var $thead = $('<thead/>');
716
+ var $theadRow = $('<tr/>').appendTo($thead);
717
+
718
+ // add columns according to current columns count
719
+ for (var columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {
720
+ $('<th/>').appendTo($theadRow);
721
+ }
722
+
723
+ // add thead to table
724
+ $table.prepend($thead);
725
+
726
+ applyTagClasses($table);
727
+ rebuildResizeLayers();
728
+ }),
339
729
  };
340
730
 
341
- var addColumn = {
342
- title: t.lang.tableAddColumn,
343
- text: t.lang.tableAddColumn,
344
- ico: 'col-right',
345
731
 
346
- fn: function () {
347
- t.saveRange();
732
+ ////// Columns
348
733
 
349
- var node = t.doc.getSelection().focusNode;
350
- var focusedCol = $(node).closest('td');
351
- var table = $(node).closest('table');
352
- var focusedColIdx = focusedCol.index();
734
+ var addColumnButtonAction = function (isBefore = false) {
735
+ return tableButtonAction(function ($table, $focusedRow, node, tableState) {
736
+ var $rows = $('tr', $table);
737
+ var focusedRowIndex = $rows.index($focusedRow);
738
+ var focusedRowState = tableState[focusedRowIndex];
739
+ var $focusedCell = $(node).closest('td, th');
353
740
 
354
- if(table.length > 0) {
355
- $(table).find('tr').each(function() {
356
- $($(this).children()[focusedColIdx]).after('<td></td>');
357
- });
741
+ // Shift one column before if insert before
742
+ var cellIndex = getCellIndex($focusedCell[0], focusedRowState);
743
+ if (isBefore) {
744
+ cellIndex = Math.max(0, cellIndex - 1);
745
+ } else {
746
+ var rawCellColspan = $focusedCell.attr('colspan');
747
+ var cellColspan = parseInt(rawCellColspan ? rawCellColspan : 1, 10);
748
+ cellIndex += cellColspan - 1;
358
749
  }
359
750
 
360
- t.syncCode();
361
- }
751
+ // add a cell to each row
752
+ var rowCount = tableState.length;
753
+ var mustInsertBefore = isBefore && cellIndex === 0;
754
+ for (var rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
755
+ var rowState = tableState[rowIndex];
756
+ var nextCellState = mustInsertBefore ? undefined : rowState[cellIndex + 1];
757
+ if (nextCellState !== undefined) {
758
+ var originCellState = rowState[cellIndex];
759
+ var originCellMergedInState = getCellState(tableState, originCellState.mergedIn);
760
+ var nextCellMergedInState = getCellState(tableState, nextCellState.mergedIn);
761
+
762
+ var realOriginCellState = originCellState.element ? originCellState : originCellMergedInState;
763
+ var originCellElement = realOriginCellState.element;
764
+ var nextCellElement = nextCellState.element ? nextCellState.element : nextCellMergedInState.element;
765
+
766
+ if (originCellElement === nextCellElement) {
767
+ originCellElement.setAttribute('colspan', realOriginCellState.colspan + 1);
768
+
769
+ continue;
770
+ }
771
+ }
772
+
773
+ // Get previous real cell state
774
+ var previousRealCellState;
775
+ var previousColumnShift = 0;
776
+ do {
777
+ var newIndex = cellIndex - previousColumnShift;
778
+ if (newIndex < 0) {
779
+ break;
780
+ }
781
+
782
+ previousRealCellState = rowState[newIndex];
783
+ previousColumnShift += 1;
784
+ } while (previousRealCellState.mergedIn !== undefined);
785
+
786
+ // Create and append the cell next to the previous
787
+ var $previousCell = previousRealCellState.element;
788
+ var newCellElement = t.doc.createElement($previousCell.tagName);
789
+ if (cellIndex === 0 && isBefore) {
790
+ $previousCell.before(newCellElement);
791
+ } else {
792
+ $previousCell.after(newCellElement);
793
+ }
794
+ }
795
+
796
+ applyTagClasses($table);
797
+ rebuildResizeLayers();
798
+ });
362
799
  };
363
800
 
364
801
  var addColumnLeft = {
@@ -366,39 +803,28 @@
366
803
  text: t.lang.tableAddColumnLeft,
367
804
  ico: 'col-left',
368
805
 
369
- fn: function () {
370
- t.saveRange();
371
-
372
- var node = t.doc.getSelection().focusNode;
373
- var focusedCol = $(node).closest('td');
374
- var table = $(node).closest('table');
375
- var focusedColIdx = focusedCol.index();
806
+ fn: addColumnButtonAction(true)
807
+ };
376
808
 
377
- if(table.length > 0) {
378
- $(table).find('tr').each(function() {
379
- $($(this).children()[focusedColIdx]).before('<td></td>');
380
- });
381
- }
809
+ var addColumnRight = {
810
+ title: t.lang.tableAddColumn,
811
+ text: t.lang.tableAddColumn,
812
+ ico: 'col-right',
382
813
 
383
- t.syncCode();
384
- }
814
+ fn: addColumnButtonAction(false)
385
815
  };
386
816
 
817
+
818
+ ////// Delete
819
+
387
820
  var destroy = {
388
821
  title: t.lang.tableDestroy,
389
822
  text: t.lang.tableDestroy,
390
823
  ico: 'table-delete',
391
824
 
392
- fn: function () {
393
- t.saveRange();
394
-
395
- var node = t.doc.getSelection().focusNode,
396
- table = $(node).closest('table');
397
-
398
- table.remove();
399
-
400
- t.syncCode();
401
- }
825
+ fn: tableButtonAction(function ($table) {
826
+ $table.remove();
827
+ })
402
828
  };
403
829
 
404
830
  var deleteRow = {
@@ -406,16 +832,55 @@
406
832
  text: t.lang.tableDeleteRow,
407
833
  ico: 'row-delete',
408
834
 
409
- fn: function () {
410
- t.saveRange();
835
+ fn: tableButtonAction(function ($table, $focusedRow, node, tableState) {
836
+ // Only one row is remaining in the table, remove the table
837
+ if ($('tbody tr', $table).length === 1) {
838
+ $table.remove();
839
+ return;
840
+ }
411
841
 
412
- var node = t.doc.getSelection().focusNode,
413
- row = $(node).closest('tr');
842
+ // Pick element to remove
843
+ var $elementToRemove = $focusedRow;
844
+ var $focusedRowParent = $focusedRow.parent();
845
+ if ($focusedRowParent.is('thead')) {
846
+ $elementToRemove = $focusedRowParent;
847
+ }
414
848
 
415
- row.remove();
849
+ // Manage merged cells
850
+ var $rows = $('tr', $table);
851
+ var rowIndex = $rows.index($(node).closest('tr'));
852
+ for (var y = 0; y < tableState[0].length; y += 1) {
853
+ var cellState = getCellState(tableState, [rowIndex, y], false);
416
854
 
417
- t.syncCode();
418
- }
855
+ if (cellState.rowspan === 1) {
856
+ continue;
857
+ }
858
+
859
+ var originCellState = getCellState(tableState, [rowIndex, y]);
860
+ originCellState.element.setAttribute('rowspan', originCellState.rowspan - 1);
861
+
862
+ // If origin cell is not in this row, continue
863
+ if (cellState.mergedIn !== undefined) {
864
+ continue;
865
+ }
866
+
867
+ // If origin cell is in this row, move it to the next row
868
+ var originCellIndex = getCellIndex(cellState.element, tableState[rowIndex]);
869
+ if (originCellIndex === 0) {
870
+ $($rows[rowIndex + 1]).prepend(originCellState.element);
871
+ continue;
872
+ }
873
+ var nextRowPreviousColumnCellState = getCellState(tableState, [
874
+ rowIndex + 1,
875
+ originCellIndex - 1
876
+ ]);
877
+ $(nextRowPreviousColumnCellState.element).after(originCellState.element);
878
+ }
879
+
880
+ $elementToRemove.remove();
881
+ simplifyCells($table);
882
+ redrawResizeLayers();
883
+ }),
419
884
  };
420
885
 
421
886
  var deleteColumn = {
@@ -423,31 +888,844 @@
423
888
  text: t.lang.tableDeleteColumn,
424
889
  ico: 'col-delete',
425
890
 
426
- fn: function () {
427
- t.saveRange();
891
+ fn: tableButtonAction(function ($table, $focusedRow, node, tableState) {
892
+ var $rows = $('tr', $table);
893
+ var rowIndex = $rows.index($(node).closest('tr'));
894
+ var columnIndex = getCellIndex($(node).closest('td, th')[0], tableState[rowIndex]);
895
+
896
+ for (var x = 0; x < tableState.length; x += 1) {
897
+ var cellState = getCellState(tableState, [x, columnIndex], false);
898
+
899
+ // Reduce cell colspan by 1
900
+ if (cellState.colspan > 1) {
901
+ var originCellState = getCellState(tableState, [x, columnIndex]);
902
+ originCellState.element.setAttribute('colspan', originCellState.colspan - 1);
903
+ continue;
904
+ }
905
+
906
+ // Delete cell if not merged
907
+ cellState.element.remove();
908
+ }
909
+
910
+ simplifyCells();
911
+ redrawResizeLayers();
912
+ })
913
+ };
914
+
915
+
916
+ ////// Cell merging
917
+
918
+ var getCellState = function (tableState, cellCoordinates, mustGetDeep = true) {
919
+ if (cellCoordinates === undefined) {
920
+ return undefined;
921
+ }
922
+
923
+ var cellState = tableState[cellCoordinates[0]][cellCoordinates[1]];
924
+
925
+ if (mustGetDeep && cellState.mergedIn !== undefined) {
926
+ cellState = tableState[cellState.mergedIn[0]][cellState.mergedIn[1]];
927
+ }
928
+
929
+ return cellState;
930
+ };
931
+
932
+ var canMergeSelectedCells = function (tableState) {
933
+ if (tableSelectedCells.length === 0) {
934
+ return false;
935
+ }
936
+
937
+ // Check that all tags are the same
938
+ var firstCellStateCoordinates = tableSelectedCells[0];
939
+ var firstSelectedCellTag = getCellState(tableState, firstCellStateCoordinates).tag;
940
+ var allTagsAreTheSame = tableSelectedCells.every(function (cellCoordinates) {
941
+ var cellState = getCellState(tableState, cellCoordinates);
942
+
943
+ return cellState.tag === firstSelectedCellTag;
944
+ });
945
+
946
+ if (!allTagsAreTheSame) {
947
+ return false;
948
+ }
949
+
950
+ // Check that all selected cells make a rectangle
951
+ var minByRow = [];
952
+ var maxByRow = [];
953
+ $(tableSelectedCells).each(function (_, tableSelectedCell) {
954
+ var y = tableSelectedCell[0];
955
+ var x = tableSelectedCell[1];
956
+ var cellState = tableState[y][x];
957
+
958
+ var cellRowspan = cellState.rowspan;
959
+ var maxRow = y + cellRowspan;
960
+
961
+ for (; y < maxRow; y += 1) {
962
+ if (minByRow[y] === undefined) {
963
+ minByRow[y] = tableState[0].length;
964
+ }
965
+
966
+ if (maxByRow[y] === undefined) {
967
+ maxByRow[y] = 0;
968
+ }
969
+
970
+ minByRow[y] = Math.min(minByRow[y], x);
971
+ maxByRow[y] = Math.max(maxByRow[y], x + cellState.colspan);
972
+ }
973
+ });
974
+
975
+ if (minByRow.length === 0 || maxByRow.length === 0) {
976
+ return false;
977
+ }
978
+
979
+ var allMinAreTheSame = minByRow.every(function (value) {
980
+ return value === minByRow[minByRow.length - 1];
981
+ });
982
+
983
+ var allMaxAreTheSame = maxByRow.every(function (value) {
984
+ return value === maxByRow[maxByRow.length - 1];
985
+ });
986
+
987
+ return allMinAreTheSame && allMaxAreTheSame;
988
+ };
989
+
990
+ var findTopLeftCellInSelection = function () {
991
+ var MAX_VALUE = 999999;
992
+ var topLeftY = MAX_VALUE;
993
+ var topLeftX = MAX_VALUE;
994
+
995
+ $(tableSelectedCells).each(function (_, cell) {
996
+ topLeftY = Math.min(cell[0], topLeftY);
997
+ topLeftX = Math.min(cell[1], topLeftX);
998
+ });
999
+
1000
+ if (topLeftX === MAX_VALUE || topLeftY === MAX_VALUE) {
1001
+ return undefined;
1002
+ }
1003
+
1004
+ return [topLeftY, topLeftX];
1005
+ };
428
1006
 
429
- var node = t.doc.getSelection().focusNode,
430
- table = $(node).closest('table'),
431
- td = $(node).closest('td'),
432
- cellIndex = td.index();
1007
+ var simplifyCells = function ($table) {
1008
+ var tableState = getTableState($table);
433
1009
 
434
- $(table).find('tr').each(function() {
435
- $(this).find('td:eq(' + cellIndex + ')').remove();
1010
+ // Remove rowspan if a row is empty
1011
+ var $rows = $('tr', $table);
1012
+ $(tableState).each(function (rowIndex, rowState) {
1013
+ var isRowEmpty = rowState.every(function (cellState) {
1014
+ return cellState.mergedIn !== undefined;
436
1015
  });
437
1016
 
438
- t.syncCode();
1017
+ if (!isRowEmpty) {
1018
+ return;
1019
+ }
1020
+
1021
+ // Reduce by 1 the rowspan on each cell in previous row
1022
+ $(tableState[rowIndex - 1]).each(function (_, cellState) {
1023
+ if (cellState.mergedIn !== undefined) {
1024
+ cellState = getCellState(tableState, cellState.mergedIn);
1025
+ }
1026
+ cellState.rowspan -= 1;
1027
+
1028
+ if (cellState.rowspan <= 1) {
1029
+ cellState.element.removeAttribute('rowspan');
1030
+ return;
1031
+ }
1032
+
1033
+ cellState.element.setAttribute('rowspan', cellState.rowspan);
1034
+ });
1035
+
1036
+ // Remove empty tr
1037
+ $rows[rowIndex].remove();
1038
+ });
1039
+
1040
+ // Remove empty attributes
1041
+ $('[class=""]', $table).removeAttr('class');
1042
+ $('[style=""]', $table).removeAttr('style');
1043
+ };
1044
+
1045
+ var mergeCells = {
1046
+ title: t.lang.tableMergeCells,
1047
+ text: t.lang.tableMergeCells,
1048
+ ico: 'table-merge',
1049
+
1050
+ fn: tableButtonAction(function ($table, $focusedRow, node, tableState) {
1051
+ if (!canMergeSelectedCells(tableState)) {
1052
+ return;
1053
+ }
1054
+
1055
+ var topLeftCellCoordinates = findTopLeftCellInSelection();
1056
+ if (topLeftCellCoordinates === undefined) {
1057
+ return;
1058
+ }
1059
+
1060
+ var topLeftCellState = getCellState(tableState, topLeftCellCoordinates);
1061
+ var $topLeftCell = $(topLeftCellState.element);
1062
+ var minY = 999999;
1063
+ var maxY = 0;
1064
+ var minX = 999999;
1065
+ var maxX = 0;
1066
+ $(tableSelectedCells).each(function (_, selectedCell) {
1067
+ var y = selectedCell[0];
1068
+ var x = selectedCell[1];
1069
+ var cellState = tableState[y][x];
1070
+
1071
+ minY = Math.min(minY, y);
1072
+ maxY = Math.max(maxY, y + cellState.rowspan - 1);
1073
+ minX = Math.min(minX, x);
1074
+ maxX = Math.max(maxX, x + cellState.colspan - 1);
1075
+
1076
+ if (cellState.element === $topLeftCell[0]) {
1077
+ return;
1078
+ }
1079
+
1080
+ cellState.element.remove();
1081
+ });
1082
+
1083
+ var cellHeight = maxY - minY + 1;
1084
+ var cellWidth = maxX - minX + 1;
1085
+
1086
+ if (cellHeight > 1) {
1087
+ $topLeftCell.attr('rowspan', cellHeight);
1088
+ }
1089
+ if (cellWidth > 1) {
1090
+ $topLeftCell.attr('colspan', cellWidth);
1091
+ }
1092
+
1093
+ simplifyCells($table);
1094
+
1095
+ rebuildResizeLayers();
1096
+ }),
1097
+ };
1098
+
1099
+ var unmergeCells = {
1100
+ title: t.lang.tableUnmergeCells,
1101
+ text: t.lang.tableUnmergeCells,
1102
+ ico: 'table-unmerge',
1103
+
1104
+ fn: tableButtonAction(function ($table, $focusedRow, node, tableState) {
1105
+ foreachSelectedCell(function ($cell) {
1106
+ $cell.removeAttr('colspan').removeAttr('rowspan');
1107
+ var $rows = $('tr', $table);
1108
+
1109
+ var cellRowIndex = $rows.index($cell.closest('tr'));
1110
+ var cellColumnIndex = getCellIndex($cell[0], tableState[cellRowIndex]);
1111
+ var cellState = tableState[cellRowIndex][cellColumnIndex];
1112
+
1113
+ for (var rowIndex = 0; rowIndex < cellState.rowspan; rowIndex += 1) {
1114
+ var colIndex = (rowIndex === 0) ? 1 : 0;
1115
+ var previousCellState = getCellState(tableState, [
1116
+ cellRowIndex + rowIndex,
1117
+ cellColumnIndex + colIndex - 1
1118
+ ]);
1119
+ var previousCellElement = previousCellState.element;
1120
+ for (; colIndex < cellState.colspan; colIndex += 1) {
1121
+ var newCellElement = t.doc.createElement(previousCellElement.tagName);
1122
+ $(previousCellElement).after(newCellElement);
1123
+ }
1124
+ }
1125
+ }, tableState);
1126
+
1127
+ applyTagClasses($table);
1128
+ rebuildResizeLayers();
1129
+ }),
1130
+ };
1131
+
1132
+
1133
+ ////// Cell selection
1134
+
1135
+ var getCellIndex = function (cellElement, rowState) {
1136
+ return rowState.findIndex(function (rowStateCell) {
1137
+ if (rowStateCell.element === undefined) {
1138
+ return false;
1139
+ }
1140
+
1141
+ return rowStateCell.element === cellElement;
1142
+ });
1143
+ };
1144
+
1145
+ var resetTableMouseHacks = function () {
1146
+ $('table', t.$ed).off('mousedown.tbwTable');
1147
+ $('table', t.$ed).on('mousedown.tbwTable', function (e) {
1148
+ // Cells drag and drop while changing cell selection
1149
+ t.doc.getSelection().removeAllRanges();
1150
+
1151
+ // Prevent Ctrl+Click on Firefox
1152
+ if (!e.ctrlKey) {
1153
+ return;
1154
+ }
1155
+
1156
+ e.preventDefault();
1157
+ });
1158
+ };
1159
+
1160
+ var tableCellSelectionModeClass = t.o.prefix + 'table-cell-selection-mode';
1161
+ var tableCellSelectedClass = t.o.prefix + 'table-cell-selected';
1162
+ setTimeout(function () { // Wait for init
1163
+ resetTableMouseHacks();
1164
+ t.$c.on('tbwchange.tbwTable', function () {
1165
+ resetTableMouseHacks();
1166
+ rebuildResizeLayers();
1167
+ });
1168
+
1169
+ rebuildResizeLayers();
1170
+
1171
+ $(t.doc).on('selectionchange.tbwTable', function () {
1172
+ tableSelectedCells = undefined;
1173
+
1174
+ var selection = t.doc.getSelection();
1175
+ var rangeCount = selection.rangeCount;
1176
+
1177
+ var anchorNode = selection.anchorNode;
1178
+ var focusNode = selection.focusNode;
1179
+
1180
+ // Firefox create one range by cell
1181
+ if (rangeCount > 1) {
1182
+ var firstRange = selection.getRangeAt(0);
1183
+ var lastRange = selection.getRangeAt(rangeCount - 1);
1184
+
1185
+ anchorNode = firstRange.startContainer.childNodes[firstRange.startOffset];
1186
+ focusNode = lastRange.startContainer.childNodes[lastRange.startOffset];
1187
+ }
1188
+
1189
+ var $anchorSelectedCell = $(anchorNode).closest('td, th');
1190
+ var $focusSelectedCell = $(focusNode).closest('td, th');
1191
+
1192
+ var $tableAnchor = $anchorSelectedCell.closest('table');
1193
+ var $tableFocus = $focusSelectedCell.closest('table');
1194
+
1195
+ $('[class="' + tableCellSelectedClass + '"]', t.$ed).removeAttr('class');
1196
+ $('.' + tableCellSelectedClass, t.$ed).removeClass(tableCellSelectedClass);
1197
+
1198
+ if (($tableAnchor.length === 0 && $tableFocus.length === 0) ||
1199
+ $tableAnchor[0] !== $tableFocus[0] ||
1200
+ $anchorSelectedCell[0] === $focusSelectedCell[0]
1201
+ ) {
1202
+ $('.' + tableCellSelectionModeClass, t.$ed).removeClass(tableCellSelectionModeClass);
1203
+ return;
1204
+ }
1205
+
1206
+ // Toggle table to selection mode
1207
+ $tableAnchor.addClass(tableCellSelectionModeClass);
1208
+
1209
+ // Get table state
1210
+ var tableState = getTableState($tableAnchor);
1211
+
1212
+ // Find cells to set as selected
1213
+ var $allRows = $('tr', $tableAnchor);
1214
+
1215
+ var $anchorSelectedRow = $anchorSelectedCell.closest('tr');
1216
+ var anchorSelectedRowIndex = $allRows.index($anchorSelectedRow);
1217
+ var $focusSelectedRow = $focusSelectedCell.closest('tr');
1218
+ var focusSelectedRowIndex = $allRows.index($focusSelectedRow);
1219
+
1220
+ var anchorSelectedCellIndex = getCellIndex($anchorSelectedCell[0], tableState[anchorSelectedRowIndex]);
1221
+ var focusSelectedCellIndex = getCellIndex($focusSelectedCell[0], tableState[focusSelectedRowIndex]);
1222
+
1223
+ var firstSelectedRowIndex = Math.min(anchorSelectedRowIndex, focusSelectedRowIndex);
1224
+ var lastSelectedRowIndex = Math.max(anchorSelectedRowIndex, focusSelectedRowIndex);
1225
+ var firstSelectedCellIndex = Math.min(anchorSelectedCellIndex, focusSelectedCellIndex);
1226
+ var lastSelectedCellIndex = Math.max(anchorSelectedCellIndex, focusSelectedCellIndex);
1227
+
1228
+ // Set cells as selected
1229
+ var selectedCellsCoordinates = [];
1230
+ $allRows.each(function (rowIndex, rowElement) {
1231
+ if (rowIndex < firstSelectedRowIndex || rowIndex > lastSelectedRowIndex) {
1232
+ return;
1233
+ }
1234
+
1235
+ $('td, th', rowElement).each(function (_, cellElement) {
1236
+ var cellIndex = getCellIndex(cellElement, tableState[rowIndex]);
1237
+ if (cellIndex < firstSelectedCellIndex || cellIndex > lastSelectedCellIndex) {
1238
+ return;
1239
+ }
1240
+
1241
+ selectedCellsCoordinates.push([rowIndex, cellIndex]);
1242
+ $(cellElement).addClass(tableCellSelectedClass);
1243
+ });
1244
+ });
1245
+
1246
+ tableSelectedCells = selectedCellsCoordinates;
1247
+ });
1248
+ });
1249
+
1250
+ var foreachSelectedCell = function (callback, tableState) {
1251
+ if (tableSelectedCells === undefined) {
1252
+ var $cell = $(t.doc.getSelection().anchorNode).closest('td, th');
1253
+ if ($cell.length === 0) {
1254
+ return;
1255
+ }
1256
+
1257
+ callback($cell);
1258
+ return;
439
1259
  }
1260
+
1261
+ $(tableSelectedCells).each(function (_, cellCoordinates) {
1262
+ var cellState = getCellState(tableState, cellCoordinates, false);
1263
+ if (cellState.mergedIn !== undefined) {
1264
+ return;
1265
+ }
1266
+
1267
+ callback($(cellState.element));
1268
+ });
440
1269
  };
441
1270
 
1271
+
1272
+ ////// Cell resize
1273
+
1274
+ var TRUMBOWYG_TABLE_HANDLE_FOR = 'trumbowyg-table-handle-for';
1275
+ var $targetTable;
1276
+ var targetTableState;
1277
+ var targetColumnIndex;
1278
+ var rebuildResizeLayers = trumbowygThrottle(function () {
1279
+ if (!t.o.plugins.table.allowHorizontalResize) {
1280
+ return;
1281
+ }
1282
+
1283
+ var $resizeLayers = $('.' + t.o.prefix + 'table-resize-layers', t.$edBox);
1284
+ var hasResizeLayers = $resizeLayers.length > 0;
1285
+ if (!hasResizeLayers) {
1286
+ $resizeLayers = $('<div/>', {
1287
+ class: t.o.prefix + 'table-resize-layers',
1288
+ }).appendTo(t.$edBox);
1289
+ }
1290
+
1291
+ // Reset and remove old handles
1292
+ $('.' + t.o.prefix + 'table-resize-vertical-handle', $resizeLayers).each(function (_, handle) {
1293
+ $(handle)
1294
+ .off()
1295
+ .remove();
1296
+ });
1297
+
1298
+ $('td, th', t.$ed).each(function (_cellIndex, cell) {
1299
+ // Vertical handles
1300
+ $('<div/>', {
1301
+ class: t.o.prefix + 'table-resize-vertical-handle',
1302
+ })
1303
+ .prop(TRUMBOWYG_TABLE_HANDLE_FOR, cell)
1304
+ .on('mousedown.tbwTable', function (e) {
1305
+ e.preventDefault();
1306
+ e.stopPropagation();
1307
+ var targetCell = $(e.target).prop(TRUMBOWYG_TABLE_HANDLE_FOR);
1308
+ $targetTable = $(targetCell).closest('table');
1309
+ targetTableState = getTableState($targetTable);
1310
+ var $allRows = $('tr', $targetTable);
1311
+ var $row = $(targetCell).closest('tr');
1312
+ var rowIndex = $allRows.index($row);
1313
+ var rowState = targetTableState[rowIndex];
1314
+ var columnIndex = getCellIndex(targetCell, rowState);
1315
+ var targetCellState = targetTableState[rowIndex][columnIndex];
1316
+ if (targetCellState.mergedIn !== undefined) {
1317
+ targetCellState = targetTableState[targetCellState.mergedIn[0]][targetCellState.mergedIn[1]];
1318
+ }
1319
+
1320
+ targetColumnIndex = columnIndex + targetCellState.colspan - 1;
1321
+
1322
+ ensureColgroupExists($targetTable, targetTableState);
1323
+ setColWidthInPixels($targetTable, targetTableState);
1324
+ redrawResizeLayers();
1325
+
1326
+ $targetTable.css({
1327
+ maxWidth: '',
1328
+ });
1329
+ })
1330
+ .appendTo($resizeLayers);
1331
+ });
1332
+ redrawResizeLayers();
1333
+
1334
+ // If resize layer was here
1335
+ // We do not need to add following events
1336
+ if (hasResizeLayers) {
1337
+ return;
1338
+ }
1339
+
1340
+ $(t.doc)
1341
+ .on('mousemove.tbwTable', function (e) {
1342
+ if (targetColumnIndex === undefined) {
1343
+ return;
1344
+ }
1345
+ e.preventDefault();
1346
+ e.stopPropagation();
1347
+
1348
+ var tableRect = $targetTable[0].getBoundingClientRect();
1349
+ var tableLeftInPixels = e.pageX - tableRect.left;
1350
+
1351
+ var cellState = findFirstCellAtIndex(targetTableState, targetColumnIndex);
1352
+
1353
+ var cellElement = cellState.element;
1354
+ var cellRect = cellElement.getBoundingClientRect();
1355
+ var cellLeftInPixels = cellRect.left - tableRect.left;
1356
+
1357
+ var cellWidthInPixels = tableLeftInPixels - cellLeftInPixels;
1358
+
1359
+ var colElement = $('col', $targetTable)[targetColumnIndex];
1360
+ $(colElement).css({
1361
+ width: cellWidthInPixels,
1362
+ });
1363
+
1364
+ redrawResizeLayers();
1365
+ })
1366
+ .on('mouseup.tbwTable', function (e) {
1367
+ if (targetColumnIndex === undefined) {
1368
+ return;
1369
+ }
1370
+ e.preventDefault();
1371
+ e.stopPropagation();
1372
+
1373
+ // Fix width
1374
+ ensureColgroupExists($targetTable, targetTableState);
1375
+ setColWidthInPercents($targetTable, targetTableState);
1376
+
1377
+ // Reset resize state
1378
+ $targetTable = undefined;
1379
+ targetTableState = undefined;
1380
+ targetColumnIndex = undefined;
1381
+
1382
+ // Update HTML
1383
+ t.syncCode();
1384
+ redrawResizeLayers();
1385
+ });
1386
+
1387
+ $(window)
1388
+ .on('resize.tbwTable', function () {
1389
+ redrawResizeLayers();
1390
+ });
1391
+ }, 100);
1392
+
1393
+ var ensureColgroupExists = function ($table, tableState) {
1394
+ var $colgroup = $('colgroup', $table);
1395
+ if ($colgroup.length === 0) {
1396
+ $colgroup = $('<colgroup/>').prependTo($table);
1397
+ }
1398
+
1399
+ var columnCount = tableState[0].length;
1400
+ var currentColCount = $('col', $colgroup).length;
1401
+ for (; currentColCount < columnCount; currentColCount += 1) {
1402
+ $('<col/>').appendTo($colgroup);
1403
+ }
1404
+ };
1405
+
1406
+ var findFirstCellAtIndex = function (tableState, cellIndex) {
1407
+ var cellState;
1408
+ var rowIndex = 0;
1409
+ do {
1410
+ cellState = tableState[rowIndex][cellIndex];
1411
+ rowIndex += 1;
1412
+ } while (cellState.element === undefined || cellState.colspan !== 1);
1413
+
1414
+ return cellState;
1415
+ };
1416
+
1417
+ var setColWidths = function ($table, tableState, isUnitPercent = false) {
1418
+ var $colgroup = $('colgroup', $table);
1419
+ var $cols = $('col', $colgroup);
1420
+ var tableWidth = Math.ceil($table[0].getBoundingClientRect().width);
1421
+ $table.css({
1422
+ maxWidth: tableWidth,
1423
+ });
1424
+
1425
+ var columnCount = tableState[0].length;
1426
+ var colWidths = [];
1427
+ for (var columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {
1428
+ var cellElement = findFirstCellAtIndex(tableState, columnIndex).element;
1429
+ var cellWidth = cellElement.getBoundingClientRect().width;
1430
+
1431
+ if (isUnitPercent) {
1432
+ cellWidth = ((cellWidth / tableWidth) * 100) + '%';
1433
+ }
1434
+
1435
+ colWidths[columnIndex] = cellWidth;
1436
+ }
1437
+
1438
+ for (var colIndex = 0; colIndex < columnCount; colIndex += 1) {
1439
+ $($cols[colIndex]).css({
1440
+ width: colWidths[colIndex],
1441
+ });
1442
+ }
1443
+ };
1444
+
1445
+ var setColWidthInPixels = function ($table, tableState) {
1446
+ setColWidths($table, tableState, false);
1447
+ };
1448
+
1449
+ var setColWidthInPercents = function ($table, tableState) {
1450
+ setColWidths($table, tableState, true);
1451
+ };
1452
+
1453
+ var redrawResizeLayers = trumbowygThrottle(function () {
1454
+ var $resizeLayers = $('.' + t.o.prefix + 'table-resize-layers', t.$edBox);
1455
+
1456
+ var resizeLayersBoundingClientRect = $resizeLayers[0].getBoundingClientRect();
1457
+ var resizeLayersTop = resizeLayersBoundingClientRect.top;
1458
+ var resizeLayersLeft = resizeLayersBoundingClientRect.left;
1459
+
1460
+ $('.' + t.o.prefix + 'table-resize-vertical-handle', $resizeLayers).each(function (_, cellHandle) {
1461
+ var $cellHandle = $(cellHandle);
1462
+ var cell = $cellHandle.prop(TRUMBOWYG_TABLE_HANDLE_FOR);
1463
+ var cellBoundingClientRect = cell.getBoundingClientRect();
1464
+ $cellHandle.css({
1465
+ top: cellBoundingClientRect.top - resizeLayersTop,
1466
+ left: cellBoundingClientRect.left - resizeLayersLeft + cellBoundingClientRect.width,
1467
+ height: cellBoundingClientRect.height,
1468
+ });
1469
+ });
1470
+ }, 20);
1471
+
1472
+
1473
+ ////// Vertical alignment
1474
+
1475
+ var tableVerticalAlign = function (alignPosition) {
1476
+ return tableButtonAction(function ($table, $focusedRow, node, tableState) {
1477
+ foreachSelectedCell(function ($cell) {
1478
+ $cell.css({
1479
+ verticalAlign: alignPosition,
1480
+ });
1481
+ }, tableState);
1482
+ });
1483
+ };
1484
+
1485
+ var verticalAlignTop = {
1486
+ title: t.lang.tableVerticalAlignTop,
1487
+ text: t.lang.tableVerticalAlignTop,
1488
+ ico: 'align-top',
1489
+
1490
+ fn: tableVerticalAlign('top'),
1491
+ };
1492
+
1493
+ var verticalAlignMiddle = {
1494
+ title: t.lang.tableVerticalAlignMiddle,
1495
+ text: t.lang.tableVerticalAlignMiddle,
1496
+ ico: 'align-middle',
1497
+
1498
+ fn: tableVerticalAlign('middle'),
1499
+ };
1500
+
1501
+ var verticalAlignBottom = {
1502
+ title: t.lang.tableVerticalAlignBottom,
1503
+ text: t.lang.tableVerticalAlignBottom,
1504
+ ico: 'align-bottom',
1505
+
1506
+ fn: tableVerticalAlign('bottom'),
1507
+ };
1508
+
1509
+
1510
+ ////// Cell Background color
1511
+
1512
+ var getColorDropdownClass = function (mustDisplayAsList) {
1513
+ return mustDisplayAsList ? t.o.prefix + 'dropdown--color-list' : '';
1514
+ };
1515
+
1516
+ var buildColorDropdown = function (name, colorList, mustDisplayAsList, allowCustomColor, callback) {
1517
+ var dropdown = [];
1518
+ var trumbowygTableOptions = t.o.plugins.table;
1519
+
1520
+ $.each(colorList, function (i, color) {
1521
+ var btn = name + color;
1522
+ var btnDef = {
1523
+ fn: callback('#' + color),
1524
+ hasIcon: false,
1525
+ text: t.lang['#' + color] || ('#' + color),
1526
+ style: 'background-color: #' + color + ';'
1527
+ };
1528
+
1529
+ t.addBtnDef(btn, btnDef);
1530
+ dropdown.push(btn);
1531
+ });
1532
+
1533
+ // Remove color
1534
+ var removeColorButtonName = 'remove' + ucFirst(name),
1535
+ removeColorBtnDef = {
1536
+ fn: callback(''),
1537
+ hasIcon: false,
1538
+ style: 'background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAG0lEQVQIW2NkQAAfEJMRmwBYhoGBYQtMBYoAADziAp0jtJTgAAAAAElFTkSuQmCC);'
1539
+ };
1540
+
1541
+ if (mustDisplayAsList) {
1542
+ removeColorBtnDef.style = '';
1543
+ }
1544
+
1545
+ t.addBtnDef(removeColorButtonName, removeColorBtnDef);
1546
+ dropdown.push(removeColorButtonName);
1547
+
1548
+ // Custom color
1549
+ if (trumbowygTableOptions.allowCustomBackgroundColor) {
1550
+ var freeColorBtnDef = {
1551
+ fn: function () {
1552
+ t.openModalInsert(t.lang.backgroundColor,
1553
+ {
1554
+ color: {
1555
+ label: 'backgroundColor',
1556
+ forceCss: true,
1557
+ type: 'color',
1558
+ value: '#FFFFFF'
1559
+ }
1560
+ },
1561
+ // callback
1562
+ function (values) {
1563
+ callback(values.color)();
1564
+ return true;
1565
+ }
1566
+ );
1567
+ },
1568
+ hasIcon: false,
1569
+ text: '#',
1570
+ // style adjust for displaying the text
1571
+ style: 'text-indent: 0; line-height: 20px; padding: 0 5px;'
1572
+ };
1573
+
1574
+ var freeColorButtonName = 'free' + ucFirst(name);
1575
+ t.addBtnDef(freeColorButtonName, freeColorBtnDef);
1576
+ dropdown.push(freeColorButtonName);
1577
+ }
1578
+
1579
+ return dropdown;
1580
+ };
1581
+ var applyBackgroundColorToSelectedCells = function (color) {
1582
+ return function () {
1583
+ var $table = $(t.doc.getSelection().anchorNode).closest('table', t.$ed[0]);
1584
+
1585
+ if ($table.length === 0) {
1586
+ return;
1587
+ }
1588
+
1589
+ var tableState = getTableState($table);
1590
+ foreachSelectedCell(function ($cell) {
1591
+ $cell.css({
1592
+ backgroundColor: color,
1593
+ });
1594
+ }, tableState);
1595
+
1596
+ simplifyCells($table);
1597
+ };
1598
+ };
1599
+ var cellBackgroundColorBtnDef = {
1600
+ dropdown: buildColorDropdown(
1601
+ 'tableCellBackgroundColor',
1602
+ t.o.plugins.table.backgroundColorList || t.o.plugins.table.colorList,
1603
+ t.o.plugins.table.displayBackgroundColorsAsList,
1604
+ t.o.plugins.table.allowCustomBackgroundColor,
1605
+ applyBackgroundColorToSelectedCells
1606
+ ),
1607
+ dropdownClass: getColorDropdownClass(t.o.plugins.table.displayBackgroundColorsAsList),
1608
+ };
1609
+
1610
+
1611
+ ////// Table border color
1612
+
1613
+ var applyBorderColor = function (color) {
1614
+ return function () {
1615
+ var $table = $(t.doc.getSelection().anchorNode).closest('table', t.$ed[0]);
1616
+
1617
+ if ($table.length === 0) {
1618
+ return;
1619
+ }
1620
+
1621
+ var border = {
1622
+ borderColor: color,
1623
+ };
1624
+ if (parseInt($table.css('border-width'), 10) === 0) {
1625
+ border.borderWidth = '2px';
1626
+ border.borderStyle = 'solid';
1627
+ }
1628
+
1629
+ if (color === '') {
1630
+ border.borderWidth = '';
1631
+ border.borderStyle = '';
1632
+ }
1633
+
1634
+ $table.css(border);
1635
+ };
1636
+ };
1637
+ var tableBorderColorBtnDef = {
1638
+ dropdown: buildColorDropdown(
1639
+ 'tableBorderColor',
1640
+ t.o.plugins.table.borderColorList || t.o.plugins.table.colorList,
1641
+ t.o.plugins.table.displayBorderColorsAsList,
1642
+ t.o.plugins.table.allowCustomBorderColor,
1643
+ applyBorderColor
1644
+ ),
1645
+ dropdownClass: getColorDropdownClass(t.o.plugins.table.displayBorderColorsAsList),
1646
+ };
1647
+
1648
+
1649
+
1650
+ ////// Register buttons
1651
+
442
1652
  t.addBtnDef('table', buildButtonDef);
1653
+
1654
+ t.addBtnDef('tableAddHeaderRow', addHeaderRow);
1655
+
443
1656
  t.addBtnDef('tableAddRowAbove', addRowAbove);
444
- t.addBtnDef('tableAddRow', addRow);
1657
+ t.addBtnDef('tableAddRow', addRowBelow);
1658
+
445
1659
  t.addBtnDef('tableAddColumnLeft', addColumnLeft);
446
- t.addBtnDef('tableAddColumn', addColumn);
1660
+ t.addBtnDef('tableAddColumn', addColumnRight);
1661
+
1662
+ t.addBtnDef('tableMergeCells', mergeCells);
1663
+ t.addBtnDef('tableUnmergeCells', unmergeCells);
1664
+
1665
+ t.addBtnDef('tableVerticalAlignTop', verticalAlignTop);
1666
+ t.addBtnDef('tableVerticalAlignMiddle', verticalAlignMiddle);
1667
+ t.addBtnDef('tableVerticalAlignBottom', verticalAlignBottom);
1668
+
1669
+ t.addBtnDef('tableCellBackgroundColor', cellBackgroundColorBtnDef);
1670
+ t.addBtnDef('tableBorderColor', tableBorderColorBtnDef);
1671
+
447
1672
  t.addBtnDef('tableDeleteRow', deleteRow);
448
1673
  t.addBtnDef('tableDeleteColumn', deleteColumn);
449
1674
  t.addBtnDef('tableDestroy', destroy);
450
- }
1675
+ },
1676
+ destroy: function (t) {
1677
+ $(window)
1678
+ .off('resize.tbwTable');
1679
+
1680
+ $(t.doc)
1681
+ .off('selectionchange.tbwTable')
1682
+ .off('mousemove.tbwTable')
1683
+ .off('mouseup.tbwTable');
1684
+
1685
+ t.$c
1686
+ .off('tbwchange.tbwTable');
1687
+
1688
+ $('table', t.$ed)
1689
+ .off('mousedown.tbwTable');
1690
+ },
1691
+ tagHandler: function (element, t) {
1692
+ var tags = [];
1693
+
1694
+ if (element.tagName === 'TABLE') {
1695
+ tags.push('table');
1696
+
1697
+ var elementBorderColor = element.style.borderColor;
1698
+ if (elementBorderColor !== '') {
1699
+ var borderColor = colorToHex(elementBorderColor);
1700
+ if (t.o.plugins.table.colorList.indexOf(borderColor) >= 0) {
1701
+ tags.push('tableBorderColor' + borderColor);
1702
+ } else {
1703
+ tags.push('freeTableBorderColor');
1704
+ }
1705
+ }
1706
+ }
1707
+
1708
+ if (!element.style) {
1709
+ return tags;
1710
+ }
1711
+
1712
+ var elementVerticalAlign = element.style.verticalAlign;
1713
+ if (elementVerticalAlign !== '') {
1714
+ tags.push('tableVerticalAlign' + ucFirst(elementVerticalAlign));
1715
+ }
1716
+
1717
+ var elementBackgroundColor = element.style.backgroundColor;
1718
+ if ((element.tagName === 'TH' || element.tagName === 'TD') && elementBackgroundColor !== '') {
1719
+ var backgroundColor = colorToHex(elementBackgroundColor);
1720
+ if (t.o.plugins.table.colorList.indexOf(backgroundColor) >= 0) {
1721
+ tags.push('tableCellBackgroundColor' + backgroundColor);
1722
+ } else {
1723
+ tags.push('freeTableCellBackgroundColor');
1724
+ }
1725
+ }
1726
+
1727
+ return tags;
1728
+ },
451
1729
  }
452
1730
  }
453
1731
  });