mirador_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (206) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +13 -0
  7. data/README.md +97 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/lib/mirador_rails.rb +16 -0
  12. data/lib/mirador_rails/version.rb +3 -0
  13. data/lib/mirador_rails/view_helpers.rb +30 -0
  14. data/mirador_rails.gemspec +30 -0
  15. data/vendor/assets/images/border_type_1.png +0 -0
  16. data/vendor/assets/images/border_type_2.png +0 -0
  17. data/vendor/assets/images/border_type_3.png +0 -0
  18. data/vendor/assets/images/debut_dark.png +0 -0
  19. data/vendor/assets/javascripts/mirador.js +1 -0
  20. data/vendor/assets/javascripts/mirador.min.js +88 -0
  21. data/vendor/assets/javascripts/mirador.min.js.map +1 -0
  22. data/vendor/assets/locales/ar/translation.json +42 -0
  23. data/vendor/assets/locales/de/translation.json +77 -0
  24. data/vendor/assets/locales/en/translation.json +82 -0
  25. data/vendor/assets/locales/es/translation.json +35 -0
  26. data/vendor/assets/locales/fr/translation.json +77 -0
  27. data/vendor/assets/locales/ga/translation.json +39 -0
  28. data/vendor/assets/locales/ja/translation.json +82 -0
  29. data/vendor/assets/locales/ko/translation.json +74 -0
  30. data/vendor/assets/locales/nl/translation.json +39 -0
  31. data/vendor/assets/locales/zh-CN/translation.json +74 -0
  32. data/vendor/assets/locales/zh-TW/translation.json +74 -0
  33. data/vendor/assets/locales/zh/translation.json +74 -0
  34. data/vendor/assets/plugins/plugins/advlist/plugin.js +97 -0
  35. data/vendor/assets/plugins/plugins/advlist/plugin.min.js +1 -0
  36. data/vendor/assets/plugins/plugins/anchor/plugin.js +45 -0
  37. data/vendor/assets/plugins/plugins/anchor/plugin.min.js +1 -0
  38. data/vendor/assets/plugins/plugins/autolink/plugin.js +194 -0
  39. data/vendor/assets/plugins/plugins/autolink/plugin.min.js +1 -0
  40. data/vendor/assets/plugins/plugins/autoresize/plugin.js +152 -0
  41. data/vendor/assets/plugins/plugins/autoresize/plugin.min.js +1 -0
  42. data/vendor/assets/plugins/plugins/autosave/plugin.js +165 -0
  43. data/vendor/assets/plugins/plugins/autosave/plugin.min.js +1 -0
  44. data/vendor/assets/plugins/plugins/bbcode/plugin.js +123 -0
  45. data/vendor/assets/plugins/plugins/bbcode/plugin.min.js +1 -0
  46. data/vendor/assets/plugins/plugins/charmap/plugin.js +370 -0
  47. data/vendor/assets/plugins/plugins/charmap/plugin.min.js +1 -0
  48. data/vendor/assets/plugins/plugins/code/plugin.js +60 -0
  49. data/vendor/assets/plugins/plugins/code/plugin.min.js +1 -0
  50. data/vendor/assets/plugins/plugins/colorpicker/plugin.js +112 -0
  51. data/vendor/assets/plugins/plugins/colorpicker/plugin.min.js +1 -0
  52. data/vendor/assets/plugins/plugins/compat3x/css/dialog.css +118 -0
  53. data/vendor/assets/plugins/plugins/compat3x/img/buttons.png +0 -0
  54. data/vendor/assets/plugins/plugins/compat3x/img/icons.gif +0 -0
  55. data/vendor/assets/plugins/plugins/compat3x/img/items.gif +0 -0
  56. data/vendor/assets/plugins/plugins/compat3x/img/menu_arrow.gif +0 -0
  57. data/vendor/assets/plugins/plugins/compat3x/img/menu_check.gif +0 -0
  58. data/vendor/assets/plugins/plugins/compat3x/img/progress.gif +0 -0
  59. data/vendor/assets/plugins/plugins/compat3x/img/tabs.gif +0 -0
  60. data/vendor/assets/plugins/plugins/compat3x/plugin.js +297 -0
  61. data/vendor/assets/plugins/plugins/compat3x/plugin.min.js +1 -0
  62. data/vendor/assets/plugins/plugins/compat3x/tiny_mce_popup.js +542 -0
  63. data/vendor/assets/plugins/plugins/compat3x/utils/editable_selects.js +70 -0
  64. data/vendor/assets/plugins/plugins/compat3x/utils/form_utils.js +210 -0
  65. data/vendor/assets/plugins/plugins/compat3x/utils/mctabs.js +164 -0
  66. data/vendor/assets/plugins/plugins/compat3x/utils/validate.js +252 -0
  67. data/vendor/assets/plugins/plugins/contextmenu/plugin.js +87 -0
  68. data/vendor/assets/plugins/plugins/contextmenu/plugin.min.js +1 -0
  69. data/vendor/assets/plugins/plugins/directionality/plugin.js +64 -0
  70. data/vendor/assets/plugins/plugins/directionality/plugin.min.js +1 -0
  71. data/vendor/assets/plugins/plugins/emoticons/img/smiley-cool.gif +0 -0
  72. data/vendor/assets/plugins/plugins/emoticons/img/smiley-cry.gif +0 -0
  73. data/vendor/assets/plugins/plugins/emoticons/img/smiley-embarassed.gif +0 -0
  74. data/vendor/assets/plugins/plugins/emoticons/img/smiley-foot-in-mouth.gif +0 -0
  75. data/vendor/assets/plugins/plugins/emoticons/img/smiley-frown.gif +0 -0
  76. data/vendor/assets/plugins/plugins/emoticons/img/smiley-innocent.gif +0 -0
  77. data/vendor/assets/plugins/plugins/emoticons/img/smiley-kiss.gif +0 -0
  78. data/vendor/assets/plugins/plugins/emoticons/img/smiley-laughing.gif +0 -0
  79. data/vendor/assets/plugins/plugins/emoticons/img/smiley-money-mouth.gif +0 -0
  80. data/vendor/assets/plugins/plugins/emoticons/img/smiley-sealed.gif +0 -0
  81. data/vendor/assets/plugins/plugins/emoticons/img/smiley-smile.gif +0 -0
  82. data/vendor/assets/plugins/plugins/emoticons/img/smiley-surprised.gif +0 -0
  83. data/vendor/assets/plugins/plugins/emoticons/img/smiley-tongue-out.gif +0 -0
  84. data/vendor/assets/plugins/plugins/emoticons/img/smiley-undecided.gif +0 -0
  85. data/vendor/assets/plugins/plugins/emoticons/img/smiley-wink.gif +0 -0
  86. data/vendor/assets/plugins/plugins/emoticons/img/smiley-yell.gif +0 -0
  87. data/vendor/assets/plugins/plugins/emoticons/plugin.js +65 -0
  88. data/vendor/assets/plugins/plugins/emoticons/plugin.min.js +1 -0
  89. data/vendor/assets/plugins/plugins/example/dialog.html +8 -0
  90. data/vendor/assets/plugins/plugins/example/plugin.js +68 -0
  91. data/vendor/assets/plugins/plugins/example/plugin.min.js +1 -0
  92. data/vendor/assets/plugins/plugins/example_dependency/plugin.js +22 -0
  93. data/vendor/assets/plugins/plugins/example_dependency/plugin.min.js +1 -0
  94. data/vendor/assets/plugins/plugins/fullpage/plugin.js +490 -0
  95. data/vendor/assets/plugins/plugins/fullpage/plugin.min.js +1 -0
  96. data/vendor/assets/plugins/plugins/fullscreen/plugin.js +136 -0
  97. data/vendor/assets/plugins/plugins/fullscreen/plugin.min.js +1 -0
  98. data/vendor/assets/plugins/plugins/hr/plugin.js +30 -0
  99. data/vendor/assets/plugins/plugins/hr/plugin.min.js +1 -0
  100. data/vendor/assets/plugins/plugins/image/plugin.js +439 -0
  101. data/vendor/assets/plugins/plugins/image/plugin.min.js +1 -0
  102. data/vendor/assets/plugins/plugins/importcss/plugin.js +195 -0
  103. data/vendor/assets/plugins/plugins/importcss/plugin.min.js +1 -0
  104. data/vendor/assets/plugins/plugins/insertdatetime/plugin.js +121 -0
  105. data/vendor/assets/plugins/plugins/insertdatetime/plugin.min.js +1 -0
  106. data/vendor/assets/plugins/plugins/layer/plugin.js +225 -0
  107. data/vendor/assets/plugins/plugins/layer/plugin.min.js +1 -0
  108. data/vendor/assets/plugins/plugins/legacyoutput/plugin.js +211 -0
  109. data/vendor/assets/plugins/plugins/legacyoutput/plugin.min.js +1 -0
  110. data/vendor/assets/plugins/plugins/link/plugin.js +400 -0
  111. data/vendor/assets/plugins/plugins/link/plugin.min.js +1 -0
  112. data/vendor/assets/plugins/plugins/lists/plugin.js +791 -0
  113. data/vendor/assets/plugins/plugins/lists/plugin.min.js +1 -0
  114. data/vendor/assets/plugins/plugins/media/moxieplayer.swf +0 -0
  115. data/vendor/assets/plugins/plugins/media/plugin.js +774 -0
  116. data/vendor/assets/plugins/plugins/media/plugin.min.js +1 -0
  117. data/vendor/assets/plugins/plugins/nonbreaking/plugin.js +53 -0
  118. data/vendor/assets/plugins/plugins/nonbreaking/plugin.min.js +1 -0
  119. data/vendor/assets/plugins/plugins/noneditable/plugin.js +540 -0
  120. data/vendor/assets/plugins/plugins/noneditable/plugin.min.js +1 -0
  121. data/vendor/assets/plugins/plugins/pagebreak/plugin.js +88 -0
  122. data/vendor/assets/plugins/plugins/pagebreak/plugin.min.js +1 -0
  123. data/vendor/assets/plugins/plugins/paste/classes/Clipboard.js +634 -0
  124. data/vendor/assets/plugins/plugins/paste/classes/Plugin.js +110 -0
  125. data/vendor/assets/plugins/plugins/paste/classes/Quirks.js +159 -0
  126. data/vendor/assets/plugins/plugins/paste/classes/Utils.js +130 -0
  127. data/vendor/assets/plugins/plugins/paste/classes/WordFilter.js +493 -0
  128. data/vendor/assets/plugins/plugins/paste/plugin.dev.js +120 -0
  129. data/vendor/assets/plugins/plugins/paste/plugin.js +1625 -0
  130. data/vendor/assets/plugins/plugins/paste/plugin.min.js +1 -0
  131. data/vendor/assets/plugins/plugins/preview/plugin.js +88 -0
  132. data/vendor/assets/plugins/plugins/preview/plugin.min.js +1 -0
  133. data/vendor/assets/plugins/plugins/print/plugin.js +32 -0
  134. data/vendor/assets/plugins/plugins/print/plugin.min.js +1 -0
  135. data/vendor/assets/plugins/plugins/save/plugin.js +94 -0
  136. data/vendor/assets/plugins/plugins/save/plugin.min.js +1 -0
  137. data/vendor/assets/plugins/plugins/searchreplace/plugin.js +594 -0
  138. data/vendor/assets/plugins/plugins/searchreplace/plugin.min.js +1 -0
  139. data/vendor/assets/plugins/plugins/spellchecker/classes/DomTextMatcher.js +470 -0
  140. data/vendor/assets/plugins/plugins/spellchecker/classes/Plugin.js +436 -0
  141. data/vendor/assets/plugins/plugins/spellchecker/plugin.dev.js +117 -0
  142. data/vendor/assets/plugins/plugins/spellchecker/plugin.js +996 -0
  143. data/vendor/assets/plugins/plugins/spellchecker/plugin.min.js +1 -0
  144. data/vendor/assets/plugins/plugins/tabfocus/plugin.js +120 -0
  145. data/vendor/assets/plugins/plugins/tabfocus/plugin.min.js +1 -0
  146. data/vendor/assets/plugins/plugins/table/classes/CellSelection.js +176 -0
  147. data/vendor/assets/plugins/plugins/table/classes/Dialogs.js +749 -0
  148. data/vendor/assets/plugins/plugins/table/classes/Plugin.js +422 -0
  149. data/vendor/assets/plugins/plugins/table/classes/Quirks.js +372 -0
  150. data/vendor/assets/plugins/plugins/table/classes/TableGrid.js +864 -0
  151. data/vendor/assets/plugins/plugins/table/plugin.dev.js +118 -0
  152. data/vendor/assets/plugins/plugins/table/plugin.js +2680 -0
  153. data/vendor/assets/plugins/plugins/table/plugin.min.js +1 -0
  154. data/vendor/assets/plugins/plugins/template/plugin.js +262 -0
  155. data/vendor/assets/plugins/plugins/template/plugin.min.js +1 -0
  156. data/vendor/assets/plugins/plugins/textcolor/plugin.js +272 -0
  157. data/vendor/assets/plugins/plugins/textcolor/plugin.min.js +1 -0
  158. data/vendor/assets/plugins/plugins/textpattern/plugin.js +268 -0
  159. data/vendor/assets/plugins/plugins/textpattern/plugin.min.js +1 -0
  160. data/vendor/assets/plugins/plugins/visualblocks/css/visualblocks.css +135 -0
  161. data/vendor/assets/plugins/plugins/visualblocks/img/address.gif +0 -0
  162. data/vendor/assets/plugins/plugins/visualblocks/img/article.gif +0 -0
  163. data/vendor/assets/plugins/plugins/visualblocks/img/aside.gif +0 -0
  164. data/vendor/assets/plugins/plugins/visualblocks/img/blockquote.gif +0 -0
  165. data/vendor/assets/plugins/plugins/visualblocks/img/div.gif +0 -0
  166. data/vendor/assets/plugins/plugins/visualblocks/img/dl.gif +0 -0
  167. data/vendor/assets/plugins/plugins/visualblocks/img/figure.gif +0 -0
  168. data/vendor/assets/plugins/plugins/visualblocks/img/h1.gif +0 -0
  169. data/vendor/assets/plugins/plugins/visualblocks/img/h2.gif +0 -0
  170. data/vendor/assets/plugins/plugins/visualblocks/img/h3.gif +0 -0
  171. data/vendor/assets/plugins/plugins/visualblocks/img/h4.gif +0 -0
  172. data/vendor/assets/plugins/plugins/visualblocks/img/h5.gif +0 -0
  173. data/vendor/assets/plugins/plugins/visualblocks/img/h6.gif +0 -0
  174. data/vendor/assets/plugins/plugins/visualblocks/img/hgroup.gif +0 -0
  175. data/vendor/assets/plugins/plugins/visualblocks/img/ol.gif +0 -0
  176. data/vendor/assets/plugins/plugins/visualblocks/img/p.gif +0 -0
  177. data/vendor/assets/plugins/plugins/visualblocks/img/pre.gif +0 -0
  178. data/vendor/assets/plugins/plugins/visualblocks/img/section.gif +0 -0
  179. data/vendor/assets/plugins/plugins/visualblocks/img/ul.gif +0 -0
  180. data/vendor/assets/plugins/plugins/visualblocks/plugin.js +86 -0
  181. data/vendor/assets/plugins/plugins/visualblocks/plugin.min.js +1 -0
  182. data/vendor/assets/plugins/plugins/visualchars/plugin.js +88 -0
  183. data/vendor/assets/plugins/plugins/visualchars/plugin.min.js +1 -0
  184. data/vendor/assets/plugins/plugins/wordcount/plugin.js +69 -0
  185. data/vendor/assets/plugins/plugins/wordcount/plugin.min.js +1 -0
  186. data/vendor/assets/skins/skins/lightgray/content.inline.min.css +1 -0
  187. data/vendor/assets/skins/skins/lightgray/content.min.css +1 -0
  188. data/vendor/assets/skins/skins/lightgray/fonts/tinymce-small.eot +0 -0
  189. data/vendor/assets/skins/skins/lightgray/fonts/tinymce-small.svg +62 -0
  190. data/vendor/assets/skins/skins/lightgray/fonts/tinymce-small.ttf +0 -0
  191. data/vendor/assets/skins/skins/lightgray/fonts/tinymce-small.woff +0 -0
  192. data/vendor/assets/skins/skins/lightgray/fonts/tinymce.eot +0 -0
  193. data/vendor/assets/skins/skins/lightgray/fonts/tinymce.svg +63 -0
  194. data/vendor/assets/skins/skins/lightgray/fonts/tinymce.ttf +0 -0
  195. data/vendor/assets/skins/skins/lightgray/fonts/tinymce.woff +0 -0
  196. data/vendor/assets/skins/skins/lightgray/img/anchor.gif +0 -0
  197. data/vendor/assets/skins/skins/lightgray/img/loader.gif +0 -0
  198. data/vendor/assets/skins/skins/lightgray/img/object.gif +0 -0
  199. data/vendor/assets/skins/skins/lightgray/img/trans.gif +0 -0
  200. data/vendor/assets/skins/skins/lightgray/skin.ie7.min.css +1 -0
  201. data/vendor/assets/skins/skins/lightgray/skin.min.css +1 -0
  202. data/vendor/assets/stylesheets/mirador-combined.css +3969 -0
  203. data/vendor/assets/stylesheets/mirador.css +11 -0
  204. data/vendor/assets/themes/themes/modern/theme.js +617 -0
  205. data/vendor/assets/themes/themes/modern/theme.min.js +1 -0
  206. metadata +318 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Inline development version. Only to be used while developing since it uses document.write to load scripts.
3
+ */
4
+
5
+ /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
6
+ /*globals $code */
7
+
8
+ (function(exports) {
9
+ "use strict";
10
+
11
+ var html = "", baseDir;
12
+ var modules = {}, exposedModules = [], moduleCount = 0;
13
+
14
+ var scripts = document.getElementsByTagName('script');
15
+ for (var i = 0; i < scripts.length; i++) {
16
+ var src = scripts[i].src;
17
+
18
+ if (src.indexOf('/plugin.dev.js') != -1) {
19
+ baseDir = src.substring(0, src.lastIndexOf('/'));
20
+ }
21
+ }
22
+
23
+ function require(ids, callback) {
24
+ var module, defs = [];
25
+
26
+ for (var i = 0; i < ids.length; ++i) {
27
+ module = modules[ids[i]] || resolve(ids[i]);
28
+ if (!module) {
29
+ throw 'module definition dependecy not found: ' + ids[i];
30
+ }
31
+
32
+ defs.push(module);
33
+ }
34
+
35
+ callback.apply(null, defs);
36
+ }
37
+
38
+ function resolve(id) {
39
+ var target = exports;
40
+ var fragments = id.split(/[.\/]/);
41
+
42
+ for (var fi = 0; fi < fragments.length; ++fi) {
43
+ if (!target[fragments[fi]]) {
44
+ return;
45
+ }
46
+
47
+ target = target[fragments[fi]];
48
+ }
49
+
50
+ return target;
51
+ }
52
+
53
+ function register(id) {
54
+ var target = exports;
55
+ var fragments = id.split(/[.\/]/);
56
+
57
+ for (var fi = 0; fi < fragments.length - 1; ++fi) {
58
+ if (target[fragments[fi]] === undefined) {
59
+ target[fragments[fi]] = {};
60
+ }
61
+
62
+ target = target[fragments[fi]];
63
+ }
64
+
65
+ target[fragments[fragments.length - 1]] = modules[id];
66
+ }
67
+
68
+ function define(id, dependencies, definition) {
69
+ if (typeof id !== 'string') {
70
+ throw 'invalid module definition, module id must be defined and be a string';
71
+ }
72
+
73
+ if (dependencies === undefined) {
74
+ throw 'invalid module definition, dependencies must be specified';
75
+ }
76
+
77
+ if (definition === undefined) {
78
+ throw 'invalid module definition, definition function must be specified';
79
+ }
80
+
81
+ require(dependencies, function() {
82
+ modules[id] = definition.apply(null, arguments);
83
+ });
84
+
85
+ if (--moduleCount === 0) {
86
+ for (var i = 0; i < exposedModules.length; i++) {
87
+ register(exposedModules[i]);
88
+ }
89
+ }
90
+ }
91
+
92
+ function expose(ids) {
93
+ exposedModules = ids;
94
+ }
95
+
96
+ function writeScripts() {
97
+ document.write(html);
98
+ }
99
+
100
+ function load(path) {
101
+ html += '<script type="text/javascript" src="' + baseDir + '/' + path + '"></script>\n';
102
+ moduleCount++;
103
+ }
104
+
105
+ // Expose globally
106
+ exports.define = define;
107
+ exports.require = require;
108
+
109
+ load('classes/TableGrid.js');
110
+ load('classes/Quirks.js');
111
+ load('classes/CellSelection.js');
112
+ load('classes/Dialogs.js');
113
+ load('classes/Plugin.js');
114
+
115
+ writeScripts();
116
+ })(this);
117
+
118
+ // $hash: a13998323851e68e073e373581ee90e4
@@ -0,0 +1,2680 @@
1
+ /**
2
+ * Compiled inline version. (Library mode)
3
+ */
4
+
5
+ /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
6
+ /*globals $code */
7
+
8
+ (function(exports, undefined) {
9
+ "use strict";
10
+
11
+ var modules = {};
12
+
13
+ function require(ids, callback) {
14
+ var module, defs = [];
15
+
16
+ for (var i = 0; i < ids.length; ++i) {
17
+ module = modules[ids[i]] || resolve(ids[i]);
18
+ if (!module) {
19
+ throw 'module definition dependecy not found: ' + ids[i];
20
+ }
21
+
22
+ defs.push(module);
23
+ }
24
+
25
+ callback.apply(null, defs);
26
+ }
27
+
28
+ function define(id, dependencies, definition) {
29
+ if (typeof id !== 'string') {
30
+ throw 'invalid module definition, module id must be defined and be a string';
31
+ }
32
+
33
+ if (dependencies === undefined) {
34
+ throw 'invalid module definition, dependencies must be specified';
35
+ }
36
+
37
+ if (definition === undefined) {
38
+ throw 'invalid module definition, definition function must be specified';
39
+ }
40
+
41
+ require(dependencies, function() {
42
+ modules[id] = definition.apply(null, arguments);
43
+ });
44
+ }
45
+
46
+ function defined(id) {
47
+ return !!modules[id];
48
+ }
49
+
50
+ function resolve(id) {
51
+ var target = exports;
52
+ var fragments = id.split(/[.\/]/);
53
+
54
+ for (var fi = 0; fi < fragments.length; ++fi) {
55
+ if (!target[fragments[fi]]) {
56
+ return;
57
+ }
58
+
59
+ target = target[fragments[fi]];
60
+ }
61
+
62
+ return target;
63
+ }
64
+
65
+ function expose(ids) {
66
+ for (var i = 0; i < ids.length; i++) {
67
+ var target = exports;
68
+ var id = ids[i];
69
+ var fragments = id.split(/[.\/]/);
70
+
71
+ for (var fi = 0; fi < fragments.length - 1; ++fi) {
72
+ if (target[fragments[fi]] === undefined) {
73
+ target[fragments[fi]] = {};
74
+ }
75
+
76
+ target = target[fragments[fi]];
77
+ }
78
+
79
+ target[fragments[fragments.length - 1]] = modules[id];
80
+ }
81
+ }
82
+
83
+ // Included from: js/tinymce/plugins/table/classes/TableGrid.js
84
+
85
+ /**
86
+ * TableGrid.js
87
+ *
88
+ * Copyright, Moxiecode Systems AB
89
+ * Released under LGPL License.
90
+ *
91
+ * License: http://www.tinymce.com/license
92
+ * Contributing: http://www.tinymce.com/contributing
93
+ */
94
+
95
+ /**
96
+ * This class creates a grid out of a table element. This
97
+ * makes it a whole lot easier to handle complex tables with
98
+ * col/row spans.
99
+ *
100
+ * @class tinymce.tableplugin.TableGrid
101
+ * @private
102
+ */
103
+ define("tinymce/tableplugin/TableGrid", [
104
+ "tinymce/util/Tools",
105
+ "tinymce/Env"
106
+ ], function(Tools, Env) {
107
+ var each = Tools.each;
108
+
109
+ function getSpanVal(td, name) {
110
+ return parseInt(td.getAttribute(name) || 1, 10);
111
+ }
112
+
113
+ return function(editor, table) {
114
+ var grid, gridWidth, startPos, endPos, selectedCell, selection = editor.selection, dom = selection.dom;
115
+
116
+ function buildGrid() {
117
+ var startY = 0;
118
+
119
+ grid = [];
120
+ gridWidth = 0;
121
+
122
+ each(['thead', 'tbody', 'tfoot'], function(part) {
123
+ var rows = dom.select('> ' + part + ' tr', table);
124
+
125
+ each(rows, function(tr, y) {
126
+ y += startY;
127
+
128
+ each(dom.select('> td, > th', tr), function(td, x) {
129
+ var x2, y2, rowspan, colspan;
130
+
131
+ // Skip over existing cells produced by rowspan
132
+ if (grid[y]) {
133
+ while (grid[y][x]) {
134
+ x++;
135
+ }
136
+ }
137
+
138
+ // Get col/rowspan from cell
139
+ rowspan = getSpanVal(td, 'rowspan');
140
+ colspan = getSpanVal(td, 'colspan');
141
+
142
+ // Fill out rowspan/colspan right and down
143
+ for (y2 = y; y2 < y + rowspan; y2++) {
144
+ if (!grid[y2]) {
145
+ grid[y2] = [];
146
+ }
147
+
148
+ for (x2 = x; x2 < x + colspan; x2++) {
149
+ grid[y2][x2] = {
150
+ part: part,
151
+ real: y2 == y && x2 == x,
152
+ elm: td,
153
+ rowspan: rowspan,
154
+ colspan: colspan
155
+ };
156
+ }
157
+ }
158
+
159
+ gridWidth = Math.max(gridWidth, x + 1);
160
+ });
161
+ });
162
+
163
+ startY += rows.length;
164
+ });
165
+ }
166
+
167
+ function cloneNode(node, children) {
168
+ node = node.cloneNode(children);
169
+ node.removeAttribute('id');
170
+
171
+ return node;
172
+ }
173
+
174
+ function getCell(x, y) {
175
+ var row;
176
+
177
+ row = grid[y];
178
+ if (row) {
179
+ return row[x];
180
+ }
181
+ }
182
+
183
+ function setSpanVal(td, name, val) {
184
+ if (td) {
185
+ val = parseInt(val, 10);
186
+
187
+ if (val === 1) {
188
+ td.removeAttribute(name, 1);
189
+ } else {
190
+ td.setAttribute(name, val, 1);
191
+ }
192
+ }
193
+ }
194
+
195
+ function isCellSelected(cell) {
196
+ return cell && (dom.hasClass(cell.elm, 'mce-item-selected') || cell == selectedCell);
197
+ }
198
+
199
+ function getSelectedRows() {
200
+ var rows = [];
201
+
202
+ each(table.rows, function(row) {
203
+ each(row.cells, function(cell) {
204
+ if (dom.hasClass(cell, 'mce-item-selected') || (selectedCell && cell == selectedCell.elm)) {
205
+ rows.push(row);
206
+ return false;
207
+ }
208
+ });
209
+ });
210
+
211
+ return rows;
212
+ }
213
+
214
+ function deleteTable() {
215
+ var rng = dom.createRng();
216
+
217
+ rng.setStartAfter(table);
218
+ rng.setEndAfter(table);
219
+
220
+ selection.setRng(rng);
221
+
222
+ dom.remove(table);
223
+ }
224
+
225
+ function cloneCell(cell) {
226
+ var formatNode, cloneFormats = {};
227
+
228
+ if (editor.settings.table_clone_elements !== false) {
229
+ cloneFormats = Tools.makeMap(
230
+ (editor.settings.table_clone_elements || 'strong em b i span font h1 h2 h3 h4 h5 h6 p div').toUpperCase(),
231
+ /[ ,]/
232
+ );
233
+ }
234
+
235
+ // Clone formats
236
+ Tools.walk(cell, function(node) {
237
+ var curNode;
238
+
239
+ if (node.nodeType == 3) {
240
+ each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {
241
+ if (!cloneFormats[node.nodeName]) {
242
+ return;
243
+ }
244
+
245
+ node = cloneNode(node, false);
246
+
247
+ if (!formatNode) {
248
+ formatNode = curNode = node;
249
+ } else if (curNode) {
250
+ curNode.appendChild(node);
251
+ }
252
+
253
+ curNode = node;
254
+ });
255
+
256
+ // Add something to the inner node
257
+ if (curNode) {
258
+ curNode.innerHTML = Env.ie ? '&nbsp;' : '<br data-mce-bogus="1" />';
259
+ }
260
+
261
+ return false;
262
+ }
263
+ }, 'childNodes');
264
+
265
+ cell = cloneNode(cell, false);
266
+ setSpanVal(cell, 'rowSpan', 1);
267
+ setSpanVal(cell, 'colSpan', 1);
268
+
269
+ if (formatNode) {
270
+ cell.appendChild(formatNode);
271
+ } else {
272
+ if (!Env.ie || Env.ie > 10) {
273
+ cell.innerHTML = '<br data-mce-bogus="1" />';
274
+ }
275
+ }
276
+
277
+ return cell;
278
+ }
279
+
280
+ function cleanup() {
281
+ var rng = dom.createRng(), row;
282
+
283
+ // Empty rows
284
+ each(dom.select('tr', table), function(tr) {
285
+ if (tr.cells.length === 0) {
286
+ dom.remove(tr);
287
+ }
288
+ });
289
+
290
+ // Empty table
291
+ if (dom.select('tr', table).length === 0) {
292
+ rng.setStartBefore(table);
293
+ rng.setEndBefore(table);
294
+ selection.setRng(rng);
295
+ dom.remove(table);
296
+ return;
297
+ }
298
+
299
+ // Empty header/body/footer
300
+ each(dom.select('thead,tbody,tfoot', table), function(part) {
301
+ if (part.rows.length === 0) {
302
+ dom.remove(part);
303
+ }
304
+ });
305
+
306
+ // Restore selection to start position if it still exists
307
+ buildGrid();
308
+
309
+ // If we have a valid startPos object
310
+ if (startPos) {
311
+ // Restore the selection to the closest table position
312
+ row = grid[Math.min(grid.length - 1, startPos.y)];
313
+ if (row) {
314
+ selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true);
315
+ selection.collapse(true);
316
+ }
317
+ }
318
+ }
319
+
320
+ function fillLeftDown(x, y, rows, cols) {
321
+ var tr, x2, r, c, cell;
322
+
323
+ tr = grid[y][x].elm.parentNode;
324
+ for (r = 1; r <= rows; r++) {
325
+ tr = dom.getNext(tr, 'tr');
326
+
327
+ if (tr) {
328
+ // Loop left to find real cell
329
+ for (x2 = x; x2 >= 0; x2--) {
330
+ cell = grid[y + r][x2].elm;
331
+
332
+ if (cell.parentNode == tr) {
333
+ // Append clones after
334
+ for (c = 1; c <= cols; c++) {
335
+ dom.insertAfter(cloneCell(cell), cell);
336
+ }
337
+
338
+ break;
339
+ }
340
+ }
341
+
342
+ if (x2 == -1) {
343
+ // Insert nodes before first cell
344
+ for (c = 1; c <= cols; c++) {
345
+ tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]);
346
+ }
347
+ }
348
+ }
349
+ }
350
+ }
351
+
352
+ function split() {
353
+ each(grid, function(row, y) {
354
+ each(row, function(cell, x) {
355
+ var colSpan, rowSpan, i;
356
+
357
+ if (isCellSelected(cell)) {
358
+ cell = cell.elm;
359
+ colSpan = getSpanVal(cell, 'colspan');
360
+ rowSpan = getSpanVal(cell, 'rowspan');
361
+
362
+ if (colSpan > 1 || rowSpan > 1) {
363
+ setSpanVal(cell, 'rowSpan', 1);
364
+ setSpanVal(cell, 'colSpan', 1);
365
+
366
+ // Insert cells right
367
+ for (i = 0; i < colSpan - 1; i++) {
368
+ dom.insertAfter(cloneCell(cell), cell);
369
+ }
370
+
371
+ fillLeftDown(x, y, rowSpan - 1, colSpan);
372
+ }
373
+ }
374
+ });
375
+ });
376
+ }
377
+
378
+ function merge(cell, cols, rows) {
379
+ var pos, startX, startY, endX, endY, x, y, startCell, endCell, children, count;
380
+
381
+ // Use specified cell and cols/rows
382
+ if (cell) {
383
+ pos = getPos(cell);
384
+ startX = pos.x;
385
+ startY = pos.y;
386
+ endX = startX + (cols - 1);
387
+ endY = startY + (rows - 1);
388
+ } else {
389
+ startPos = endPos = null;
390
+
391
+ // Calculate start/end pos by checking for selected cells in grid works better with context menu
392
+ each(grid, function(row, y) {
393
+ each(row, function(cell, x) {
394
+ if (isCellSelected(cell)) {
395
+ if (!startPos) {
396
+ startPos = {x: x, y: y};
397
+ }
398
+
399
+ endPos = {x: x, y: y};
400
+ }
401
+ });
402
+ });
403
+
404
+ // Use selection, but make sure startPos is valid before accessing
405
+ if (startPos) {
406
+ startX = startPos.x;
407
+ startY = startPos.y;
408
+ endX = endPos.x;
409
+ endY = endPos.y;
410
+ }
411
+ }
412
+
413
+ // Find start/end cells
414
+ startCell = getCell(startX, startY);
415
+ endCell = getCell(endX, endY);
416
+
417
+ // Check if the cells exists and if they are of the same part for example tbody = tbody
418
+ if (startCell && endCell && startCell.part == endCell.part) {
419
+ // Split and rebuild grid
420
+ split();
421
+ buildGrid();
422
+
423
+ // Set row/col span to start cell
424
+ startCell = getCell(startX, startY).elm;
425
+ setSpanVal(startCell, 'colSpan', (endX - startX) + 1);
426
+ setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);
427
+
428
+ // Remove other cells and add it's contents to the start cell
429
+ for (y = startY; y <= endY; y++) {
430
+ for (x = startX; x <= endX; x++) {
431
+ if (!grid[y] || !grid[y][x]) {
432
+ continue;
433
+ }
434
+
435
+ cell = grid[y][x].elm;
436
+
437
+ /*jshint loopfunc:true */
438
+ /*eslint no-loop-func:0 */
439
+ if (cell != startCell) {
440
+ // Move children to startCell
441
+ children = Tools.grep(cell.childNodes);
442
+ each(children, function(node) {
443
+ startCell.appendChild(node);
444
+ });
445
+
446
+ // Remove bogus nodes if there is children in the target cell
447
+ if (children.length) {
448
+ children = Tools.grep(startCell.childNodes);
449
+ count = 0;
450
+ each(children, function(node) {
451
+ if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1) {
452
+ startCell.removeChild(node);
453
+ }
454
+ });
455
+ }
456
+
457
+ dom.remove(cell);
458
+ }
459
+ }
460
+ }
461
+
462
+ // Remove empty rows etc and restore caret location
463
+ cleanup();
464
+ }
465
+ }
466
+
467
+ function insertRow(before) {
468
+ var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;
469
+
470
+ // Find first/last row
471
+ each(grid, function(row, y) {
472
+ each(row, function(cell) {
473
+ if (isCellSelected(cell)) {
474
+ cell = cell.elm;
475
+ rowElm = cell.parentNode;
476
+ newRow = cloneNode(rowElm, false);
477
+ posY = y;
478
+
479
+ if (before) {
480
+ return false;
481
+ }
482
+ }
483
+ });
484
+
485
+ if (before) {
486
+ return !posY;
487
+ }
488
+ });
489
+
490
+ // If posY is undefined there is nothing for us to do here...just return to avoid crashing below
491
+ if (posY === undefined) {
492
+ return;
493
+ }
494
+
495
+ for (x = 0; x < grid[0].length; x++) {
496
+ // Cell not found could be because of an invalid table structure
497
+ if (!grid[posY][x]) {
498
+ continue;
499
+ }
500
+
501
+ cell = grid[posY][x].elm;
502
+
503
+ if (cell != lastCell) {
504
+ if (!before) {
505
+ rowSpan = getSpanVal(cell, 'rowspan');
506
+ if (rowSpan > 1) {
507
+ setSpanVal(cell, 'rowSpan', rowSpan + 1);
508
+ continue;
509
+ }
510
+ } else {
511
+ // Check if cell above can be expanded
512
+ if (posY > 0 && grid[posY - 1][x]) {
513
+ otherCell = grid[posY - 1][x].elm;
514
+ rowSpan = getSpanVal(otherCell, 'rowSpan');
515
+ if (rowSpan > 1) {
516
+ setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
517
+ continue;
518
+ }
519
+ }
520
+ }
521
+
522
+ // Insert new cell into new row
523
+ newCell = cloneCell(cell);
524
+ setSpanVal(newCell, 'colSpan', cell.colSpan);
525
+
526
+ newRow.appendChild(newCell);
527
+
528
+ lastCell = cell;
529
+ }
530
+ }
531
+
532
+ if (newRow.hasChildNodes()) {
533
+ if (!before) {
534
+ dom.insertAfter(newRow, rowElm);
535
+ } else {
536
+ rowElm.parentNode.insertBefore(newRow, rowElm);
537
+ }
538
+ }
539
+ }
540
+
541
+ function insertCol(before) {
542
+ var posX, lastCell;
543
+
544
+ // Find first/last column
545
+ each(grid, function(row) {
546
+ each(row, function(cell, x) {
547
+ if (isCellSelected(cell)) {
548
+ posX = x;
549
+
550
+ if (before) {
551
+ return false;
552
+ }
553
+ }
554
+ });
555
+
556
+ if (before) {
557
+ return !posX;
558
+ }
559
+ });
560
+
561
+ each(grid, function(row, y) {
562
+ var cell, rowSpan, colSpan;
563
+
564
+ if (!row[posX]) {
565
+ return;
566
+ }
567
+
568
+ cell = row[posX].elm;
569
+ if (cell != lastCell) {
570
+ colSpan = getSpanVal(cell, 'colspan');
571
+ rowSpan = getSpanVal(cell, 'rowspan');
572
+
573
+ if (colSpan == 1) {
574
+ if (!before) {
575
+ dom.insertAfter(cloneCell(cell), cell);
576
+ fillLeftDown(posX, y, rowSpan - 1, colSpan);
577
+ } else {
578
+ cell.parentNode.insertBefore(cloneCell(cell), cell);
579
+ fillLeftDown(posX, y, rowSpan - 1, colSpan);
580
+ }
581
+ } else {
582
+ setSpanVal(cell, 'colSpan', cell.colSpan + 1);
583
+ }
584
+
585
+ lastCell = cell;
586
+ }
587
+ });
588
+ }
589
+
590
+ function deleteCols() {
591
+ var cols = [];
592
+
593
+ // Get selected column indexes
594
+ each(grid, function(row) {
595
+ each(row, function(cell, x) {
596
+ if (isCellSelected(cell) && Tools.inArray(cols, x) === -1) {
597
+ each(grid, function(row) {
598
+ var cell = row[x].elm, colSpan;
599
+
600
+ colSpan = getSpanVal(cell, 'colSpan');
601
+
602
+ if (colSpan > 1) {
603
+ setSpanVal(cell, 'colSpan', colSpan - 1);
604
+ } else {
605
+ dom.remove(cell);
606
+ }
607
+ });
608
+
609
+ cols.push(x);
610
+ }
611
+ });
612
+ });
613
+
614
+ cleanup();
615
+ }
616
+
617
+ function deleteRows() {
618
+ var rows;
619
+
620
+ function deleteRow(tr) {
621
+ var pos, lastCell;
622
+
623
+ // Move down row spanned cells
624
+ each(tr.cells, function(cell) {
625
+ var rowSpan = getSpanVal(cell, 'rowSpan');
626
+
627
+ if (rowSpan > 1) {
628
+ setSpanVal(cell, 'rowSpan', rowSpan - 1);
629
+ pos = getPos(cell);
630
+ fillLeftDown(pos.x, pos.y, 1, 1);
631
+ }
632
+ });
633
+
634
+ // Delete cells
635
+ pos = getPos(tr.cells[0]);
636
+ each(grid[pos.y], function(cell) {
637
+ var rowSpan;
638
+
639
+ cell = cell.elm;
640
+
641
+ if (cell != lastCell) {
642
+ rowSpan = getSpanVal(cell, 'rowSpan');
643
+
644
+ if (rowSpan <= 1) {
645
+ dom.remove(cell);
646
+ } else {
647
+ setSpanVal(cell, 'rowSpan', rowSpan - 1);
648
+ }
649
+
650
+ lastCell = cell;
651
+ }
652
+ });
653
+ }
654
+
655
+ // Get selected rows and move selection out of scope
656
+ rows = getSelectedRows();
657
+
658
+ // Delete all selected rows
659
+ each(rows.reverse(), function(tr) {
660
+ deleteRow(tr);
661
+ });
662
+
663
+ cleanup();
664
+ }
665
+
666
+ function cutRows() {
667
+ var rows = getSelectedRows();
668
+
669
+ dom.remove(rows);
670
+ cleanup();
671
+
672
+ return rows;
673
+ }
674
+
675
+ function copyRows() {
676
+ var rows = getSelectedRows();
677
+
678
+ each(rows, function(row, i) {
679
+ rows[i] = cloneNode(row, true);
680
+ });
681
+
682
+ return rows;
683
+ }
684
+
685
+ function pasteRows(rows, before) {
686
+ var selectedRows = getSelectedRows(),
687
+ targetRow = selectedRows[before ? 0 : selectedRows.length - 1],
688
+ targetCellCount = targetRow.cells.length;
689
+
690
+ // Nothing to paste
691
+ if (!rows) {
692
+ return;
693
+ }
694
+
695
+ // Calc target cell count
696
+ each(grid, function(row) {
697
+ var match;
698
+
699
+ targetCellCount = 0;
700
+ each(row, function(cell) {
701
+ if (cell.real) {
702
+ targetCellCount += cell.colspan;
703
+ }
704
+
705
+ if (cell.elm.parentNode == targetRow) {
706
+ match = 1;
707
+ }
708
+ });
709
+
710
+ if (match) {
711
+ return false;
712
+ }
713
+ });
714
+
715
+ if (!before) {
716
+ rows.reverse();
717
+ }
718
+
719
+ each(rows, function(row) {
720
+ var i, cellCount = row.cells.length, cell;
721
+
722
+ // Remove col/rowspans
723
+ for (i = 0; i < cellCount; i++) {
724
+ cell = row.cells[i];
725
+ setSpanVal(cell, 'colSpan', 1);
726
+ setSpanVal(cell, 'rowSpan', 1);
727
+ }
728
+
729
+ // Needs more cells
730
+ for (i = cellCount; i < targetCellCount; i++) {
731
+ row.appendChild(cloneCell(row.cells[cellCount - 1]));
732
+ }
733
+
734
+ // Needs less cells
735
+ for (i = targetCellCount; i < cellCount; i++) {
736
+ dom.remove(row.cells[i]);
737
+ }
738
+
739
+ // Add before/after
740
+ if (before) {
741
+ targetRow.parentNode.insertBefore(row, targetRow);
742
+ } else {
743
+ dom.insertAfter(row, targetRow);
744
+ }
745
+ });
746
+
747
+ // Remove current selection
748
+ dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
749
+ }
750
+
751
+ function getPos(target) {
752
+ var pos;
753
+
754
+ each(grid, function(row, y) {
755
+ each(row, function(cell, x) {
756
+ if (cell.elm == target) {
757
+ pos = {x : x, y : y};
758
+ return false;
759
+ }
760
+ });
761
+
762
+ return !pos;
763
+ });
764
+
765
+ return pos;
766
+ }
767
+
768
+ function setStartCell(cell) {
769
+ startPos = getPos(cell);
770
+ }
771
+
772
+ function findEndPos() {
773
+ var maxX, maxY;
774
+
775
+ maxX = maxY = 0;
776
+
777
+ each(grid, function(row, y) {
778
+ each(row, function(cell, x) {
779
+ var colSpan, rowSpan;
780
+
781
+ if (isCellSelected(cell)) {
782
+ cell = grid[y][x];
783
+
784
+ if (x > maxX) {
785
+ maxX = x;
786
+ }
787
+
788
+ if (y > maxY) {
789
+ maxY = y;
790
+ }
791
+
792
+ if (cell.real) {
793
+ colSpan = cell.colspan - 1;
794
+ rowSpan = cell.rowspan - 1;
795
+
796
+ if (colSpan) {
797
+ if (x + colSpan > maxX) {
798
+ maxX = x + colSpan;
799
+ }
800
+ }
801
+
802
+ if (rowSpan) {
803
+ if (y + rowSpan > maxY) {
804
+ maxY = y + rowSpan;
805
+ }
806
+ }
807
+ }
808
+ }
809
+ });
810
+ });
811
+
812
+ return {x : maxX, y : maxY};
813
+ }
814
+
815
+ function setEndCell(cell) {
816
+ var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan, x, y;
817
+
818
+ endPos = getPos(cell);
819
+
820
+ if (startPos && endPos) {
821
+ // Get start/end positions
822
+ startX = Math.min(startPos.x, endPos.x);
823
+ startY = Math.min(startPos.y, endPos.y);
824
+ endX = Math.max(startPos.x, endPos.x);
825
+ endY = Math.max(startPos.y, endPos.y);
826
+
827
+ // Expand end positon to include spans
828
+ maxX = endX;
829
+ maxY = endY;
830
+
831
+ // Expand startX
832
+ for (y = startY; y <= maxY; y++) {
833
+ cell = grid[y][startX];
834
+
835
+ if (!cell.real) {
836
+ if (startX - (cell.colspan - 1) < startX) {
837
+ startX -= cell.colspan - 1;
838
+ }
839
+ }
840
+ }
841
+
842
+ // Expand startY
843
+ for (x = startX; x <= maxX; x++) {
844
+ cell = grid[startY][x];
845
+
846
+ if (!cell.real) {
847
+ if (startY - (cell.rowspan - 1) < startY) {
848
+ startY -= cell.rowspan - 1;
849
+ }
850
+ }
851
+ }
852
+
853
+ // Find max X, Y
854
+ for (y = startY; y <= endY; y++) {
855
+ for (x = startX; x <= endX; x++) {
856
+ cell = grid[y][x];
857
+
858
+ if (cell.real) {
859
+ colSpan = cell.colspan - 1;
860
+ rowSpan = cell.rowspan - 1;
861
+
862
+ if (colSpan) {
863
+ if (x + colSpan > maxX) {
864
+ maxX = x + colSpan;
865
+ }
866
+ }
867
+
868
+ if (rowSpan) {
869
+ if (y + rowSpan > maxY) {
870
+ maxY = y + rowSpan;
871
+ }
872
+ }
873
+ }
874
+ }
875
+ }
876
+
877
+ // Remove current selection
878
+ dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
879
+
880
+ // Add new selection
881
+ for (y = startY; y <= maxY; y++) {
882
+ for (x = startX; x <= maxX; x++) {
883
+ if (grid[y][x]) {
884
+ dom.addClass(grid[y][x].elm, 'mce-item-selected');
885
+ }
886
+ }
887
+ }
888
+ }
889
+ }
890
+
891
+ function moveRelIdx(cellElm, delta) {
892
+ var pos, index, cell;
893
+
894
+ pos = getPos(cellElm);
895
+ index = pos.y * gridWidth + pos.x;
896
+
897
+ do {
898
+ index += delta;
899
+ cell = getCell(index % gridWidth, Math.floor(index / gridWidth));
900
+
901
+ if (!cell) {
902
+ break;
903
+ }
904
+
905
+ if (cell.elm != cellElm) {
906
+ selection.select(cell.elm, true);
907
+
908
+ if (dom.isEmpty(cell.elm)) {
909
+ selection.collapse(true);
910
+ }
911
+
912
+ return true;
913
+ }
914
+ } while (cell.elm == cellElm);
915
+
916
+ return false;
917
+ }
918
+
919
+ table = table || dom.getParent(selection.getStart(), 'table');
920
+
921
+ buildGrid();
922
+
923
+ selectedCell = dom.getParent(selection.getStart(), 'th,td');
924
+ if (selectedCell) {
925
+ startPos = getPos(selectedCell);
926
+ endPos = findEndPos();
927
+ selectedCell = getCell(startPos.x, startPos.y);
928
+ }
929
+
930
+ Tools.extend(this, {
931
+ deleteTable: deleteTable,
932
+ split: split,
933
+ merge: merge,
934
+ insertRow: insertRow,
935
+ insertCol: insertCol,
936
+ deleteCols: deleteCols,
937
+ deleteRows: deleteRows,
938
+ cutRows: cutRows,
939
+ copyRows: copyRows,
940
+ pasteRows: pasteRows,
941
+ getPos: getPos,
942
+ setStartCell: setStartCell,
943
+ setEndCell: setEndCell,
944
+ moveRelIdx: moveRelIdx,
945
+ refresh: buildGrid
946
+ });
947
+ };
948
+ });
949
+
950
+ // Included from: js/tinymce/plugins/table/classes/Quirks.js
951
+
952
+ /**
953
+ * Quirks.js
954
+ *
955
+ * Copyright, Moxiecode Systems AB
956
+ * Released under LGPL License.
957
+ *
958
+ * License: http://www.tinymce.com/license
959
+ * Contributing: http://www.tinymce.com/contributing
960
+ */
961
+
962
+ /**
963
+ * This class includes fixes for various browser quirks.
964
+ *
965
+ * @class tinymce.tableplugin.Quirks
966
+ * @private
967
+ */
968
+ define("tinymce/tableplugin/Quirks", [
969
+ "tinymce/util/VK",
970
+ "tinymce/Env",
971
+ "tinymce/util/Tools"
972
+ ], function(VK, Env, Tools) {
973
+ var each = Tools.each;
974
+
975
+ function getSpanVal(td, name) {
976
+ return parseInt(td.getAttribute(name) || 1, 10);
977
+ }
978
+
979
+ return function(editor) {
980
+ /**
981
+ * Fixed caret movement around tables on WebKit.
982
+ */
983
+ function moveWebKitSelection() {
984
+ function eventHandler(e) {
985
+ var key = e.keyCode;
986
+
987
+ function handle(upBool, sourceNode) {
988
+ var siblingDirection = upBool ? 'previousSibling' : 'nextSibling';
989
+ var currentRow = editor.dom.getParent(sourceNode, 'tr');
990
+ var siblingRow = currentRow[siblingDirection];
991
+
992
+ if (siblingRow) {
993
+ moveCursorToRow(editor, sourceNode, siblingRow, upBool);
994
+ e.preventDefault();
995
+ return true;
996
+ } else {
997
+ var tableNode = editor.dom.getParent(currentRow, 'table');
998
+ var middleNode = currentRow.parentNode;
999
+ var parentNodeName = middleNode.nodeName.toLowerCase();
1000
+ if (parentNodeName === 'tbody' || parentNodeName === (upBool ? 'tfoot' : 'thead')) {
1001
+ var targetParent = getTargetParent(upBool, tableNode, middleNode, 'tbody');
1002
+ if (targetParent !== null) {
1003
+ return moveToRowInTarget(upBool, targetParent, sourceNode);
1004
+ }
1005
+ }
1006
+ return escapeTable(upBool, currentRow, siblingDirection, tableNode);
1007
+ }
1008
+ }
1009
+
1010
+ function getTargetParent(upBool, topNode, secondNode, nodeName) {
1011
+ var tbodies = editor.dom.select('>' + nodeName, topNode);
1012
+ var position = tbodies.indexOf(secondNode);
1013
+ if (upBool && position === 0 || !upBool && position === tbodies.length - 1) {
1014
+ return getFirstHeadOrFoot(upBool, topNode);
1015
+ } else if (position === -1) {
1016
+ var topOrBottom = secondNode.tagName.toLowerCase() === 'thead' ? 0 : tbodies.length - 1;
1017
+ return tbodies[topOrBottom];
1018
+ } else {
1019
+ return tbodies[position + (upBool ? -1 : 1)];
1020
+ }
1021
+ }
1022
+
1023
+ function getFirstHeadOrFoot(upBool, parent) {
1024
+ var tagName = upBool ? 'thead' : 'tfoot';
1025
+ var headOrFoot = editor.dom.select('>' + tagName, parent);
1026
+ return headOrFoot.length !== 0 ? headOrFoot[0] : null;
1027
+ }
1028
+
1029
+ function moveToRowInTarget(upBool, targetParent, sourceNode) {
1030
+ var targetRow = getChildForDirection(targetParent, upBool);
1031
+
1032
+ if (targetRow) {
1033
+ moveCursorToRow(editor, sourceNode, targetRow, upBool);
1034
+ }
1035
+
1036
+ e.preventDefault();
1037
+ return true;
1038
+ }
1039
+
1040
+ function escapeTable(upBool, currentRow, siblingDirection, table) {
1041
+ var tableSibling = table[siblingDirection];
1042
+
1043
+ if (tableSibling) {
1044
+ moveCursorToStartOfElement(tableSibling);
1045
+ return true;
1046
+ } else {
1047
+ var parentCell = editor.dom.getParent(table, 'td,th');
1048
+ if (parentCell) {
1049
+ return handle(upBool, parentCell, e);
1050
+ } else {
1051
+ var backUpSibling = getChildForDirection(currentRow, !upBool);
1052
+ moveCursorToStartOfElement(backUpSibling);
1053
+ e.preventDefault();
1054
+ return false;
1055
+ }
1056
+ }
1057
+ }
1058
+
1059
+ function getChildForDirection(parent, up) {
1060
+ var child = parent && parent[up ? 'lastChild' : 'firstChild'];
1061
+ // BR is not a valid table child to return in this case we return the table cell
1062
+ return child && child.nodeName === 'BR' ? editor.dom.getParent(child, 'td,th') : child;
1063
+ }
1064
+
1065
+ function moveCursorToStartOfElement(n) {
1066
+ editor.selection.setCursorLocation(n, 0);
1067
+ }
1068
+
1069
+ function isVerticalMovement() {
1070
+ return key == VK.UP || key == VK.DOWN;
1071
+ }
1072
+
1073
+ function isInTable(editor) {
1074
+ var node = editor.selection.getNode();
1075
+ var currentRow = editor.dom.getParent(node, 'tr');
1076
+ return currentRow !== null;
1077
+ }
1078
+
1079
+ function columnIndex(column) {
1080
+ var colIndex = 0;
1081
+ var c = column;
1082
+ while (c.previousSibling) {
1083
+ c = c.previousSibling;
1084
+ colIndex = colIndex + getSpanVal(c, "colspan");
1085
+ }
1086
+ return colIndex;
1087
+ }
1088
+
1089
+ function findColumn(rowElement, columnIndex) {
1090
+ var c = 0, r = 0;
1091
+
1092
+ each(rowElement.children, function(cell, i) {
1093
+ c = c + getSpanVal(cell, "colspan");
1094
+ r = i;
1095
+ if (c > columnIndex) {
1096
+ return false;
1097
+ }
1098
+ });
1099
+ return r;
1100
+ }
1101
+
1102
+ function moveCursorToRow(ed, node, row, upBool) {
1103
+ var srcColumnIndex = columnIndex(editor.dom.getParent(node, 'td,th'));
1104
+ var tgtColumnIndex = findColumn(row, srcColumnIndex);
1105
+ var tgtNode = row.childNodes[tgtColumnIndex];
1106
+ var rowCellTarget = getChildForDirection(tgtNode, upBool);
1107
+ moveCursorToStartOfElement(rowCellTarget || tgtNode);
1108
+ }
1109
+
1110
+ function shouldFixCaret(preBrowserNode) {
1111
+ var newNode = editor.selection.getNode();
1112
+ var newParent = editor.dom.getParent(newNode, 'td,th');
1113
+ var oldParent = editor.dom.getParent(preBrowserNode, 'td,th');
1114
+
1115
+ return newParent && newParent !== oldParent && checkSameParentTable(newParent, oldParent);
1116
+ }
1117
+
1118
+ function checkSameParentTable(nodeOne, NodeTwo) {
1119
+ return editor.dom.getParent(nodeOne, 'TABLE') === editor.dom.getParent(NodeTwo, 'TABLE');
1120
+ }
1121
+
1122
+ if (isVerticalMovement() && isInTable(editor)) {
1123
+ var preBrowserNode = editor.selection.getNode();
1124
+ setTimeout(function() {
1125
+ if (shouldFixCaret(preBrowserNode)) {
1126
+ handle(!e.shiftKey && key === VK.UP, preBrowserNode, e);
1127
+ }
1128
+ }, 0);
1129
+ }
1130
+ }
1131
+
1132
+ editor.on('KeyDown', function(e) {
1133
+ eventHandler(e);
1134
+ });
1135
+ }
1136
+
1137
+ function fixBeforeTableCaretBug() {
1138
+ // Checks if the selection/caret is at the start of the specified block element
1139
+ function isAtStart(rng, par) {
1140
+ var doc = par.ownerDocument, rng2 = doc.createRange(), elm;
1141
+
1142
+ rng2.setStartBefore(par);
1143
+ rng2.setEnd(rng.endContainer, rng.endOffset);
1144
+
1145
+ elm = doc.createElement('body');
1146
+ elm.appendChild(rng2.cloneContents());
1147
+
1148
+ // Check for text characters of other elements that should be treated as content
1149
+ return elm.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi, '-').replace(/<[^>]+>/g, '').length === 0;
1150
+ }
1151
+
1152
+ // Fixes an bug where it's impossible to place the caret before a table in Gecko
1153
+ // this fix solves it by detecting when the caret is at the beginning of such a table
1154
+ // and then manually moves the caret infront of the table
1155
+ editor.on('KeyDown', function(e) {
1156
+ var rng, table, dom = editor.dom;
1157
+
1158
+ // On gecko it's not possible to place the caret before a table
1159
+ if (e.keyCode == 37 || e.keyCode == 38) {
1160
+ rng = editor.selection.getRng();
1161
+ table = dom.getParent(rng.startContainer, 'table');
1162
+
1163
+ if (table && editor.getBody().firstChild == table) {
1164
+ if (isAtStart(rng, table)) {
1165
+ rng = dom.createRng();
1166
+
1167
+ rng.setStartBefore(table);
1168
+ rng.setEndBefore(table);
1169
+
1170
+ editor.selection.setRng(rng);
1171
+
1172
+ e.preventDefault();
1173
+ }
1174
+ }
1175
+ }
1176
+ });
1177
+ }
1178
+
1179
+ // Fixes an issue on Gecko where it's impossible to place the caret behind a table
1180
+ // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
1181
+ function fixTableCaretPos() {
1182
+ editor.on('KeyDown SetContent VisualAid', function() {
1183
+ var last;
1184
+
1185
+ // Skip empty text nodes from the end
1186
+ for (last = editor.getBody().lastChild; last; last = last.previousSibling) {
1187
+ if (last.nodeType == 3) {
1188
+ if (last.nodeValue.length > 0) {
1189
+ break;
1190
+ }
1191
+ } else if (last.nodeType == 1 && (last.tagName == 'BR' || !last.getAttribute('data-mce-bogus'))) {
1192
+ break;
1193
+ }
1194
+ }
1195
+
1196
+ if (last && last.nodeName == 'TABLE') {
1197
+ if (editor.settings.forced_root_block) {
1198
+ editor.dom.add(
1199
+ editor.getBody(),
1200
+ editor.settings.forced_root_block,
1201
+ editor.settings.forced_root_block_attrs,
1202
+ Env.ie && Env.ie < 11 ? '&nbsp;' : '<br data-mce-bogus="1" />'
1203
+ );
1204
+ } else {
1205
+ editor.dom.add(editor.getBody(), 'br', {'data-mce-bogus': '1'});
1206
+ }
1207
+ }
1208
+ });
1209
+
1210
+ editor.on('PreProcess', function(o) {
1211
+ var last = o.node.lastChild;
1212
+
1213
+ if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 &&
1214
+ (last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))) &&
1215
+ last.previousSibling && last.previousSibling.nodeName == "TABLE") {
1216
+ editor.dom.remove(last);
1217
+ }
1218
+ });
1219
+ }
1220
+
1221
+ // this nasty hack is here to work around some WebKit selection bugs.
1222
+ function fixTableCellSelection() {
1223
+ function tableCellSelected(ed, rng, n, currentCell) {
1224
+ // The decision of when a table cell is selected is somewhat involved. The fact that this code is
1225
+ // required is actually a pointer to the root cause of this bug. A cell is selected when the start
1226
+ // and end offsets are 0, the start container is a text, and the selection node is either a TR (most cases)
1227
+ // or the parent of the table (in the case of the selection containing the last cell of a table).
1228
+ var TEXT_NODE = 3, table = ed.dom.getParent(rng.startContainer, 'TABLE');
1229
+ var tableParent, allOfCellSelected, tableCellSelection;
1230
+
1231
+ if (table) {
1232
+ tableParent = table.parentNode;
1233
+ }
1234
+
1235
+ allOfCellSelected = rng.startContainer.nodeType == TEXT_NODE &&
1236
+ rng.startOffset === 0 &&
1237
+ rng.endOffset === 0 &&
1238
+ currentCell &&
1239
+ (n.nodeName == "TR" || n == tableParent);
1240
+
1241
+ tableCellSelection = (n.nodeName == "TD" || n.nodeName == "TH") && !currentCell;
1242
+
1243
+ return allOfCellSelected || tableCellSelection;
1244
+ }
1245
+
1246
+ function fixSelection() {
1247
+ var rng = editor.selection.getRng();
1248
+ var n = editor.selection.getNode();
1249
+ var currentCell = editor.dom.getParent(rng.startContainer, 'TD,TH');
1250
+
1251
+ if (!tableCellSelected(editor, rng, n, currentCell)) {
1252
+ return;
1253
+ }
1254
+
1255
+ if (!currentCell) {
1256
+ currentCell = n;
1257
+ }
1258
+
1259
+ // Get the very last node inside the table cell
1260
+ var end = currentCell.lastChild;
1261
+ while (end.lastChild) {
1262
+ end = end.lastChild;
1263
+ }
1264
+
1265
+ // Select the entire table cell. Nothing outside of the table cell should be selected.
1266
+ if (end.nodeType == 3) {
1267
+ rng.setEnd(end, end.data.length);
1268
+ editor.selection.setRng(rng);
1269
+ }
1270
+ }
1271
+
1272
+ editor.on('KeyDown', function() {
1273
+ fixSelection();
1274
+ });
1275
+
1276
+ editor.on('MouseDown', function(e) {
1277
+ if (e.button != 2) {
1278
+ fixSelection();
1279
+ }
1280
+ });
1281
+ }
1282
+
1283
+ /**
1284
+ * Delete table if all cells are selected.
1285
+ */
1286
+ function deleteTable() {
1287
+ editor.on('keydown', function(e) {
1288
+ if ((e.keyCode == VK.DELETE || e.keyCode == VK.BACKSPACE) && !e.isDefaultPrevented()) {
1289
+ var table = editor.dom.getParent(editor.selection.getStart(), 'table');
1290
+
1291
+ if (table) {
1292
+ var cells = editor.dom.select('td,th', table), i = cells.length;
1293
+ while (i--) {
1294
+ if (!editor.dom.hasClass(cells[i], 'mce-item-selected')) {
1295
+ return;
1296
+ }
1297
+ }
1298
+
1299
+ e.preventDefault();
1300
+ editor.execCommand('mceTableDelete');
1301
+ }
1302
+ }
1303
+ });
1304
+ }
1305
+
1306
+ deleteTable();
1307
+
1308
+ if (Env.webkit) {
1309
+ moveWebKitSelection();
1310
+ fixTableCellSelection();
1311
+ }
1312
+
1313
+ if (Env.gecko) {
1314
+ fixBeforeTableCaretBug();
1315
+ fixTableCaretPos();
1316
+ }
1317
+
1318
+ if (Env.ie > 10) {
1319
+ fixBeforeTableCaretBug();
1320
+ fixTableCaretPos();
1321
+ }
1322
+ };
1323
+ });
1324
+
1325
+ // Included from: js/tinymce/plugins/table/classes/CellSelection.js
1326
+
1327
+ /**
1328
+ * CellSelection.js
1329
+ *
1330
+ * Copyright, Moxiecode Systems AB
1331
+ * Released under LGPL License.
1332
+ *
1333
+ * License: http://www.tinymce.com/license
1334
+ * Contributing: http://www.tinymce.com/contributing
1335
+ */
1336
+
1337
+ /**
1338
+ * This class handles table cell selection by faking it using a css class that gets applied
1339
+ * to cells when dragging the mouse from one cell to another.
1340
+ *
1341
+ * @class tinymce.tableplugin.CellSelection
1342
+ * @private
1343
+ */
1344
+ define("tinymce/tableplugin/CellSelection", [
1345
+ "tinymce/tableplugin/TableGrid",
1346
+ "tinymce/dom/TreeWalker",
1347
+ "tinymce/util/Tools"
1348
+ ], function(TableGrid, TreeWalker, Tools) {
1349
+ return function(editor) {
1350
+ var dom = editor.dom, tableGrid, startCell, startTable, hasCellSelection = true, resizing;
1351
+
1352
+ function clear(force) {
1353
+ // Restore selection possibilities
1354
+ editor.getBody().style.webkitUserSelect = '';
1355
+
1356
+ if (force || hasCellSelection) {
1357
+ editor.dom.removeClass(
1358
+ editor.dom.select('td.mce-item-selected,th.mce-item-selected'),
1359
+ 'mce-item-selected'
1360
+ );
1361
+
1362
+ hasCellSelection = false;
1363
+ }
1364
+ }
1365
+
1366
+ function cellSelectionHandler(e) {
1367
+ var sel, table, target = e.target;
1368
+
1369
+ if (resizing) {
1370
+ return;
1371
+ }
1372
+
1373
+ if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) {
1374
+ table = dom.getParent(target, 'table');
1375
+ if (table == startTable) {
1376
+ if (!tableGrid) {
1377
+ tableGrid = new TableGrid(editor, table);
1378
+ tableGrid.setStartCell(startCell);
1379
+
1380
+ editor.getBody().style.webkitUserSelect = 'none';
1381
+ }
1382
+
1383
+ tableGrid.setEndCell(target);
1384
+ hasCellSelection = true;
1385
+ }
1386
+
1387
+ // Remove current selection
1388
+ sel = editor.selection.getSel();
1389
+
1390
+ try {
1391
+ if (sel.removeAllRanges) {
1392
+ sel.removeAllRanges();
1393
+ } else {
1394
+ sel.empty();
1395
+ }
1396
+ } catch (ex) {
1397
+ // IE9 might throw errors here
1398
+ }
1399
+
1400
+ e.preventDefault();
1401
+ }
1402
+ }
1403
+
1404
+ // Add cell selection logic
1405
+ editor.on('MouseDown', function(e) {
1406
+ if (e.button != 2 && !resizing) {
1407
+ clear();
1408
+
1409
+ startCell = dom.getParent(e.target, 'td,th');
1410
+ startTable = dom.getParent(startCell, 'table');
1411
+ }
1412
+ });
1413
+
1414
+ editor.on('mouseover', cellSelectionHandler);
1415
+
1416
+ editor.on('remove', function() {
1417
+ dom.unbind(editor.getDoc(), 'mouseover', cellSelectionHandler);
1418
+ });
1419
+
1420
+ editor.on('MouseUp', function() {
1421
+ var rng, sel = editor.selection, selectedCells, walker, node, lastNode;
1422
+
1423
+ function setPoint(node, start) {
1424
+ var walker = new TreeWalker(node, node);
1425
+
1426
+ do {
1427
+ // Text node
1428
+ if (node.nodeType == 3 && Tools.trim(node.nodeValue).length !== 0) {
1429
+ if (start) {
1430
+ rng.setStart(node, 0);
1431
+ } else {
1432
+ rng.setEnd(node, node.nodeValue.length);
1433
+ }
1434
+
1435
+ return;
1436
+ }
1437
+
1438
+ // BR element
1439
+ if (node.nodeName == 'BR') {
1440
+ if (start) {
1441
+ rng.setStartBefore(node);
1442
+ } else {
1443
+ rng.setEndBefore(node);
1444
+ }
1445
+
1446
+ return;
1447
+ }
1448
+ } while ((node = (start ? walker.next() : walker.prev())));
1449
+ }
1450
+
1451
+ // Move selection to startCell
1452
+ if (startCell) {
1453
+ if (tableGrid) {
1454
+ editor.getBody().style.webkitUserSelect = '';
1455
+ }
1456
+
1457
+ // Try to expand text selection as much as we can only Gecko supports cell selection
1458
+ selectedCells = dom.select('td.mce-item-selected,th.mce-item-selected');
1459
+ if (selectedCells.length > 0) {
1460
+ rng = dom.createRng();
1461
+ node = selectedCells[0];
1462
+ rng.setStartBefore(node);
1463
+ rng.setEndAfter(node);
1464
+
1465
+ setPoint(node, 1);
1466
+ walker = new TreeWalker(node, dom.getParent(selectedCells[0], 'table'));
1467
+
1468
+ do {
1469
+ if (node.nodeName == 'TD' || node.nodeName == 'TH') {
1470
+ if (!dom.hasClass(node, 'mce-item-selected')) {
1471
+ break;
1472
+ }
1473
+
1474
+ lastNode = node;
1475
+ }
1476
+ } while ((node = walker.next()));
1477
+
1478
+ setPoint(lastNode);
1479
+
1480
+ sel.setRng(rng);
1481
+ }
1482
+
1483
+ editor.nodeChanged();
1484
+ startCell = tableGrid = startTable = null;
1485
+ }
1486
+ });
1487
+
1488
+ editor.on('KeyUp Drop SetContent', function(e) {
1489
+ clear(e.type == 'setcontent');
1490
+ startCell = tableGrid = startTable = null;
1491
+ resizing = false;
1492
+ });
1493
+
1494
+ editor.on('ObjectResizeStart ObjectResized', function(e) {
1495
+ resizing = e.type != 'objectresized';
1496
+ });
1497
+
1498
+ return {
1499
+ clear: clear
1500
+ };
1501
+ };
1502
+ });
1503
+
1504
+ // Included from: js/tinymce/plugins/table/classes/Dialogs.js
1505
+
1506
+ /**
1507
+ * Dialogs.js
1508
+ *
1509
+ * Copyright, Moxiecode Systems AB
1510
+ * Released under LGPL License.
1511
+ *
1512
+ * License: http://www.tinymce.com/license
1513
+ * Contributing: http://www.tinymce.com/contributing
1514
+ */
1515
+
1516
+ /**
1517
+ * ...
1518
+ *
1519
+ * @class tinymce.tableplugin.Dialogs
1520
+ * @private
1521
+ */
1522
+ define("tinymce/tableplugin/Dialogs", [
1523
+ "tinymce/util/Tools",
1524
+ "tinymce/Env"
1525
+ ], function(Tools, Env) {
1526
+ var each = Tools.each;
1527
+
1528
+ return function(editor) {
1529
+ var self = this;
1530
+
1531
+ function createColorPickAction() {
1532
+ var colorPickerCallback = editor.settings.color_picker_callback;
1533
+
1534
+ if (colorPickerCallback) {
1535
+ return function() {
1536
+ var self = this;
1537
+
1538
+ colorPickerCallback.call(
1539
+ editor,
1540
+ function(value) {
1541
+ self.value(value).fire('change');
1542
+ },
1543
+ self.value()
1544
+ );
1545
+ };
1546
+ }
1547
+ }
1548
+
1549
+ function createStyleForm(dom) {
1550
+ return {
1551
+ title: 'Advanced',
1552
+ type: 'form',
1553
+ defaults: {
1554
+ onchange: function() {
1555
+ updateStyle(dom, this.parents().reverse()[0], this.name() == "style");
1556
+ }
1557
+ },
1558
+ items: [
1559
+ {
1560
+ label: 'Style',
1561
+ name: 'style',
1562
+ type: 'textbox'
1563
+ },
1564
+
1565
+ {
1566
+ type: 'form',
1567
+ padding: 0,
1568
+ formItemDefaults: {
1569
+ layout: 'grid',
1570
+ alignH: ['start', 'right']
1571
+ },
1572
+ defaults: {
1573
+ size: 7
1574
+ },
1575
+ items: [
1576
+ {
1577
+ label: 'Border color',
1578
+ type: 'colorbox',
1579
+ name: 'borderColor',
1580
+ onaction: createColorPickAction()
1581
+ },
1582
+
1583
+ {
1584
+ label: 'Background color',
1585
+ type: 'colorbox',
1586
+ name: 'backgroundColor',
1587
+ onaction: createColorPickAction()
1588
+ }
1589
+ ]
1590
+ }
1591
+ ]
1592
+ };
1593
+ }
1594
+
1595
+ function removePxSuffix(size) {
1596
+ return size ? size.replace(/px$/, '') : "";
1597
+ }
1598
+
1599
+ function addSizeSuffix(size) {
1600
+ if (/^[0-9]+$/.test(size)) {
1601
+ size += "px";
1602
+ }
1603
+
1604
+ return size;
1605
+ }
1606
+
1607
+ function unApplyAlign(elm) {
1608
+ each('left center right'.split(' '), function(name) {
1609
+ editor.formatter.remove('align' + name, {}, elm);
1610
+ });
1611
+ }
1612
+
1613
+ function unApplyVAlign(elm) {
1614
+ each('top middle bottom'.split(' '), function(name) {
1615
+ editor.formatter.remove('valign' + name, {}, elm);
1616
+ });
1617
+ }
1618
+
1619
+ function buildListItems(inputList, itemCallback, startItems) {
1620
+ function appendItems(values, output) {
1621
+ output = output || [];
1622
+
1623
+ Tools.each(values, function(item) {
1624
+ var menuItem = {text: item.text || item.title};
1625
+
1626
+ if (item.menu) {
1627
+ menuItem.menu = appendItems(item.menu);
1628
+ } else {
1629
+ menuItem.value = item.value;
1630
+
1631
+ if (itemCallback) {
1632
+ itemCallback(menuItem);
1633
+ }
1634
+ }
1635
+
1636
+ output.push(menuItem);
1637
+ });
1638
+
1639
+ return output;
1640
+ }
1641
+
1642
+ return appendItems(inputList, startItems || []);
1643
+ }
1644
+
1645
+ function updateStyle(dom, win, isStyleCtrl) {
1646
+ var data = win.toJSON();
1647
+ var css = dom.parseStyle(data.style);
1648
+
1649
+ if (isStyleCtrl) {
1650
+ win.find('#borderColor').value(css["border-color"] || '')[0].fire('change');
1651
+ win.find('#backgroundColor').value(css["background-color"] || '')[0].fire('change');
1652
+ } else {
1653
+ css["border-color"] = data.borderColor;
1654
+ css["background-color"] = data.backgroundColor;
1655
+ }
1656
+
1657
+ win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
1658
+ }
1659
+
1660
+ function appendStylesToData(dom, data, elm) {
1661
+ var css = dom.parseStyle(dom.getAttrib(elm, 'style'));
1662
+
1663
+ if (css["border-color"]) {
1664
+ data.borderColor = css["border-color"];
1665
+ }
1666
+
1667
+ if (css["background-color"]) {
1668
+ data.backgroundColor = css["background-color"];
1669
+ }
1670
+
1671
+ data.style = dom.serializeStyle(css);
1672
+ }
1673
+
1674
+ self.tableProps = function() {
1675
+ self.table(true);
1676
+ };
1677
+
1678
+ self.table = function(isProps) {
1679
+ var dom = editor.dom, tableElm, colsCtrl, rowsCtrl, classListCtrl, data = {}, generalTableForm;
1680
+
1681
+ function onSubmitTableForm() {
1682
+ var captionElm;
1683
+
1684
+ updateStyle(dom, this);
1685
+ data = Tools.extend(data, this.toJSON());
1686
+
1687
+ Tools.each('backgroundColor borderColor'.split(' '), function(name) {
1688
+ delete data[name];
1689
+ });
1690
+
1691
+ if (data["class"] === false) {
1692
+ delete data["class"];
1693
+ }
1694
+
1695
+ editor.undoManager.transact(function() {
1696
+ if (!tableElm) {
1697
+ tableElm = editor.plugins.table.insertTable(data.cols || 1, data.rows || 1);
1698
+ }
1699
+
1700
+ editor.dom.setAttribs(tableElm, {
1701
+ cellspacing: data.cellspacing,
1702
+ cellpadding: data.cellpadding,
1703
+ border: data.border,
1704
+ style: data.style,
1705
+ 'class': data['class']
1706
+ });
1707
+
1708
+ if (dom.getAttrib(tableElm, 'width')) {
1709
+ dom.setAttrib(tableElm, 'width', removePxSuffix(data.width));
1710
+ } else {
1711
+ dom.setStyle(tableElm, 'width', addSizeSuffix(data.width));
1712
+ }
1713
+
1714
+ dom.setStyle(tableElm, 'height', addSizeSuffix(data.height));
1715
+
1716
+ // Toggle caption on/off
1717
+ captionElm = dom.select('caption', tableElm)[0];
1718
+
1719
+ if (captionElm && !data.caption) {
1720
+ dom.remove(captionElm);
1721
+ }
1722
+
1723
+ if (!captionElm && data.caption) {
1724
+ captionElm = dom.create('caption');
1725
+ captionElm.innerHTML = !Env.ie ? '<br data-mce-bogus="1"/>' : '\u00a0';
1726
+ tableElm.insertBefore(captionElm, tableElm.firstChild);
1727
+ }
1728
+
1729
+ unApplyAlign(tableElm);
1730
+ if (data.align) {
1731
+ editor.formatter.apply('align' + data.align, {}, tableElm);
1732
+ }
1733
+
1734
+ editor.focus();
1735
+ editor.addVisual();
1736
+ });
1737
+ }
1738
+
1739
+ if (isProps === true) {
1740
+ tableElm = dom.getParent(editor.selection.getStart(), 'table');
1741
+
1742
+ if (tableElm) {
1743
+ data = {
1744
+ width: removePxSuffix(dom.getStyle(tableElm, 'width') || dom.getAttrib(tableElm, 'width')),
1745
+ height: removePxSuffix(dom.getStyle(tableElm, 'height') || dom.getAttrib(tableElm, 'height')),
1746
+ cellspacing: tableElm ? dom.getAttrib(tableElm, 'cellspacing') : '',
1747
+ cellpadding: tableElm ? dom.getAttrib(tableElm, 'cellpadding') : '',
1748
+ border: tableElm ? dom.getAttrib(tableElm, 'border') : '',
1749
+ caption: !!dom.select('caption', tableElm)[0],
1750
+ 'class': dom.getAttrib(tableElm, 'class')
1751
+ };
1752
+
1753
+ each('left center right'.split(' '), function(name) {
1754
+ if (editor.formatter.matchNode(tableElm, 'align' + name)) {
1755
+ data.align = name;
1756
+ }
1757
+ });
1758
+ }
1759
+ } else {
1760
+ colsCtrl = {label: 'Cols', name: 'cols'};
1761
+ rowsCtrl = {label: 'Rows', name: 'rows'};
1762
+ }
1763
+
1764
+ if (editor.settings.table_class_list) {
1765
+ if (data["class"]) {
1766
+ data["class"] = data["class"].replace(/\s*mce\-item\-table\s*/g, '');
1767
+ }
1768
+
1769
+ classListCtrl = {
1770
+ name: 'class',
1771
+ type: 'listbox',
1772
+ label: 'Class',
1773
+ values: buildListItems(
1774
+ editor.settings.table_class_list,
1775
+ function(item) {
1776
+ if (item.value) {
1777
+ item.textStyle = function() {
1778
+ return editor.formatter.getCssText({block: 'table', classes: [item.value]});
1779
+ };
1780
+ }
1781
+ }
1782
+ )
1783
+ };
1784
+ }
1785
+
1786
+ generalTableForm = {
1787
+ type: 'form',
1788
+ layout: 'flex',
1789
+ direction: 'column',
1790
+ labelGapCalc: 'children',
1791
+ padding: 0,
1792
+ items: [
1793
+ {
1794
+ type: 'form',
1795
+ labelGapCalc: false,
1796
+ padding: 0,
1797
+ layout: 'grid',
1798
+ columns: 2,
1799
+ defaults: {
1800
+ type: 'textbox',
1801
+ maxWidth: 50
1802
+ },
1803
+ items: [
1804
+ colsCtrl,
1805
+ rowsCtrl,
1806
+ {label: 'Width', name: 'width'},
1807
+ {label: 'Height', name: 'height'},
1808
+ {label: 'Cell spacing', name: 'cellspacing'},
1809
+ {label: 'Cell padding', name: 'cellpadding'},
1810
+ {label: 'Border', name: 'border'},
1811
+ {label: 'Caption', name: 'caption', type: 'checkbox'}
1812
+ ]
1813
+ },
1814
+
1815
+ {
1816
+ label: 'Alignment',
1817
+ name: 'align',
1818
+ type: 'listbox',
1819
+ text: 'None',
1820
+ values: [
1821
+ {text: 'None', value: ''},
1822
+ {text: 'Left', value: 'left'},
1823
+ {text: 'Center', value: 'center'},
1824
+ {text: 'Right', value: 'right'}
1825
+ ]
1826
+ },
1827
+
1828
+ classListCtrl
1829
+ ]
1830
+ };
1831
+
1832
+ if (editor.settings.table_advtab !== false) {
1833
+ appendStylesToData(dom, data, tableElm);
1834
+
1835
+ editor.windowManager.open({
1836
+ title: "Table properties",
1837
+ data: data,
1838
+ bodyType: 'tabpanel',
1839
+ body: [
1840
+ {
1841
+ title: 'General',
1842
+ type: 'form',
1843
+ items: generalTableForm
1844
+ },
1845
+ createStyleForm(dom)
1846
+ ],
1847
+
1848
+ onsubmit: onSubmitTableForm
1849
+ });
1850
+ } else {
1851
+ editor.windowManager.open({
1852
+ title: "Table properties",
1853
+ data: data,
1854
+ body: generalTableForm,
1855
+ onsubmit: onSubmitTableForm
1856
+ });
1857
+ }
1858
+ };
1859
+
1860
+ self.merge = function(grid, cell) {
1861
+ editor.windowManager.open({
1862
+ title: "Merge cells",
1863
+ body: [
1864
+ {label: 'Cols', name: 'cols', type: 'textbox', value: '1', size: 10},
1865
+ {label: 'Rows', name: 'rows', type: 'textbox', value: '1', size: 10}
1866
+ ],
1867
+ onsubmit: function() {
1868
+ var data = this.toJSON();
1869
+
1870
+ editor.undoManager.transact(function() {
1871
+ grid.merge(cell, data.cols, data.rows);
1872
+ });
1873
+ }
1874
+ });
1875
+ };
1876
+
1877
+ self.cell = function() {
1878
+ var dom = editor.dom, cellElm, data, classListCtrl, cells = [];
1879
+
1880
+ function onSubmitCellForm() {
1881
+ updateStyle(dom, this);
1882
+ data = Tools.extend(data, this.toJSON());
1883
+
1884
+ editor.undoManager.transact(function() {
1885
+ each(cells, function(cellElm) {
1886
+ editor.dom.setAttribs(cellElm, {
1887
+ scope: data.scope,
1888
+ style: data.style,
1889
+ 'class': data['class']
1890
+ });
1891
+
1892
+ editor.dom.setStyles(cellElm, {
1893
+ width: addSizeSuffix(data.width),
1894
+ height: addSizeSuffix(data.height)
1895
+ });
1896
+
1897
+ // Switch cell type
1898
+ if (data.type && cellElm.nodeName.toLowerCase() != data.type) {
1899
+ cellElm = dom.rename(cellElm, data.type);
1900
+ }
1901
+
1902
+ // Apply/remove alignment
1903
+ unApplyAlign(cellElm);
1904
+ if (data.align) {
1905
+ editor.formatter.apply('align' + data.align, {}, cellElm);
1906
+ }
1907
+
1908
+ // Apply/remove vertical alignment
1909
+ unApplyVAlign(cellElm);
1910
+ if (data.valign) {
1911
+ editor.formatter.apply('valign' + data.valign, {}, cellElm);
1912
+ }
1913
+ });
1914
+
1915
+ editor.focus();
1916
+ });
1917
+ }
1918
+
1919
+ // Get selected cells or the current cell
1920
+ cells = editor.dom.select('td.mce-item-selected,th.mce-item-selected');
1921
+ cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
1922
+ if (!cells.length && cellElm) {
1923
+ cells.push(cellElm);
1924
+ }
1925
+
1926
+ cellElm = cellElm || cells[0];
1927
+
1928
+ if (!cellElm) {
1929
+ // If this element is null, return now to avoid crashing.
1930
+ return;
1931
+ }
1932
+
1933
+ data = {
1934
+ width: removePxSuffix(dom.getStyle(cellElm, 'width') || dom.getAttrib(cellElm, 'width')),
1935
+ height: removePxSuffix(dom.getStyle(cellElm, 'height') || dom.getAttrib(cellElm, 'height')),
1936
+ scope: dom.getAttrib(cellElm, 'scope'),
1937
+ 'class': dom.getAttrib(cellElm, 'class')
1938
+ };
1939
+
1940
+ data.type = cellElm.nodeName.toLowerCase();
1941
+
1942
+ each('left center right'.split(' '), function(name) {
1943
+ if (editor.formatter.matchNode(cellElm, 'align' + name)) {
1944
+ data.align = name;
1945
+ }
1946
+ });
1947
+
1948
+ each('top middle bottom'.split(' '), function(name) {
1949
+ if (editor.formatter.matchNode(cellElm, 'valign' + name)) {
1950
+ data.valign = name;
1951
+ }
1952
+ });
1953
+
1954
+ if (editor.settings.table_cell_class_list) {
1955
+ classListCtrl = {
1956
+ name: 'class',
1957
+ type: 'listbox',
1958
+ label: 'Class',
1959
+ values: buildListItems(
1960
+ editor.settings.table_cell_class_list,
1961
+ function(item) {
1962
+ if (item.value) {
1963
+ item.textStyle = function() {
1964
+ return editor.formatter.getCssText({block: 'td', classes: [item.value]});
1965
+ };
1966
+ }
1967
+ }
1968
+ )
1969
+ };
1970
+ }
1971
+
1972
+ var generalCellForm = {
1973
+ type: 'form',
1974
+ layout: 'flex',
1975
+ direction: 'column',
1976
+ labelGapCalc: 'children',
1977
+ padding: 0,
1978
+ items: [
1979
+ {
1980
+ type: 'form',
1981
+ layout: 'grid',
1982
+ columns: 2,
1983
+ labelGapCalc: false,
1984
+ padding: 0,
1985
+ defaults: {
1986
+ type: 'textbox',
1987
+ maxWidth: 50
1988
+ },
1989
+ items: [
1990
+ {label: 'Width', name: 'width'},
1991
+ {label: 'Height', name: 'height'},
1992
+ {
1993
+ label: 'Cell type',
1994
+ name: 'type',
1995
+ type: 'listbox',
1996
+ text: 'None',
1997
+ minWidth: 90,
1998
+ maxWidth: null,
1999
+ values: [
2000
+ {text: 'Cell', value: 'td'},
2001
+ {text: 'Header cell', value: 'th'}
2002
+ ]
2003
+ },
2004
+ {
2005
+ label: 'Scope',
2006
+ name: 'scope',
2007
+ type: 'listbox',
2008
+ text: 'None',
2009
+ minWidth: 90,
2010
+ maxWidth: null,
2011
+ values: [
2012
+ {text: 'None', value: ''},
2013
+ {text: 'Row', value: 'row'},
2014
+ {text: 'Column', value: 'col'},
2015
+ {text: 'Row group', value: 'rowgroup'},
2016
+ {text: 'Column group', value: 'colgroup'}
2017
+ ]
2018
+ },
2019
+ {
2020
+ label: 'H Align',
2021
+ name: 'align',
2022
+ type: 'listbox',
2023
+ text: 'None',
2024
+ minWidth: 90,
2025
+ maxWidth: null,
2026
+ values: [
2027
+ {text: 'None', value: ''},
2028
+ {text: 'Left', value: 'left'},
2029
+ {text: 'Center', value: 'center'},
2030
+ {text: 'Right', value: 'right'}
2031
+ ]
2032
+ },
2033
+ {
2034
+ label: 'V Align',
2035
+ name: 'valign',
2036
+ type: 'listbox',
2037
+ text: 'None',
2038
+ minWidth: 90,
2039
+ maxWidth: null,
2040
+ values: [
2041
+ {text: 'None', value: ''},
2042
+ {text: 'Top', value: 'top'},
2043
+ {text: 'Middle', value: 'middle'},
2044
+ {text: 'Bottom', value: 'bottom'}
2045
+ ]
2046
+ }
2047
+ ]
2048
+ },
2049
+
2050
+ classListCtrl
2051
+ ]
2052
+ };
2053
+
2054
+ if (editor.settings.table_cell_advtab !== false) {
2055
+ appendStylesToData(dom, data, cellElm);
2056
+
2057
+ editor.windowManager.open({
2058
+ title: "Cell properties",
2059
+ bodyType: 'tabpanel',
2060
+ data: data,
2061
+ body: [
2062
+ {
2063
+ title: 'General',
2064
+ type: 'form',
2065
+ items: generalCellForm
2066
+ },
2067
+
2068
+ createStyleForm(dom)
2069
+ ],
2070
+
2071
+ onsubmit: onSubmitCellForm
2072
+ });
2073
+ } else {
2074
+ editor.windowManager.open({
2075
+ title: "Cell properties",
2076
+ data: data,
2077
+ body: generalCellForm,
2078
+ onsubmit: onSubmitCellForm
2079
+ });
2080
+ }
2081
+ };
2082
+
2083
+ self.row = function() {
2084
+ var dom = editor.dom, tableElm, cellElm, rowElm, classListCtrl, data, rows = [], generalRowForm;
2085
+
2086
+ function onSubmitRowForm() {
2087
+ var tableElm, oldParentElm, parentElm;
2088
+
2089
+ updateStyle(dom, this);
2090
+ data = Tools.extend(data, this.toJSON());
2091
+
2092
+ editor.undoManager.transact(function() {
2093
+ var toType = data.type;
2094
+
2095
+ each(rows, function(rowElm) {
2096
+ editor.dom.setAttribs(rowElm, {
2097
+ scope: data.scope,
2098
+ style: data.style,
2099
+ 'class': data['class']
2100
+ });
2101
+
2102
+ editor.dom.setStyles(rowElm, {
2103
+ height: addSizeSuffix(data.height)
2104
+ });
2105
+
2106
+ if (toType != rowElm.parentNode.nodeName.toLowerCase()) {
2107
+ tableElm = dom.getParent(rowElm, 'table');
2108
+
2109
+ oldParentElm = rowElm.parentNode;
2110
+ parentElm = dom.select(toType, tableElm)[0];
2111
+ if (!parentElm) {
2112
+ parentElm = dom.create(toType);
2113
+ if (tableElm.firstChild) {
2114
+ tableElm.insertBefore(parentElm, tableElm.firstChild);
2115
+ } else {
2116
+ tableElm.appendChild(parentElm);
2117
+ }
2118
+ }
2119
+
2120
+ parentElm.appendChild(rowElm);
2121
+
2122
+ if (!oldParentElm.hasChildNodes()) {
2123
+ dom.remove(oldParentElm);
2124
+ }
2125
+ }
2126
+
2127
+ // Apply/remove alignment
2128
+ unApplyAlign(rowElm);
2129
+ if (data.align) {
2130
+ editor.formatter.apply('align' + data.align, {}, rowElm);
2131
+ }
2132
+ });
2133
+
2134
+ editor.focus();
2135
+ });
2136
+ }
2137
+
2138
+ tableElm = editor.dom.getParent(editor.selection.getStart(), 'table');
2139
+ cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
2140
+
2141
+ each(tableElm.rows, function(row) {
2142
+ each(row.cells, function(cell) {
2143
+ if (dom.hasClass(cell, 'mce-item-selected') || cell == cellElm) {
2144
+ rows.push(row);
2145
+ return false;
2146
+ }
2147
+ });
2148
+ });
2149
+
2150
+ rowElm = rows[0];
2151
+ if (!rowElm) {
2152
+ // If this element is null, return now to avoid crashing.
2153
+ return;
2154
+ }
2155
+
2156
+ data = {
2157
+ height: removePxSuffix(dom.getStyle(rowElm, 'height') || dom.getAttrib(rowElm, 'height')),
2158
+ scope: dom.getAttrib(rowElm, 'scope'),
2159
+ 'class': dom.getAttrib(rowElm, 'class')
2160
+ };
2161
+
2162
+ data.type = rowElm.parentNode.nodeName.toLowerCase();
2163
+
2164
+ each('left center right'.split(' '), function(name) {
2165
+ if (editor.formatter.matchNode(rowElm, 'align' + name)) {
2166
+ data.align = name;
2167
+ }
2168
+ });
2169
+
2170
+ if (editor.settings.table_row_class_list) {
2171
+ classListCtrl = {
2172
+ name: 'class',
2173
+ type: 'listbox',
2174
+ label: 'Class',
2175
+ values: buildListItems(
2176
+ editor.settings.table_row_class_list,
2177
+ function(item) {
2178
+ if (item.value) {
2179
+ item.textStyle = function() {
2180
+ return editor.formatter.getCssText({block: 'tr', classes: [item.value]});
2181
+ };
2182
+ }
2183
+ }
2184
+ )
2185
+ };
2186
+ }
2187
+
2188
+ generalRowForm = {
2189
+ type: 'form',
2190
+ columns: 2,
2191
+ padding: 0,
2192
+ defaults: {
2193
+ type: 'textbox'
2194
+ },
2195
+ items: [
2196
+ {
2197
+ type: 'listbox',
2198
+ name: 'type',
2199
+ label: 'Row type',
2200
+ text: 'None',
2201
+ maxWidth: null,
2202
+ values: [
2203
+ {text: 'Header', value: 'thead'},
2204
+ {text: 'Body', value: 'tbody'},
2205
+ {text: 'Footer', value: 'tfoot'}
2206
+ ]
2207
+ },
2208
+ {
2209
+ type: 'listbox',
2210
+ name: 'align',
2211
+ label: 'Alignment',
2212
+ text: 'None',
2213
+ maxWidth: null,
2214
+ values: [
2215
+ {text: 'None', value: ''},
2216
+ {text: 'Left', value: 'left'},
2217
+ {text: 'Center', value: 'center'},
2218
+ {text: 'Right', value: 'right'}
2219
+ ]
2220
+ },
2221
+ {label: 'Height', name: 'height'},
2222
+ classListCtrl
2223
+ ]
2224
+ };
2225
+
2226
+ if (editor.settings.table_row_advtab !== false) {
2227
+ appendStylesToData(dom, data, rowElm);
2228
+
2229
+ editor.windowManager.open({
2230
+ title: "Row properties",
2231
+ data: data,
2232
+ bodyType: 'tabpanel',
2233
+ body: [
2234
+ {
2235
+ title: 'General',
2236
+ type: 'form',
2237
+ items: generalRowForm
2238
+ },
2239
+ createStyleForm(dom)
2240
+ ],
2241
+
2242
+ onsubmit: onSubmitRowForm
2243
+ });
2244
+ } else {
2245
+ editor.windowManager.open({
2246
+ title: "Row properties",
2247
+ data: data,
2248
+ body: generalRowForm,
2249
+ onsubmit: onSubmitRowForm
2250
+ });
2251
+ }
2252
+ };
2253
+ };
2254
+ });
2255
+
2256
+ // Included from: js/tinymce/plugins/table/classes/Plugin.js
2257
+
2258
+ /**
2259
+ * Plugin.js
2260
+ *
2261
+ * Copyright, Moxiecode Systems AB
2262
+ * Released under LGPL License.
2263
+ *
2264
+ * License: http://www.tinymce.com/license
2265
+ * Contributing: http://www.tinymce.com/contributing
2266
+ */
2267
+
2268
+ /**
2269
+ * This class contains all core logic for the table plugin.
2270
+ *
2271
+ * @class tinymce.tableplugin.Plugin
2272
+ * @private
2273
+ */
2274
+ define("tinymce/tableplugin/Plugin", [
2275
+ "tinymce/tableplugin/TableGrid",
2276
+ "tinymce/tableplugin/Quirks",
2277
+ "tinymce/tableplugin/CellSelection",
2278
+ "tinymce/tableplugin/Dialogs",
2279
+ "tinymce/util/Tools",
2280
+ "tinymce/dom/TreeWalker",
2281
+ "tinymce/Env",
2282
+ "tinymce/PluginManager"
2283
+ ], function(TableGrid, Quirks, CellSelection, Dialogs, Tools, TreeWalker, Env, PluginManager) {
2284
+ var each = Tools.each;
2285
+
2286
+ function Plugin(editor) {
2287
+ var clipboardRows, self = this, dialogs = new Dialogs(editor);
2288
+
2289
+ function cmd(command) {
2290
+ return function() {
2291
+ editor.execCommand(command);
2292
+ };
2293
+ }
2294
+
2295
+ function insertTable(cols, rows) {
2296
+ var y, x, html, tableElm;
2297
+
2298
+ html = '<table id="__mce"><tbody>';
2299
+
2300
+ for (y = 0; y < rows; y++) {
2301
+ html += '<tr>';
2302
+
2303
+ for (x = 0; x < cols; x++) {
2304
+ html += '<td>' + (Env.ie ? " " : '<br>') + '</td>';
2305
+ }
2306
+
2307
+ html += '</tr>';
2308
+ }
2309
+
2310
+ html += '</tbody></table>';
2311
+
2312
+ editor.undoManager.transact(function() {
2313
+ editor.insertContent(html);
2314
+
2315
+ tableElm = editor.dom.get('__mce');
2316
+ editor.dom.setAttrib(tableElm, 'id', null);
2317
+
2318
+ editor.dom.setAttribs(tableElm, editor.settings.table_default_attributes || {});
2319
+ editor.dom.setStyles(tableElm, editor.settings.table_default_styles || {});
2320
+ });
2321
+
2322
+ return tableElm;
2323
+ }
2324
+
2325
+ function handleDisabledState(ctrl, selector) {
2326
+ function bindStateListener() {
2327
+ ctrl.disabled(!editor.dom.getParent(editor.selection.getStart(), selector));
2328
+
2329
+ editor.selection.selectorChanged(selector, function(state) {
2330
+ ctrl.disabled(!state);
2331
+ });
2332
+ }
2333
+
2334
+ if (editor.initialized) {
2335
+ bindStateListener();
2336
+ } else {
2337
+ editor.on('init', bindStateListener);
2338
+ }
2339
+ }
2340
+
2341
+ function postRender() {
2342
+ /*jshint validthis:true*/
2343
+ handleDisabledState(this, 'table');
2344
+ }
2345
+
2346
+ function postRenderCell() {
2347
+ /*jshint validthis:true*/
2348
+ handleDisabledState(this, 'td,th');
2349
+ }
2350
+
2351
+ function generateTableGrid() {
2352
+ var html = '';
2353
+
2354
+ html = '<table role="grid" class="mce-grid mce-grid-border" aria-readonly="true">';
2355
+
2356
+ for (var y = 0; y < 10; y++) {
2357
+ html += '<tr>';
2358
+
2359
+ for (var x = 0; x < 10; x++) {
2360
+ html += '<td role="gridcell" tabindex="-1"><a id="mcegrid' + (y * 10 + x) + '" href="#" ' +
2361
+ 'data-mce-x="' + x + '" data-mce-y="' + y + '"></a></td>';
2362
+ }
2363
+
2364
+ html += '</tr>';
2365
+ }
2366
+
2367
+ html += '</table>';
2368
+
2369
+ html += '<div class="mce-text-center" role="presentation">1 x 1</div>';
2370
+
2371
+ return html;
2372
+ }
2373
+
2374
+ function selectGrid(tx, ty, control) {
2375
+ var table = control.getEl().getElementsByTagName('table')[0];
2376
+ var x, y, focusCell, cell, active;
2377
+ var rtl = control.isRtl() || control.parent().rel == 'tl-tr';
2378
+
2379
+ table.nextSibling.innerHTML = (tx + 1) + ' x ' + (ty + 1);
2380
+
2381
+ if (rtl) {
2382
+ tx = 9 - tx;
2383
+ }
2384
+
2385
+ for (y = 0; y < 10; y++) {
2386
+ for (x = 0; x < 10; x++) {
2387
+ cell = table.rows[y].childNodes[x].firstChild;
2388
+ active = (rtl ? x >= tx : x <= tx) && y <= ty;
2389
+
2390
+ editor.dom.toggleClass(cell, 'mce-active', active);
2391
+
2392
+ if (active) {
2393
+ focusCell = cell;
2394
+ }
2395
+ }
2396
+ }
2397
+
2398
+ return focusCell.parentNode;
2399
+ }
2400
+
2401
+ if (editor.settings.table_grid === false) {
2402
+ editor.addMenuItem('inserttable', {
2403
+ text: 'Insert table',
2404
+ icon: 'table',
2405
+ context: 'table',
2406
+ onclick: dialogs.table
2407
+ });
2408
+ } else {
2409
+ editor.addMenuItem('inserttable', {
2410
+ text: 'Insert table',
2411
+ icon: 'table',
2412
+ context: 'table',
2413
+ ariaHideMenu: true,
2414
+ onclick: function(e) {
2415
+ if (e.aria) {
2416
+ this.parent().hideAll();
2417
+ e.stopImmediatePropagation();
2418
+ dialogs.table();
2419
+ }
2420
+ },
2421
+ onshow: function() {
2422
+ selectGrid(0, 0, this.menu.items()[0]);
2423
+ },
2424
+ onhide: function() {
2425
+ var elements = this.menu.items()[0].getEl().getElementsByTagName('a');
2426
+ editor.dom.removeClass(elements, 'mce-active');
2427
+ editor.dom.addClass(elements[0], 'mce-active');
2428
+ },
2429
+ menu: [
2430
+ {
2431
+ type: 'container',
2432
+ html: generateTableGrid(),
2433
+
2434
+ onPostRender: function() {
2435
+ this.lastX = this.lastY = 0;
2436
+ },
2437
+
2438
+ onmousemove: function(e) {
2439
+ var target = e.target, x, y;
2440
+
2441
+ if (target.tagName.toUpperCase() == 'A') {
2442
+ x = parseInt(target.getAttribute('data-mce-x'), 10);
2443
+ y = parseInt(target.getAttribute('data-mce-y'), 10);
2444
+
2445
+ if (this.isRtl() || this.parent().rel == 'tl-tr') {
2446
+ x = 9 - x;
2447
+ }
2448
+
2449
+ if (x !== this.lastX || y !== this.lastY) {
2450
+ selectGrid(x, y, e.control);
2451
+
2452
+ this.lastX = x;
2453
+ this.lastY = y;
2454
+ }
2455
+ }
2456
+ },
2457
+
2458
+ onclick: function(e) {
2459
+ var self = this;
2460
+
2461
+ if (e.target.tagName.toUpperCase() == 'A') {
2462
+ e.preventDefault();
2463
+ e.stopPropagation();
2464
+ self.parent().cancel();
2465
+
2466
+ editor.undoManager.transact(function() {
2467
+ insertTable(self.lastX + 1, self.lastY + 1);
2468
+ });
2469
+
2470
+ editor.addVisual();
2471
+ }
2472
+ }
2473
+ }
2474
+ ]
2475
+ });
2476
+ }
2477
+
2478
+ editor.addMenuItem('tableprops', {
2479
+ text: 'Table properties',
2480
+ context: 'table',
2481
+ onPostRender: postRender,
2482
+ onclick: dialogs.tableProps
2483
+ });
2484
+
2485
+ editor.addMenuItem('deletetable', {
2486
+ text: 'Delete table',
2487
+ context: 'table',
2488
+ onPostRender: postRender,
2489
+ cmd: 'mceTableDelete'
2490
+ });
2491
+
2492
+ editor.addMenuItem('cell', {
2493
+ separator: 'before',
2494
+ text: 'Cell',
2495
+ context: 'table',
2496
+ menu: [
2497
+ {text: 'Cell properties', onclick: cmd('mceTableCellProps'), onPostRender: postRenderCell},
2498
+ {text: 'Merge cells', onclick: cmd('mceTableMergeCells'), onPostRender: postRenderCell},
2499
+ {text: 'Split cell', onclick: cmd('mceTableSplitCells'), onPostRender: postRenderCell}
2500
+ ]
2501
+ });
2502
+
2503
+ editor.addMenuItem('row', {
2504
+ text: 'Row',
2505
+ context: 'table',
2506
+ menu: [
2507
+ {text: 'Insert row before', onclick: cmd('mceTableInsertRowBefore'), onPostRender: postRenderCell},
2508
+ {text: 'Insert row after', onclick: cmd('mceTableInsertRowAfter'), onPostRender: postRenderCell},
2509
+ {text: 'Delete row', onclick: cmd('mceTableDeleteRow'), onPostRender: postRenderCell},
2510
+ {text: 'Row properties', onclick: cmd('mceTableRowProps'), onPostRender: postRenderCell},
2511
+ {text: '-'},
2512
+ {text: 'Cut row', onclick: cmd('mceTableCutRow'), onPostRender: postRenderCell},
2513
+ {text: 'Copy row', onclick: cmd('mceTableCopyRow'), onPostRender: postRenderCell},
2514
+ {text: 'Paste row before', onclick: cmd('mceTablePasteRowBefore'), onPostRender: postRenderCell},
2515
+ {text: 'Paste row after', onclick: cmd('mceTablePasteRowAfter'), onPostRender: postRenderCell}
2516
+ ]
2517
+ });
2518
+
2519
+ editor.addMenuItem('column', {
2520
+ text: 'Column',
2521
+ context: 'table',
2522
+ menu: [
2523
+ {text: 'Insert column before', onclick: cmd('mceTableInsertColBefore'), onPostRender: postRenderCell},
2524
+ {text: 'Insert column after', onclick: cmd('mceTableInsertColAfter'), onPostRender: postRenderCell},
2525
+ {text: 'Delete column', onclick: cmd('mceTableDeleteCol'), onPostRender: postRenderCell}
2526
+ ]
2527
+ });
2528
+
2529
+ var menuItems = [];
2530
+ each("inserttable tableprops deletetable | cell row column".split(' '), function(name) {
2531
+ if (name == '|') {
2532
+ menuItems.push({text: '-'});
2533
+ } else {
2534
+ menuItems.push(editor.menuItems[name]);
2535
+ }
2536
+ });
2537
+
2538
+ editor.addButton("table", {
2539
+ type: "menubutton",
2540
+ title: "Table",
2541
+ menu: menuItems
2542
+ });
2543
+
2544
+ // Select whole table is a table border is clicked
2545
+ if (!Env.isIE) {
2546
+ editor.on('click', function(e) {
2547
+ e = e.target;
2548
+
2549
+ if (e.nodeName === 'TABLE') {
2550
+ editor.selection.select(e);
2551
+ editor.nodeChanged();
2552
+ }
2553
+ });
2554
+ }
2555
+
2556
+ self.quirks = new Quirks(editor);
2557
+
2558
+ editor.on('Init', function() {
2559
+ self.cellSelection = new CellSelection(editor);
2560
+ });
2561
+
2562
+ // Register action commands
2563
+ each({
2564
+ mceTableSplitCells: function(grid) {
2565
+ grid.split();
2566
+ },
2567
+
2568
+ mceTableMergeCells: function(grid) {
2569
+ var cell;
2570
+
2571
+ cell = editor.dom.getParent(editor.selection.getStart(), 'th,td');
2572
+
2573
+ if (!editor.dom.select('td.mce-item-selected,th.mce-item-selected').length) {
2574
+ dialogs.merge(grid, cell);
2575
+ } else {
2576
+ grid.merge();
2577
+ }
2578
+ },
2579
+
2580
+ mceTableInsertRowBefore: function(grid) {
2581
+ grid.insertRow(true);
2582
+ },
2583
+
2584
+ mceTableInsertRowAfter: function(grid) {
2585
+ grid.insertRow();
2586
+ },
2587
+
2588
+ mceTableInsertColBefore: function(grid) {
2589
+ grid.insertCol(true);
2590
+ },
2591
+
2592
+ mceTableInsertColAfter: function(grid) {
2593
+ grid.insertCol();
2594
+ },
2595
+
2596
+ mceTableDeleteCol: function(grid) {
2597
+ grid.deleteCols();
2598
+ },
2599
+
2600
+ mceTableDeleteRow: function(grid) {
2601
+ grid.deleteRows();
2602
+ },
2603
+
2604
+ mceTableCutRow: function(grid) {
2605
+ clipboardRows = grid.cutRows();
2606
+ },
2607
+
2608
+ mceTableCopyRow: function(grid) {
2609
+ clipboardRows = grid.copyRows();
2610
+ },
2611
+
2612
+ mceTablePasteRowBefore: function(grid) {
2613
+ grid.pasteRows(clipboardRows, true);
2614
+ },
2615
+
2616
+ mceTablePasteRowAfter: function(grid) {
2617
+ grid.pasteRows(clipboardRows);
2618
+ },
2619
+
2620
+ mceTableDelete: function(grid) {
2621
+ grid.deleteTable();
2622
+ }
2623
+ }, function(func, name) {
2624
+ editor.addCommand(name, function() {
2625
+ var grid = new TableGrid(editor);
2626
+
2627
+ if (grid) {
2628
+ func(grid);
2629
+ editor.execCommand('mceRepaint');
2630
+ self.cellSelection.clear();
2631
+ }
2632
+ });
2633
+ });
2634
+
2635
+ // Register dialog commands
2636
+ each({
2637
+ mceInsertTable: dialogs.table,
2638
+ mceTableProps: function() {
2639
+ dialogs.table(true);
2640
+ },
2641
+ mceTableRowProps: dialogs.row,
2642
+ mceTableCellProps: dialogs.cell
2643
+ }, function(func, name) {
2644
+ editor.addCommand(name, function(ui, val) {
2645
+ func(val);
2646
+ });
2647
+ });
2648
+
2649
+ // Enable tab key cell navigation
2650
+ if (editor.settings.table_tab_navigation !== false) {
2651
+ editor.on('keydown', function(e) {
2652
+ var cellElm, grid, delta;
2653
+
2654
+ if (e.keyCode == 9) {
2655
+ cellElm = editor.dom.getParent(editor.selection.getStart(), 'th,td');
2656
+
2657
+ if (cellElm) {
2658
+ e.preventDefault();
2659
+
2660
+ grid = new TableGrid(editor);
2661
+ delta = e.shiftKey ? -1 : 1;
2662
+
2663
+ editor.undoManager.transact(function() {
2664
+ if (!grid.moveRelIdx(cellElm, delta) && delta > 0) {
2665
+ grid.insertRow();
2666
+ grid.refresh();
2667
+ grid.moveRelIdx(cellElm, delta);
2668
+ }
2669
+ });
2670
+ }
2671
+ }
2672
+ });
2673
+ }
2674
+
2675
+ self.insertTable = insertTable;
2676
+ }
2677
+
2678
+ PluginManager.add('table', Plugin);
2679
+ });
2680
+ })(this);