suneditor 2.46.2 → 3.0.0-alpha.2

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 (289) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc.json +64 -0
  3. package/CONTRIBUTING.md +36 -0
  4. package/LICENSE.txt +1 -1
  5. package/README.md +11 -1560
  6. package/package.json +94 -70
  7. package/src/assets/icons/_default.js +194 -0
  8. package/src/assets/suneditor-content.css +642 -0
  9. package/src/assets/suneditor.css +3378 -0
  10. package/src/core/base/eventHandlers/handler_toolbar.js +114 -0
  11. package/src/core/base/eventHandlers/handler_ww_clipboard.js +31 -0
  12. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +69 -0
  13. package/src/core/base/eventHandlers/handler_ww_key_input.js +978 -0
  14. package/src/core/base/eventHandlers/handler_ww_mouse.js +118 -0
  15. package/src/core/base/eventManager.js +1129 -0
  16. package/src/core/base/events.js +320 -0
  17. package/src/core/base/history.js +301 -0
  18. package/src/core/class/char.js +146 -0
  19. package/src/core/class/component.js +624 -0
  20. package/src/core/class/format.js +3255 -0
  21. package/src/core/class/html.js +1621 -0
  22. package/src/core/class/menu.js +260 -0
  23. package/src/core/class/nodeTransform.js +379 -0
  24. package/src/core/class/notice.js +42 -0
  25. package/src/core/class/offset.js +578 -0
  26. package/src/core/class/selection.js +508 -0
  27. package/src/core/class/shortcuts.js +38 -0
  28. package/src/core/class/toolbar.js +440 -0
  29. package/src/core/class/viewer.js +646 -0
  30. package/src/core/editor.js +1588 -0
  31. package/src/core/section/actives.js +107 -0
  32. package/src/core/section/constructor.js +1237 -0
  33. package/src/core/section/context.js +97 -0
  34. package/src/editorInjector/_classes.js +22 -0
  35. package/src/editorInjector/_core.js +28 -0
  36. package/src/editorInjector/index.js +13 -0
  37. package/src/helper/converter.js +313 -0
  38. package/src/helper/domUtils.js +1177 -0
  39. package/src/helper/env.js +250 -0
  40. package/src/helper/index.js +19 -0
  41. package/src/helper/numbers.js +68 -0
  42. package/src/helper/unicode.js +43 -0
  43. package/src/langs/ckb.js +161 -0
  44. package/src/langs/cs.js +161 -0
  45. package/src/langs/da.js +161 -0
  46. package/src/langs/de.js +162 -0
  47. package/src/langs/en.js +199 -0
  48. package/src/langs/es.js +162 -0
  49. package/src/langs/fa.js +159 -0
  50. package/src/langs/fr.js +161 -0
  51. package/src/langs/he.js +162 -0
  52. package/src/{lang → langs}/index.js +0 -2
  53. package/src/langs/it.js +162 -0
  54. package/src/langs/ja.js +162 -0
  55. package/src/langs/ko.js +162 -0
  56. package/src/langs/lv.js +162 -0
  57. package/src/langs/nl.js +162 -0
  58. package/src/langs/pl.js +162 -0
  59. package/src/langs/pt_br.js +162 -0
  60. package/src/langs/ro.js +162 -0
  61. package/src/langs/ru.js +162 -0
  62. package/src/langs/se.js +162 -0
  63. package/src/langs/tr.js +159 -0
  64. package/src/langs/ua.js +162 -0
  65. package/src/langs/ur.js +162 -0
  66. package/src/langs/zh_cn.js +162 -0
  67. package/src/modules/ApiManager.js +168 -0
  68. package/src/modules/ColorPicker.js +302 -0
  69. package/src/modules/Controller.js +315 -0
  70. package/src/modules/Figure.js +1174 -0
  71. package/src/modules/FileBrowser.js +271 -0
  72. package/src/modules/FileManager.js +290 -0
  73. package/src/modules/HueSlider.js +513 -0
  74. package/src/modules/Modal.js +177 -0
  75. package/src/modules/ModalAnchorEditor.js +494 -0
  76. package/src/modules/SelectMenu.js +447 -0
  77. package/src/modules/_DragHandle.js +16 -0
  78. package/src/modules/index.js +14 -0
  79. package/src/plugins/command/blockquote.js +47 -47
  80. package/src/plugins/command/exportPdf.js +168 -0
  81. package/src/plugins/command/fileUpload.js +389 -0
  82. package/src/plugins/command/list_bulleted.js +112 -0
  83. package/src/plugins/command/list_numbered.js +115 -0
  84. package/src/plugins/dropdown/align.js +143 -0
  85. package/src/plugins/dropdown/backgroundColor.js +73 -0
  86. package/src/plugins/dropdown/font.js +113 -0
  87. package/src/plugins/dropdown/fontColor.js +73 -0
  88. package/src/plugins/dropdown/formatBlock.js +141 -0
  89. package/src/plugins/dropdown/hr.js +111 -0
  90. package/src/plugins/dropdown/layout.js +72 -0
  91. package/src/plugins/dropdown/lineHeight.js +114 -0
  92. package/src/plugins/dropdown/list.js +107 -0
  93. package/src/plugins/dropdown/paragraphStyle.js +117 -0
  94. package/src/plugins/dropdown/table.js +2810 -0
  95. package/src/plugins/dropdown/template.js +71 -0
  96. package/src/plugins/dropdown/textStyle.js +137 -0
  97. package/src/plugins/field/mention.js +172 -0
  98. package/src/plugins/fileBrowser/imageGallery.js +76 -59
  99. package/src/plugins/index.js +86 -24
  100. package/src/plugins/input/fontSize.js +357 -0
  101. package/src/plugins/modal/audio.js +492 -0
  102. package/src/plugins/modal/image.js +1062 -0
  103. package/src/plugins/modal/link.js +211 -0
  104. package/src/plugins/modal/math.js +347 -0
  105. package/src/plugins/modal/video.js +870 -0
  106. package/src/suneditor.js +62 -67
  107. package/src/themes/test.css +61 -0
  108. package/typings/CommandPlugin.d.ts +8 -0
  109. package/typings/DialogPlugin.d.ts +20 -0
  110. package/typings/FileBrowserPlugin.d.ts +30 -0
  111. package/typings/Lang.d.ts +124 -0
  112. package/typings/Module.d.ts +15 -0
  113. package/typings/Plugin.d.ts +42 -0
  114. package/typings/SubmenuPlugin.d.ts +8 -0
  115. package/typings/_classes.d.ts +17 -0
  116. package/typings/_colorPicker.d.ts +60 -0
  117. package/typings/_core.d.ts +55 -0
  118. package/typings/align.d.ts +5 -0
  119. package/{src/plugins/dialog → typings}/audio.d.ts +1 -1
  120. package/typings/backgroundColor.d.ts +5 -0
  121. package/{src/plugins/command → typings}/blockquote.d.ts +1 -1
  122. package/typings/char.d.ts +39 -0
  123. package/typings/component.d.ts +38 -0
  124. package/typings/context.d.ts +39 -0
  125. package/typings/converter.d.ts +33 -0
  126. package/typings/dialog.d.ts +28 -0
  127. package/typings/domUtils.d.ts +361 -0
  128. package/typings/editor.d.ts +7 -0
  129. package/typings/editor.ts +542 -0
  130. package/typings/env.d.ts +70 -0
  131. package/typings/eventManager.d.ts +37 -0
  132. package/typings/events.d.ts +262 -0
  133. package/typings/fileBrowser.d.ts +42 -0
  134. package/typings/fileManager.d.ts +67 -0
  135. package/typings/font.d.ts +5 -0
  136. package/typings/fontColor.d.ts +5 -0
  137. package/typings/fontSize.d.ts +5 -0
  138. package/typings/format.d.ts +191 -0
  139. package/typings/formatBlock.d.ts +5 -0
  140. package/typings/history.d.ts +48 -0
  141. package/typings/horizontalRule.d.ts +5 -0
  142. package/{src/plugins/dialog → typings}/image.d.ts +1 -1
  143. package/{src/plugins/fileBrowser → typings}/imageGallery.d.ts +1 -1
  144. package/typings/index.d.ts +21 -0
  145. package/{src/plugins/modules/index.d.ts → typings/index.modules.d.ts} +3 -3
  146. package/typings/index.plugins.d.ts +58 -0
  147. package/typings/lineHeight.d.ts +5 -0
  148. package/{src/plugins/dialog → typings}/link.d.ts +1 -1
  149. package/typings/list.d.ts +5 -0
  150. package/{src/plugins/dialog → typings}/math.d.ts +1 -1
  151. package/typings/mediaContainer.d.ts +25 -0
  152. package/typings/node.d.ts +57 -0
  153. package/typings/notice.d.ts +16 -0
  154. package/typings/numbers.d.ts +29 -0
  155. package/typings/offset.d.ts +24 -0
  156. package/typings/options.d.ts +589 -0
  157. package/typings/paragraphStyle.d.ts +5 -0
  158. package/typings/resizing.d.ts +141 -0
  159. package/typings/selection.d.ts +94 -0
  160. package/typings/shortcuts.d.ts +13 -0
  161. package/typings/suneditor.d.ts +9 -0
  162. package/typings/table.d.ts +5 -0
  163. package/typings/template.d.ts +5 -0
  164. package/typings/textStyle.d.ts +5 -0
  165. package/typings/toolbar.d.ts +32 -0
  166. package/typings/unicode.d.ts +25 -0
  167. package/{src/plugins/dialog → typings}/video.d.ts +1 -1
  168. package/dist/css/suneditor.min.css +0 -1
  169. package/dist/suneditor.min.js +0 -2
  170. package/src/assets/css/suneditor-contents.css +0 -562
  171. package/src/assets/css/suneditor.css +0 -566
  172. package/src/assets/defaultIcons.js +0 -103
  173. package/src/lang/Lang.d.ts +0 -144
  174. package/src/lang/ckb.d.ts +0 -5
  175. package/src/lang/ckb.js +0 -188
  176. package/src/lang/cs.d.ts +0 -5
  177. package/src/lang/cs.js +0 -188
  178. package/src/lang/da.d.ts +0 -5
  179. package/src/lang/da.js +0 -191
  180. package/src/lang/de.d.ts +0 -5
  181. package/src/lang/de.js +0 -188
  182. package/src/lang/en.d.ts +0 -5
  183. package/src/lang/en.js +0 -188
  184. package/src/lang/es.d.ts +0 -5
  185. package/src/lang/es.js +0 -188
  186. package/src/lang/fa.d.ts +0 -5
  187. package/src/lang/fa.js +0 -188
  188. package/src/lang/fr.d.ts +0 -5
  189. package/src/lang/fr.js +0 -188
  190. package/src/lang/he.d.ts +0 -5
  191. package/src/lang/he.js +0 -188
  192. package/src/lang/index.d.ts +0 -23
  193. package/src/lang/it.d.ts +0 -5
  194. package/src/lang/it.js +0 -188
  195. package/src/lang/ja.d.ts +0 -5
  196. package/src/lang/ja.js +0 -188
  197. package/src/lang/ko.d.ts +0 -5
  198. package/src/lang/ko.js +0 -188
  199. package/src/lang/lv.d.ts +0 -5
  200. package/src/lang/lv.js +0 -188
  201. package/src/lang/nl.d.ts +0 -5
  202. package/src/lang/nl.js +0 -188
  203. package/src/lang/pl.d.ts +0 -5
  204. package/src/lang/pl.js +0 -188
  205. package/src/lang/pt_br.d.ts +0 -5
  206. package/src/lang/pt_br.js +0 -189
  207. package/src/lang/ro.d.ts +0 -5
  208. package/src/lang/ro.js +0 -188
  209. package/src/lang/ru.d.ts +0 -5
  210. package/src/lang/ru.js +0 -188
  211. package/src/lang/se.d.ts +0 -5
  212. package/src/lang/se.js +0 -191
  213. package/src/lang/tr.d.ts +0 -5
  214. package/src/lang/tr.js +0 -191
  215. package/src/lang/ua.d.ts +0 -5
  216. package/src/lang/ua.js +0 -188
  217. package/src/lang/ur.d.ts +0 -5
  218. package/src/lang/ur.js +0 -188
  219. package/src/lang/zh_cn.d.ts +0 -5
  220. package/src/lang/zh_cn.js +0 -187
  221. package/src/lib/constructor.js +0 -954
  222. package/src/lib/context.d.ts +0 -42
  223. package/src/lib/context.js +0 -71
  224. package/src/lib/core.d.ts +0 -1135
  225. package/src/lib/core.js +0 -9395
  226. package/src/lib/history.d.ts +0 -48
  227. package/src/lib/history.js +0 -219
  228. package/src/lib/util.d.ts +0 -678
  229. package/src/lib/util.js +0 -2131
  230. package/src/options.d.ts +0 -608
  231. package/src/plugins/CommandPlugin.d.ts +0 -8
  232. package/src/plugins/DialogPlugin.d.ts +0 -20
  233. package/src/plugins/FileBrowserPlugin.d.ts +0 -30
  234. package/src/plugins/Module.d.ts +0 -15
  235. package/src/plugins/Plugin.d.ts +0 -42
  236. package/src/plugins/SubmenuPlugin.d.ts +0 -8
  237. package/src/plugins/dialog/audio.js +0 -559
  238. package/src/plugins/dialog/image.js +0 -1126
  239. package/src/plugins/dialog/link.js +0 -223
  240. package/src/plugins/dialog/math.js +0 -295
  241. package/src/plugins/dialog/mention.js +0 -242
  242. package/src/plugins/dialog/video.js +0 -979
  243. package/src/plugins/index.d.ts +0 -79
  244. package/src/plugins/modules/_anchor.js +0 -461
  245. package/src/plugins/modules/_colorPicker.d.ts +0 -60
  246. package/src/plugins/modules/_colorPicker.js +0 -201
  247. package/src/plugins/modules/_notice.d.ts +0 -21
  248. package/src/plugins/modules/_notice.js +0 -72
  249. package/src/plugins/modules/_selectMenu.js +0 -119
  250. package/src/plugins/modules/component.d.ts +0 -25
  251. package/src/plugins/modules/component.js +0 -81
  252. package/src/plugins/modules/dialog.d.ts +0 -28
  253. package/src/plugins/modules/dialog.js +0 -175
  254. package/src/plugins/modules/fileBrowser.d.ts +0 -42
  255. package/src/plugins/modules/fileBrowser.js +0 -374
  256. package/src/plugins/modules/fileManager.d.ts +0 -67
  257. package/src/plugins/modules/fileManager.js +0 -326
  258. package/src/plugins/modules/index.js +0 -9
  259. package/src/plugins/modules/resizing.d.ts +0 -154
  260. package/src/plugins/modules/resizing.js +0 -903
  261. package/src/plugins/submenu/align.d.ts +0 -5
  262. package/src/plugins/submenu/align.js +0 -160
  263. package/src/plugins/submenu/font.d.ts +0 -5
  264. package/src/plugins/submenu/font.js +0 -123
  265. package/src/plugins/submenu/fontColor.d.ts +0 -5
  266. package/src/plugins/submenu/fontColor.js +0 -101
  267. package/src/plugins/submenu/fontSize.d.ts +0 -5
  268. package/src/plugins/submenu/fontSize.js +0 -112
  269. package/src/plugins/submenu/formatBlock.d.ts +0 -5
  270. package/src/plugins/submenu/formatBlock.js +0 -273
  271. package/src/plugins/submenu/hiliteColor.d.ts +0 -5
  272. package/src/plugins/submenu/hiliteColor.js +0 -102
  273. package/src/plugins/submenu/horizontalRule.d.ts +0 -5
  274. package/src/plugins/submenu/horizontalRule.js +0 -98
  275. package/src/plugins/submenu/lineHeight.d.ts +0 -5
  276. package/src/plugins/submenu/lineHeight.js +0 -104
  277. package/src/plugins/submenu/list.d.ts +0 -5
  278. package/src/plugins/submenu/list.js +0 -456
  279. package/src/plugins/submenu/paragraphStyle.d.ts +0 -5
  280. package/src/plugins/submenu/paragraphStyle.js +0 -135
  281. package/src/plugins/submenu/table.d.ts +0 -5
  282. package/src/plugins/submenu/table.js +0 -1431
  283. package/src/plugins/submenu/template.d.ts +0 -5
  284. package/src/plugins/submenu/template.js +0 -72
  285. package/src/plugins/submenu/textStyle.d.ts +0 -5
  286. package/src/plugins/submenu/textStyle.js +0 -167
  287. package/src/suneditor.d.ts +0 -9
  288. package/src/suneditor_build.js +0 -18
  289. /package/{src/plugins/dialog → typings}/mention.d.ts +0 -0
package/src/lib/util.js DELETED
@@ -1,2131 +0,0 @@
1
- /*
2
- * wysiwyg web editor
3
- *
4
- * suneditor.js
5
- * Copyright 2017 JiHong Lee.
6
- * MIT license.
7
- */
8
- 'use strict';
9
-
10
- /**
11
- * @description utility function
12
- */
13
- const util = {
14
- _d: null,
15
- _w: null,
16
- isIE: null,
17
- isIE_Edge: null,
18
- isOSX_IOS: null,
19
- isChromium: null,
20
- isMobile: null,
21
- isResizeObserverSupported: null,
22
- _propertiesInit: function () {
23
- if (this._d) return;
24
- this._d = document;
25
- this._w = window;
26
- this.isIE = navigator.userAgent.indexOf('Trident') > -1;
27
- this.isIE_Edge = (navigator.userAgent.indexOf('Trident') > -1) || (navigator.appVersion.indexOf('Edge') > -1);
28
- this.isOSX_IOS = /(Mac|iPhone|iPod|iPad)/.test(navigator.platform);
29
- this.isChromium = !!window.chrome;
30
- this.isResizeObserverSupported = (typeof ResizeObserver === 'function');
31
- this.isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
32
- },
33
-
34
- _allowedEmptyNodeList: '.se-component, pre, blockquote, hr, li, table, img, iframe, video, audio, canvas',
35
-
36
- /**
37
- * @description HTML Reserved Word Converter.
38
- * @param {String} contents
39
- * @returns {String} HTML string
40
- * @private
41
- */
42
- _HTMLConvertor: function (contents) {
43
- const ec = {'&': '&amp;', '\u00A0': '&nbsp;', '\'': '&apos;', '"': '&quot;', '<': '&lt;', '>': '&gt;'};
44
- return contents.replace(/&|\u00A0|'|"|<|>/g, function (m) {
45
- return (typeof ec[m] === 'string') ? ec[m] : m;
46
- });
47
- },
48
-
49
- /**
50
- * @description Unicode Character 'ZERO WIDTH SPACE' (\u200B)
51
- */
52
- zeroWidthSpace: String.fromCharCode(8203),
53
-
54
- /**
55
- * @description Regular expression to find 'zero width space' (/\u200B/g)
56
- */
57
- zeroWidthRegExp: new RegExp(String.fromCharCode(8203), 'g'),
58
-
59
- /**
60
- * @description Regular expression to find only 'zero width space' (/^\u200B+$/)
61
- */
62
- onlyZeroWidthRegExp: new RegExp('^' + String.fromCharCode(8203) + '+$'),
63
-
64
- fontValueMap: {
65
- 'xx-small': 1,
66
- 'x-small': 2,
67
- 'small': 3,
68
- 'medium': 4,
69
- 'large': 5,
70
- 'x-large': 6,
71
- 'xx-large': 7
72
- },
73
-
74
- /**
75
- * @description A method that checks If the text is blank or to see if it contains 'ZERO WIDTH SPACE' or empty (util.zeroWidthSpace)
76
- * @param {String|Node} text String value or Node
77
- * @returns {Boolean}
78
- */
79
- onlyZeroWidthSpace: function (text) {
80
- if (text === null || text === undefined) return false;
81
- if (typeof text !== 'string') text = text.textContent;
82
- return text === '' || this.onlyZeroWidthRegExp.test(text);
83
- },
84
-
85
- /**
86
- * @description Gets XMLHttpRequest object
87
- * @returns {XMLHttpRequest|ActiveXObject}
88
- */
89
- getXMLHttpRequest: function () {
90
- /** IE */
91
- if (this._w.ActiveXObject) {
92
- try {
93
- return new ActiveXObject('Msxml2.XMLHTTP');
94
- } catch (e) {
95
- try {
96
- return new ActiveXObject('Microsoft.XMLHTTP');
97
- } catch (e1) {
98
- return null;
99
- }
100
- }
101
- }
102
- /** netscape */
103
- else if (this._w.XMLHttpRequest) {
104
- return new XMLHttpRequest();
105
- }
106
- /** fail */
107
- else {
108
- return null;
109
- }
110
- },
111
-
112
- /**
113
- * @description Object.values
114
- * @param {Object|null} obj Object parameter.
115
- * @returns {Array}
116
- */
117
- getValues: function (obj) {
118
- return !obj ? [] : this._w.Object.keys(obj).map(function (i) {
119
- return obj[i];
120
- });
121
- },
122
-
123
- /**
124
- * @description Convert the CamelCase To the KebabCase.
125
- * @param {String|Array} param [Camel string]
126
- * @returns {String|Array}
127
- */
128
- camelToKebabCase: function (param) {
129
- if (typeof param === 'string') {
130
- return param.replace(/[A-Z]/g, function (letter) { return "-" + letter.toLowerCase(); });
131
- } else {
132
- return param.map(function(str) { return util.camelToKebabCase(str); });
133
- }
134
- },
135
-
136
- /**
137
- * @description Convert the KebabCase To the CamelCase.
138
- * @param {String|Array} param [KebabCase string]
139
- * @returns {String|Array}
140
- */
141
- kebabToCamelCase: function (param) {
142
- if (typeof param === 'string') {
143
- return param.replace(/-[a-zA-Z]/g, function (letter) { return letter.replace('-', '').toUpperCase(); });
144
- } else {
145
- return param.map(function(str) { return util.camelToKebabCase(str); });
146
- }
147
- },
148
-
149
- /**
150
- * @description Create Element node
151
- * @param {String} elementName Element name
152
- * @returns {Element}
153
- */
154
- createElement: function (elementName) {
155
- return this._d.createElement(elementName);
156
- },
157
-
158
- /**
159
- * @description Create text node
160
- * @param {String} text text contents
161
- * @returns {Node}
162
- */
163
- createTextNode: function (text) {
164
- return this._d.createTextNode(text || '');
165
- },
166
-
167
- /**
168
- * @description The editor checks tags by string.
169
- * If there is "<" or ">" in the attribute of tag, HTML is broken when checking the tag.
170
- * When using an attribute with "<" or ">", use "HTMLEncoder" to save. (ex: math(katex))
171
- * @param {String} contents HTML or Text string
172
- * @returns {String}
173
- */
174
- HTMLEncoder: function (contents) {
175
- const ec = {'<': '$lt;', '>': '$gt;'};
176
- return contents.replace(/<|>/g, function (m) {
177
- return (typeof ec[m] === 'string') ? ec[m] : m;
178
- });
179
- },
180
-
181
- /**
182
- * @description The editor checks tags by string.
183
- * If there is "<" or ">" in the attribute of tag, HTML is broken when checking the tag.
184
- * Decoder of data stored as "HTMLEncoder" (ex: math(katex))
185
- * @param {String} contents HTML or Text string
186
- * @returns {String}
187
- */
188
- HTMLDecoder: function (contents) {
189
- const ec = {'$lt;': '<', '$gt;': '>'};
190
- return contents.replace(/\$lt;|\$gt;/g, function (m) {
191
- return (typeof ec[m] === 'string') ? ec[m] : m;
192
- });
193
- },
194
-
195
- /**
196
- * @description This method run Object.prototype.hasOwnProperty.call(obj, key)
197
- * @param {Object} obj Object
198
- * @param {String} key obj.key
199
- * @returns {Boolean}
200
- */
201
- hasOwn: function (obj, key) {
202
- return this._hasOwn.call(obj, key);
203
- },
204
- _hasOwn: Object.prototype.hasOwnProperty,
205
-
206
- /**
207
- * @deprecated
208
- * @description Get the the tag path of the arguments value
209
- * If not found, return the first found value
210
- * @param {Array} nameArray File name array
211
- * @param {String} extension js, css
212
- * @returns {String}
213
- */
214
- getIncludePath: function (nameArray, extension) {
215
- let path = '';
216
- const pathList = [];
217
- const tagName = extension === 'js' ? 'script' : 'link';
218
- const src = extension === 'js' ? 'src' : 'href';
219
-
220
- let fileName = '(?:';
221
- for (let i = 0, len = nameArray.length; i < len; i++) {
222
- fileName += nameArray[i] + (i < len - 1 ? '|' : ')');
223
- }
224
-
225
- const regExp = new this._w.RegExp('(^|.*[\\/])' + fileName + '(\\.[^\\/]+)?\.' + extension + '(?:\\?.*|;.*)?$', 'i');
226
- const extRegExp = new this._w.RegExp('.+\\.' + extension + '(?:\\?.*|;.*)?$', 'i');
227
-
228
- for (let c = this._d.getElementsByTagName(tagName), i = 0; i < c.length; i++) {
229
- if (extRegExp.test(c[i][src])) {
230
- pathList.push(c[i]);
231
- }
232
- }
233
-
234
- for (let i = 0; i < pathList.length; i++) {
235
- let editorTag = pathList[i][src].match(regExp);
236
- if (editorTag) {
237
- path = editorTag[0];
238
- break;
239
- }
240
- }
241
-
242
- if (path === '') path = pathList.length > 0 ? pathList[0][src] : '';
243
-
244
- -1 === path.indexOf(':/') && '//' !== path.slice(0, 2) && (path = 0 === path.indexOf('/') ? location.href.match(/^.*?:\/\/[^\/]*/)[0] + path : location.href.match(/^[^\?]*\/(?:)/)[0] + path);
245
-
246
- if (!path) throw '[SUNEDITOR.util.getIncludePath.fail] The SUNEDITOR installation path could not be automatically detected. (name: +' + name + ', extension: ' + extension + ')';
247
-
248
- return path;
249
- },
250
-
251
- /**
252
- * @deprecated
253
- * @description Returns the CSS text that has been applied to the current page.
254
- * @param {Document|null} doc To get the CSS text of an document(core._wd). If null get the current document.
255
- * @returns {String} Styles string
256
- */
257
- getPageStyle: function (doc) {
258
- let cssText = '';
259
- const sheets = (doc || this._d).styleSheets;
260
-
261
- for (let i = 0, len = sheets.length, rules; i < len; i++) {
262
- try {
263
- rules = sheets[i].cssRules;
264
- } catch (e) {
265
- continue;
266
- }
267
-
268
- if (rules) {
269
- for (let c = 0, cLen = rules.length; c < cLen; c++) {
270
- cssText += rules[c].cssText;
271
- }
272
- }
273
- }
274
-
275
- return cssText;
276
- },
277
-
278
- /**
279
- * @description Get the argument iframe's document object
280
- * @param {Element} iframe Iframe element (context.element.wysiwygFrame)
281
- * @returns {Document}
282
- */
283
- getIframeDocument: function (iframe) {
284
- let wDocument = iframe.contentWindow || iframe.contentDocument;
285
- if (wDocument.document) wDocument = wDocument.document;
286
- return wDocument;
287
- },
288
-
289
- /**
290
- * @description Get attributes of argument element to string ('class="---" name="---" ')
291
- * @param {Element} element Element object
292
- * @param {Array|null} exceptAttrs Array of attribute names to exclude from the result
293
- * @returns {String}
294
- */
295
- getAttributesToString: function (element, exceptAttrs) {
296
- if (!element.attributes) return '';
297
-
298
- const attrs = element.attributes;
299
- let attrString = '';
300
-
301
- for (let i = 0, len = attrs.length; i < len; i++) {
302
- if (exceptAttrs && exceptAttrs.indexOf(attrs[i].name) > -1) continue;
303
- attrString += attrs[i].name + '="' + attrs[i].value + '" ';
304
- }
305
-
306
- return attrString;
307
- },
308
-
309
- /**
310
- * @descriptionGets Get the length in bytes of a string.
311
- * referencing code: "https://github.com/shaan1974/myrdin/blob/master/expressions/string.js#L11"
312
- * @param {String} text String text
313
- * @returns {Number}
314
- */
315
- getByteLength: function(text) {
316
- if (!text || !text.toString) return 0;
317
- text = text.toString();
318
-
319
- const encoder = this._w.encodeURIComponent;
320
- let cr, cl;
321
- if (this.isIE_Edge) {
322
- cl = this._w.unescape(encoder(text)).length;
323
- cr = 0;
324
-
325
- if (encoder(text).match(/(%0A|%0D)/gi) !== null) {
326
- cr = encoder(text).match(/(%0A|%0D)/gi).length;
327
- }
328
-
329
- return cl + cr;
330
- } else {
331
- cl = (new this._w.TextEncoder('utf-8').encode(text)).length;
332
- cr = 0;
333
-
334
- if (encoder(text).match(/(%0A|%0D)/gi) !== null) {
335
- cr = encoder(text).match(/(%0A|%0D)/gi).length;
336
- }
337
-
338
- return cl + cr;
339
- }
340
- },
341
-
342
- /**
343
- * @description It is judged whether it is the edit region top div element or iframe's body tag.
344
- * @param {Node} element The node to check
345
- * @returns {Boolean}
346
- */
347
- isWysiwygDiv: function (element) {
348
- return element && element.nodeType === 1 && (this.hasClass(element, 'se-wrapper-wysiwyg') || /^BODY$/i.test(element.nodeName));
349
- },
350
-
351
- /**
352
- * @description It is judged whether it is the contenteditable property is false.
353
- * @param {Node} element The node to check
354
- * @returns {Boolean}
355
- */
356
- isNonEditable: function (element) {
357
- return element && element.nodeType === 1 && element.getAttribute('contenteditable') === 'false';
358
- },
359
-
360
- /**
361
- * @description It is judged whether it is a node related to the text style.
362
- * (strong|span|font|b|var|i|em|u|ins|s|strike|del|sub|sup|mark|a|label|code)
363
- * @param {Node} element The node to check
364
- * @returns {Boolean}
365
- */
366
- isTextStyleElement: function (element) {
367
- return element && element.nodeType !== 3 && /^(strong|span|font|b|var|i|em|u|ins|s|strike|del|sub|sup|mark|a|label|code|summary)$/i.test(element.nodeName);
368
- },
369
-
370
- /**
371
- * @description It is judged whether it is the input element (INPUT, TEXTAREA)
372
- * @param {Node} element The node to check
373
- * @returns
374
- */
375
- isInputElement: function (element) {
376
- return element && element.nodeType === 1 && /^(INPUT|TEXTAREA)$/i.test(element.nodeName);
377
- },
378
-
379
- /**
380
- * @description It is judged whether it is the format element (P, DIV, H[1-6], PRE, LI | class="__se__format__replace_xxx")
381
- * Format element also contain "free format Element"
382
- * @param {Node} element The node to check
383
- * @returns {Boolean}
384
- */
385
- isFormatElement: function (element) {
386
- return element && element.nodeType === 1 && (/^(P|DIV|H[1-6]|PRE|LI|TH|TD|DETAILS)$/i.test(element.nodeName) || this.hasClass(element, '(\\s|^)__se__format__replace_.+(\\s|$)|(\\s|^)__se__format__free_.+(\\s|$)')) && !this.isComponent(element) && !this.isWysiwygDiv(element);
387
- },
388
-
389
- /**
390
- * @description It is judged whether it is the range format element. (BLOCKQUOTE, OL, UL, FIGCAPTION, TABLE, THEAD, TBODY, TR, TH, TD | class="__se__format__range_xxx")
391
- * Range format element is wrap the "format element" and "component"
392
- * @param {Node} element The node to check
393
- * @returns {Boolean}
394
- */
395
- isRangeFormatElement: function (element) {
396
- return element && element.nodeType === 1 && (/^(BLOCKQUOTE|OL|UL|FIGCAPTION|TABLE|THEAD|TBODY|TR|TH|TD|DETAILS)$/i.test(element.nodeName) || this.hasClass(element, '(\\s|^)__se__format__range_.+(\\s|$)'));
397
- },
398
-
399
- /**
400
- * @description It is judged whether it is the closure range format element. (TH, TD | class="__se__format__range__closure_xxx")
401
- * Closure range format elements is included in the range format element.
402
- * - Closure range format element is wrap the "format element" and "component"
403
- * ※ You cannot exit this format with the Enter key or Backspace key.
404
- * ※ Use it only in special cases. ([ex] format of table cells)
405
- * @param {Node} element The node to check
406
- * @returns {Boolean}
407
- */
408
- isClosureRangeFormatElement: function (element) {
409
- return element && element.nodeType === 1 && (/^(TH|TD)$/i.test(element.nodeName) || this.hasClass(element, '(\\s|^)__se__format__range__closure_.+(\\s|$)'));
410
- },
411
-
412
- /**
413
- * @description It is judged whether it is the free format element. (PRE | class="__se__format__free_xxx")
414
- * Free format elements is included in the format element.
415
- * Free format elements's line break is "BR" tag.
416
- * ※ Entering the Enter key in the space on the last line ends "Free Format" and appends "Format".
417
- * @param {Node} element The node to check
418
- * @returns {Boolean}
419
- */
420
- isFreeFormatElement: function (element) {
421
- return element && element.nodeType === 1 && (/^PRE$/i.test(element.nodeName) || this.hasClass(element, '(\\s|^)__se__format__free_.+(\\s|$)')) && !this.isComponent(element) && !this.isWysiwygDiv(element);
422
- },
423
-
424
- /**
425
- * @description It is judged whether it is the closure free format element. (class="__se__format__free__closure_xxx")
426
- * Closure free format elements is included in the free format element.
427
- * - Closure free format elements's line break is "BR" tag.
428
- * ※ You cannot exit this format with the Enter key or Backspace key.
429
- * ※ Use it only in special cases. ([ex] format of table cells)
430
- * @param {Node} element The node to check
431
- * @returns {Boolean}
432
- */
433
- isClosureFreeFormatElement: function (element) {
434
- return element && element.nodeType === 1 && this.hasClass(element, '(\\s|^)__se__format__free__closure_.+(\\s|$)');
435
- },
436
-
437
- /**
438
- * @description It is judged whether it is the component[img, iframe, video, audio, table] cover(class="se-component") and table, hr
439
- * @param {Node} element The node to check
440
- * @returns {Boolean}
441
- */
442
- isComponent: function (element) {
443
- return element && (/se-component/.test(element.className) || /^(TABLE|HR)$/.test(element.nodeName));
444
- },
445
-
446
- /**
447
- * @description Checks for "__se__uneditable" in the class list.
448
- * Components with class "__se__uneditable" cannot be modified.
449
- * @param {Element} element The element to check
450
- * @returns {Boolean}
451
- */
452
- isUneditableComponent: function (element) {
453
- return element && this.hasClass(element, '__se__uneditable');
454
- },
455
-
456
- /**
457
- * @description It is judged whether it is the component [img, iframe] cover(class="se-component")
458
- * @param {Node} element The node to check
459
- * @returns {Boolean}
460
- */
461
- isMediaComponent: function (element) {
462
- return element && /se-component/.test(element.className);
463
- },
464
-
465
- /**
466
- * @description It is judged whether it is the not checking node. (class="katex", "__se__tag")
467
- * @param {Node} element The node to check
468
- * @returns {Boolean}
469
- */
470
- isNotCheckingNode: function (element) {
471
- return element && /katex|__se__tag/.test(element.className);
472
- },
473
-
474
- /**
475
- * @description If a parent node that contains an argument node finds a format node (util.isFormatElement), it returns that node.
476
- * @param {Node} element Reference node.
477
- * @param {Function|null} validation Additional validation function.
478
- * @returns {Element|null}
479
- */
480
- getFormatElement: function (element, validation) {
481
- if (!element) return null;
482
- if (!validation) {
483
- validation = function () { return true; };
484
- }
485
-
486
- while (element) {
487
- if (this.isWysiwygDiv(element)) return null;
488
- if (this.isRangeFormatElement(element)) element.firstElementChild;
489
- if (this.isFormatElement(element) && validation(element)) return element;
490
-
491
- element = element.parentNode;
492
- }
493
-
494
- return null;
495
- },
496
-
497
- /**
498
- * @description If a parent node that contains an argument node finds a format node (util.isRangeFormatElement), it returns that node.
499
- * @param {Node} element Reference node.
500
- * @param {Function|null} validation Additional validation function.
501
- * @returns {Element|null}
502
- */
503
- getRangeFormatElement: function (element, validation) {
504
- if (!element) return null;
505
- if (!validation) {
506
- validation = function () { return true; };
507
- }
508
-
509
- while (element) {
510
- if (this.isWysiwygDiv(element)) return null;
511
- if (this.isRangeFormatElement(element) && !/^(THEAD|TBODY|TR)$/i.test(element.nodeName) && validation(element)) return element;
512
- element = element.parentNode;
513
- }
514
-
515
- return null;
516
- },
517
-
518
- /**
519
- * @description If a parent node that contains an argument node finds a free format node (util.isFreeFormatElement), it returns that node.
520
- * @param {Node} element Reference node.
521
- * @param {Function|null} validation Additional validation function.
522
- * @returns {Element|null}
523
- */
524
- getFreeFormatElement: function (element, validation) {
525
- if (!element) return null;
526
- if (!validation) {
527
- validation = function () { return true; };
528
- }
529
-
530
- while (element) {
531
- if (this.isWysiwygDiv(element)) return null;
532
- if (this.isFreeFormatElement(element) && validation(element)) return element;
533
-
534
- element = element.parentNode;
535
- }
536
-
537
- return null;
538
- },
539
-
540
- /**
541
- * @description If a parent node that contains an argument node finds a closure free format node (util.isClosureFreeFormatElement), it returns that node.
542
- * @param {Node} element Reference node.
543
- * @param {Function|null} validation Additional validation function.
544
- * @returns {Element|null}
545
- */
546
- getClosureFreeFormatElement: function (element, validation) {
547
- if (!element) return null;
548
- if (!validation) {
549
- validation = function () { return true; };
550
- }
551
-
552
- while (element) {
553
- if (this.isWysiwygDiv(element)) return null;
554
- if (this.isClosureFreeFormatElement(element) && validation(element)) return element;
555
-
556
- element = element.parentNode;
557
- }
558
-
559
- return null;
560
- },
561
-
562
- /**
563
- * @description Add style and className of copyEl to originEl
564
- * @param {Element} originEl Origin element
565
- * @param {Element} copyEl Element to copy
566
- * @param {Array|null} blacklist Blacklist array(LowerCase)
567
- */
568
- copyTagAttributes: function (originEl, copyEl, blacklist) {
569
- if (copyEl.style.cssText) {
570
- const copyStyles = copyEl.style;
571
- for (let i = 0, len = copyStyles.length; i < len; i++) {
572
- originEl.style[copyStyles[i]] = copyStyles[copyStyles[i]];
573
- }
574
- }
575
-
576
- const attrs = copyEl.attributes;
577
- for (let i = 0, len = attrs.length, name; i < len; i++) {
578
- name = attrs[i].name.toLowerCase();
579
- if ((blacklist && blacklist.indexOf(name) > -1) || !attrs[i].value) originEl.removeAttribute(name);
580
- else if (name !== 'style') originEl.setAttribute(attrs[i].name, attrs[i].value);
581
- }
582
- },
583
-
584
- /**
585
- * @description Copy and apply attributes of format tag that should be maintained. (style, class) Ignore "__se__format__" class
586
- * @param {Element} originEl Origin element
587
- * @param {Element} copyEl Element to copy
588
- */
589
- copyFormatAttributes: function (originEl, copyEl) {
590
- copyEl = copyEl.cloneNode(false);
591
- copyEl.className = copyEl.className.replace(/(\s|^)__se__format__[^\s]+/g, '');
592
- this.copyTagAttributes(originEl, copyEl);
593
- },
594
-
595
- /**
596
- * @description Get the item from the array that matches the condition.
597
- * @param {Array|HTMLCollection|NodeList} array Array to get item
598
- * @param {Function|null} validation Conditional function
599
- * @param {Boolean} multi If true, returns all items that meet the criteria otherwise, returns an empty array.
600
- * If false, returns only one item that meet the criteria otherwise return null.
601
- * @returns {Array|Node|null}
602
- */
603
- getArrayItem: function (array, validation, multi) {
604
- if (!array || array.length === 0) return null;
605
-
606
- validation = validation || function () { return true; };
607
- const arr = [];
608
-
609
- for (let i = 0, len = array.length, a; i < len; i++) {
610
- a = array[i];
611
- if (validation(a)) {
612
- if (!multi) return a;
613
- else arr.push(a);
614
- }
615
- }
616
-
617
- return !multi ? null : arr;
618
- },
619
-
620
- /**
621
- * @description Check if an array contains an element
622
- * @param {Array|HTMLCollection|NodeList} array element array
623
- * @param {Node} element The element to check for
624
- * @returns {Boolean}
625
- */
626
- arrayIncludes: function(array, element) {
627
- for (let i = 0; i < array.length; i++) {
628
- if (array[i] === element) {
629
- return true;
630
- }
631
- }
632
- return false;
633
- },
634
-
635
- /**
636
- * @description Get the index of the argument value in the element array
637
- * @param {Array|HTMLCollection|NodeList} array element array
638
- * @param {Node} element The element to find index
639
- * @returns {Number}
640
- */
641
- getArrayIndex: function (array, element) {
642
- let idx = -1;
643
- for (let i = 0, len = array.length; i < len; i++) {
644
- if (array[i] === element) {
645
- idx = i;
646
- break;
647
- }
648
- }
649
-
650
- return idx;
651
- },
652
-
653
- /**
654
- * @description Get the next index of the argument value in the element array
655
- * @param {Array|HTMLCollection|NodeList} array element array
656
- * @param {Node} item The element to find index
657
- * @returns {Number}
658
- */
659
- nextIdx: function (array, item) {
660
- let idx = this.getArrayIndex(array, item);
661
- if (idx === -1) return -1;
662
- return idx + 1;
663
- },
664
-
665
- /**
666
- * @description Get the previous index of the argument value in the element array
667
- * @param {Array|HTMLCollection|NodeList} array Element array
668
- * @param {Node} item The element to find index
669
- * @returns {Number}
670
- */
671
- prevIdx: function (array, item) {
672
- let idx = this.getArrayIndex(array, item);
673
- if (idx === -1) return -1;
674
- return idx - 1;
675
- },
676
-
677
- /**
678
- * @description Returns the index compared to other sibling nodes.
679
- * @param {Node} node The Node to find index
680
- * @returns {Number}
681
- */
682
- getPositionIndex: function (node) {
683
- let idx = 0;
684
- while ((node = node.previousSibling)) {
685
- idx += 1;
686
- }
687
- return idx;
688
- },
689
-
690
- /**
691
- * @description Returns the position of the "node" in the "parentNode" in a numerical array.
692
- * ex) <p><span>aa</span><span>bb</span></p> : getNodePath(node: "bb", parentNode: "<P>") -> [1, 0]
693
- * @param {Node} node The Node to find position path
694
- * @param {Node|null} parentNode Parent node. If null, wysiwyg div area
695
- * @param {Object|null} _newOffsets If you send an object of the form "{s: 0, e: 0}", the text nodes that are attached together are merged into one, centered on the "node" argument.
696
- * "_newOffsets.s" stores the length of the combined characters after "node" and "_newOffsets.e" stores the length of the combined characters before "node".
697
- * Do not use unless absolutely necessary.
698
- * @returns {Array}
699
- */
700
- getNodePath: function (node, parentNode, _newOffsets) {
701
- const path = [];
702
- let finds = true;
703
-
704
- this.getParentElement(node, function (el) {
705
- if (el === parentNode) finds = false;
706
- if (finds && !this.isWysiwygDiv(el)) {
707
- // merge text nodes
708
- if (_newOffsets && el.nodeType === 3) {
709
- let temp = null, tempText = null;
710
- _newOffsets.s = _newOffsets.e = 0;
711
-
712
- let previous = el.previousSibling;
713
- while (previous && previous.nodeType === 3) {
714
- tempText = previous.textContent.replace(this.zeroWidthRegExp, '');
715
- _newOffsets.s += tempText.length;
716
- el.textContent = tempText + el.textContent;
717
- temp = previous;
718
- previous = previous.previousSibling;
719
- this.removeItem(temp);
720
- }
721
-
722
- let next = el.nextSibling;
723
- while (next && next.nodeType === 3) {
724
- tempText = next.textContent.replace(this.zeroWidthRegExp, '');
725
- _newOffsets.e += tempText.length;
726
- el.textContent += tempText;
727
- temp = next;
728
- next = next.nextSibling;
729
- this.removeItem(temp);
730
- }
731
- }
732
-
733
- // index push
734
- path.push(el);
735
- }
736
- return false;
737
- }.bind(this));
738
-
739
- return path.map(this.getPositionIndex).reverse();
740
- },
741
-
742
- /**
743
- * @description Returns the node in the location of the path array obtained from "util.getNodePath".
744
- * @param {Array} offsets Position array, array obtained from "util.getNodePath"
745
- * @param {Node} parentNode Base parent element
746
- * @returns {Node}
747
- */
748
- getNodeFromPath: function (offsets, parentNode) {
749
- let current = parentNode;
750
- let nodes;
751
-
752
- for (let i = 0, len = offsets.length; i < len; i++) {
753
- nodes = current.childNodes;
754
- if (nodes.length === 0) break;
755
- if (nodes.length <= offsets[i]) {
756
- current = nodes[nodes.length - 1];
757
- } else {
758
- current = nodes[offsets[i]];
759
- }
760
- }
761
-
762
- return current;
763
- },
764
-
765
- /**
766
- * @description Compares the style and class for equal values.
767
- * Returns true if both are text nodes.
768
- * @param {Node} a Node to compare
769
- * @param {Node} b Node to compare
770
- * @returns {Boolean}
771
- */
772
- isSameAttributes: function (a, b) {
773
- if (a.nodeType === 3 && b.nodeType === 3) return true;
774
- if (a.nodeType === 3 || b.nodeType === 3) return false;
775
-
776
- const style_a = a.style;
777
- const style_b = b.style;
778
- let compStyle = 0;
779
-
780
- for (let i = 0, len = style_a.length; i < len; i++) {
781
- if (style_a[style_a[i]] === style_b[style_a[i]]) compStyle++;
782
- }
783
-
784
- const class_a = a.classList;
785
- const class_b = b.classList;
786
- const reg = this._w.RegExp;
787
- let compClass = 0;
788
-
789
- for (let i = 0, len = class_a.length; i < len; i++) {
790
- if (reg('(\s|^)' + class_a[i] + '(\s|$)').test(class_b.value)) compClass++;
791
- }
792
-
793
- return (compStyle === style_b.length && compStyle === style_a.length) && (compClass === class_b.length && compClass === class_a.length);
794
- },
795
-
796
- /**
797
- * @description Check the line element(util.isFormatElement) is empty.
798
- * @param {Element} element Format element node
799
- * @returns {Boolean}
800
- */
801
- isEmptyLine: function (element) {
802
- return !element || !element.parentNode || (!element.querySelector('IMG, IFRAME, AUDIO, VIDEO, CANVAS, TABLE') && element.children.length === 0 && this.onlyZeroWidthSpace(element.textContent));
803
- },
804
-
805
- /**
806
- * @description Check the span's attributes are empty.
807
- * @param {Element|null} element Element node
808
- * @returns {Boolean}
809
- */
810
- isSpanWithoutAttr: function (element) {
811
- return !!element && element.nodeType === 1 && /^SPAN$/i.test(element.nodeName) && !element.className && !element.style.cssText;
812
- },
813
-
814
- /**
815
- * @description Check the node is a list (ol, ul)
816
- * @param {Node|String} node The element or element name to check
817
- * @returns {Boolean}
818
- */
819
- isList: function (node) {
820
- return node && /^(OL|UL)$/i.test(typeof node === 'string' ? node : node.nodeName);
821
- },
822
-
823
- /**
824
- * @description Check the node is a list cell (li)
825
- * @param {Node|String} node The element or element name to check
826
- * @returns {Boolean}
827
- */
828
- isListCell: function (node) {
829
- return node && /^LI$/i.test(typeof node === 'string' ? node : node.nodeName);
830
- },
831
-
832
- /**
833
- * @description Check the node is a table (table, thead, tbody, tr, th, td)
834
- * @param {Node|String} node The element or element name to check
835
- * @returns {Boolean}
836
- */
837
- isTable: function (node) {
838
- return node && /^(TABLE|THEAD|TBODY|TR|TH|TD)$/i.test(typeof node === 'string' ? node : node.nodeName);
839
- },
840
-
841
- /**
842
- * @description Check the node is a table cell (td, th)
843
- * @param {Node|String} node The element or element name to check
844
- * @returns {Boolean}
845
- */
846
- isCell: function (node) {
847
- return node && /^(TD|TH)$/i.test(typeof node === 'string' ? node : node.nodeName);
848
- },
849
-
850
- /**
851
- * @description Check the node is a break node (BR)
852
- * @param {Node|String} node The element or element name to check
853
- * @returns {Boolean}
854
- */
855
- isBreak: function (node) {
856
- return node && /^BR$/i.test(typeof node === 'string' ? node : node.nodeName);
857
- },
858
-
859
-
860
- /**
861
- * @description Check the node is a anchor node (A)
862
- * @param {Node|String} node The element or element name to check
863
- * @returns {Boolean}
864
- */
865
- isAnchor: function (node) {
866
- return node && /^A$/i.test(typeof node === 'string' ? node : node.nodeName);
867
- },
868
-
869
- /**
870
- * @description Check the node is a media node (img, iframe, audio, video, canvas)
871
- * @param {Node|String} node The element or element name to check
872
- * @returns {Boolean}
873
- */
874
- isMedia: function (node) {
875
- return node && /^(IMG|IFRAME|AUDIO|VIDEO|CANVAS)$/i.test(typeof node === 'string' ? node : node.nodeName);
876
- },
877
-
878
- /**
879
- * @description Check the node is a figure tag or util.isMedia()
880
- * @param {Node|String} node The element or element name to check
881
- * @returns {Boolean}
882
- */
883
- isFigures: function (node) {
884
- return node && (this.isMedia(node) || /^(FIGURE)$/i.test(typeof node === 'string' ? node : node.nodeName));
885
- },
886
-
887
- /**
888
- * @description Checks for numeric (with decimal point).
889
- * @param {String|Number} text Text string or number
890
- * @returns {Boolean}
891
- */
892
- isNumber: function (text) {
893
- return !!text && /^-?\d+(\.\d+)?$/.test(text + '');
894
- },
895
-
896
- /**
897
- * @description Get a number.
898
- * @param {String|Number} text Text string or number
899
- * @param {Number} maxDec Maximum number of decimal places (-1 : Infinity)
900
- * @returns {Number}
901
- */
902
- getNumber: function (text, maxDec) {
903
- if (!text) return 0;
904
-
905
- let number = (text + '').match(/-?\d+(\.\d+)?/);
906
- if (!number || !number[0]) return 0;
907
-
908
- number = number[0];
909
- return maxDec < 0 ? number * 1 : maxDec === 0 ? this._w.Math.round(number * 1) : (number * 1).toFixed(maxDec) * 1;
910
- },
911
-
912
- /**
913
- * @description Get all "children" of the argument value element (Without text nodes)
914
- * @param {Element} element element to get child node
915
- * @param {Function|null} validation Conditional function
916
- * @returns {Array}
917
- */
918
- getListChildren: function (element, validation) {
919
- const children = [];
920
- if (!element || !element.children || element.children.length === 0) return children;
921
-
922
- validation = validation || function () { return true; };
923
-
924
- (function recursionFunc(current) {
925
- if (element !== current && validation(current)) {
926
- children.push(current);
927
- }
928
-
929
- if (!!current.children) {
930
- for (let i = 0, len = current.children.length; i < len; i++) {
931
- recursionFunc(current.children[i]);
932
- }
933
- }
934
- })(element);
935
-
936
- return children;
937
- },
938
-
939
- /**
940
- * @description Get all "childNodes" of the argument value element (Include text nodes)
941
- * @param {Node} element element to get child node
942
- * @param {Function|null} validation Conditional function
943
- * @returns {Array}
944
- */
945
- getListChildNodes: function (element, validation) {
946
- const children = [];
947
- if (!element || element.childNodes.length === 0) return children;
948
-
949
- validation = validation || function () { return true; };
950
-
951
- (function recursionFunc(current) {
952
- if (element !== current && validation(current)) {
953
- children.push(current);
954
- }
955
-
956
- for (let i = 0, len = current.childNodes.length; i < len; i++) {
957
- recursionFunc(current.childNodes[i]);
958
- }
959
- })(element);
960
-
961
- return children;
962
- },
963
-
964
- /**
965
- * @description Returns the number of parents nodes.
966
- * "0" when the parent node is the WYSIWYG area.
967
- * "-1" when the element argument is the WYSIWYG area.
968
- * @param {Node} element The element to check
969
- * @returns {Number}
970
- */
971
- getElementDepth: function (element) {
972
- if (!element || this.isWysiwygDiv(element)) return -1;
973
-
974
- let depth = 0;
975
- element = element.parentNode;
976
-
977
- while (element && !this.isWysiwygDiv(element)) {
978
- depth += 1;
979
- element = element.parentNode;
980
- }
981
-
982
- return depth;
983
- },
984
-
985
- /**
986
- * @description Compares two elements to find a common ancestor, and returns the order of the two elements.
987
- * @param {Node} a Node to compare.
988
- * @param {Node} b Node to compare.
989
- * @returns {Object} { ancesstor, a, b, result: (a > b ? 1 : a < b ? -1 : 0) };
990
- */
991
- compareElements: function (a, b) {
992
- let aNode = a, bNode = b;
993
- while (aNode && bNode && aNode.parentNode !== bNode.parentNode) {
994
- aNode = aNode.parentNode;
995
- bNode = bNode.parentNode;
996
- }
997
-
998
- if (!aNode || !bNode) return { ancestor: null, a: a, b: b, result: 0 };
999
-
1000
- const children = aNode.parentNode.childNodes;
1001
- const aIndex = this.getArrayIndex(children, aNode);
1002
- const bIndex = this.getArrayIndex(children, bNode);
1003
-
1004
- return {
1005
- ancestor: aNode.parentNode,
1006
- a: aNode,
1007
- b: bNode,
1008
- result: aIndex > bIndex ? 1 : aIndex < bIndex ? -1 : 0
1009
- };
1010
- },
1011
-
1012
- /**
1013
- * @description Get the parent element of the argument value.
1014
- * A tag that satisfies the query condition is imported.
1015
- * Returns null if not found.
1016
- * @param {Node} element Reference element
1017
- * @param {String|Function} query Query String (nodeName, .className, #ID, :name) or validation function.
1018
- * Not use it like jquery.
1019
- * Only one condition can be entered at a time.
1020
- * @returns {Element|null}
1021
- */
1022
- getParentElement: function (element, query) {
1023
- let check;
1024
-
1025
- if (typeof query === 'function') {
1026
- check = query;
1027
- } else {
1028
- let attr;
1029
- if (/^\./.test(query)) {
1030
- attr = 'className';
1031
- query = query.split('.')[1];
1032
- } else if (/^#/.test(query)) {
1033
- attr = 'id';
1034
- query = '^' + query.split('#')[1] + '$';
1035
- } else if (/^:/.test(query)) {
1036
- attr = 'name';
1037
- query = '^' + query.split(':')[1] + '$';
1038
- } else {
1039
- attr = 'nodeName';
1040
- query = '^' + query + '$';
1041
- }
1042
-
1043
- const regExp = new this._w.RegExp(query, 'i');
1044
- check = function (el) {
1045
- return regExp.test(el[attr]);
1046
- };
1047
- }
1048
-
1049
- while (element && !check(element)) {
1050
- if (this.isWysiwygDiv(element)) {
1051
- return null;
1052
- }
1053
- element = element.parentNode;
1054
- }
1055
-
1056
- return element;
1057
- },
1058
-
1059
- /**
1060
- * @description Gets the previous sibling last child. If there is no sibling, then it'll take it from the closest ancestor with child
1061
- * Returns null if not found.
1062
- * @param {Node} node Reference element
1063
- * @param {Node|null} ceiling Highest boundary allowed
1064
- * @returns {Node|null}
1065
- */
1066
- getPreviousDeepestNode: function (node, ceiling) {
1067
- let previousNode = node.previousSibling;
1068
- if (!previousNode) {
1069
- for (let parentNode = node.parentNode; parentNode; parentNode = parentNode.parentNode) {
1070
- if (parentNode === ceiling) return null;
1071
- if (parentNode.previousSibling) {
1072
- previousNode = parentNode.previousSibling;
1073
- break;
1074
- }
1075
- }
1076
- if (!previousNode) return null;
1077
- }
1078
- while (previousNode.lastChild) previousNode = previousNode.lastChild;
1079
-
1080
- return previousNode;
1081
- },
1082
-
1083
- /**
1084
- * @description Gets the next sibling first child. If there is no sibling, then it'll take it from the closest ancestor with child
1085
- * Returns null if not found.
1086
- * @param {Node} node Reference element
1087
- * @param {Node|null} ceiling Highest boundary allowed
1088
- * @returns {Node|null}
1089
- */
1090
- getNextDeepestNode: function (node, ceiling) {
1091
- let nextNode = node.nextSibling;
1092
- if (!nextNode) {
1093
- for (let parentNode = node.parentNode; parentNode; parentNode = parentNode.parentNode) {
1094
- if (parentNode === ceiling) return null;
1095
- if (parentNode.nextSibling) {
1096
- nextNode = parentNode.nextSibling;
1097
- break;
1098
- }
1099
- }
1100
- if (!nextNode) return null;
1101
- }
1102
- while (nextNode.firstChild) nextNode = nextNode.firstChild;
1103
-
1104
- return nextNode;
1105
- },
1106
-
1107
- /**
1108
- * @description Get the child element of the argument value.
1109
- * A tag that satisfies the query condition is imported.
1110
- * Returns null if not found.
1111
- * @param {Node} element Reference element
1112
- * @param {String|Function} query Query String (nodeName, .className, #ID, :name) or validation function.
1113
- * @param {Boolean} last If true returns the last node among the found child nodes. (default: first node)
1114
- * Not use it like jquery.
1115
- * Only one condition can be entered at a time.
1116
- * @returns {Element|null}
1117
- */
1118
- getChildElement: function (element, query, last) {
1119
- let check;
1120
-
1121
- if (typeof query === 'function') {
1122
- check = query;
1123
- } else {
1124
- let attr;
1125
- if (/^\./.test(query)) {
1126
- attr = 'className';
1127
- query = query.split('.')[1];
1128
- } else if (/^#/.test(query)) {
1129
- attr = 'id';
1130
- query = '^' + query.split('#')[1] + '$';
1131
- } else if (/^:/.test(query)) {
1132
- attr = 'name';
1133
- query = '^' + query.split(':')[1] + '$';
1134
- } else {
1135
- attr = 'nodeName';
1136
- query = '^' + (query === 'text' ? '#' + query : query) + '$';
1137
- }
1138
-
1139
- const regExp = new this._w.RegExp(query, 'i');
1140
- check = function (el) {
1141
- return regExp.test(el[attr]);
1142
- };
1143
- }
1144
-
1145
- const childList = this.getListChildNodes(element, function (current) {
1146
- return check(current);
1147
- });
1148
-
1149
- return childList[last ? childList.length - 1 : 0];
1150
- },
1151
-
1152
- /**
1153
- * @description 1. The first node of all the child nodes of the "first" element is returned.
1154
- * 2. The last node of all the child nodes of the "last" element is returned.
1155
- * 3. When there is no "last" element, the first and last nodes of all the children of the "first" element are returned.
1156
- * { sc: "first", ec: "last" }
1157
- * @param {Node} first First element
1158
- * @param {Node|null} last Last element
1159
- * @returns {Object}
1160
- */
1161
- getEdgeChildNodes: function (first, last) {
1162
- if (!first) return;
1163
- if (!last) last = first;
1164
-
1165
- while (first && first.nodeType === 1 && first.childNodes.length > 0 && !this.isBreak(first)) first = first.firstChild;
1166
- while (last && last.nodeType === 1 && last.childNodes.length > 0 && !this.isBreak(last)) last = last.lastChild;
1167
-
1168
- return {
1169
- sc: first,
1170
- ec: last || first
1171
- };
1172
- },
1173
-
1174
- /**
1175
- * @description Returns the position of the left and top of argument. {left:0, top:0}
1176
- * @param {Node} element Target node
1177
- * @param {Element|null} wysiwygFrame When use iframe option, iframe object should be sent (context.element.wysiwygFrame)
1178
- * @returns {Object}
1179
- */
1180
- getOffset: function (element, wysiwygFrame) {
1181
- let offsetLeft = 0;
1182
- let offsetTop = 0;
1183
- let offsetElement = element.nodeType === 3 ? element.parentElement : element;
1184
- const wysiwyg = this.getParentElement(element, this.isWysiwygDiv.bind(this));
1185
-
1186
- while (offsetElement && !this.hasClass(offsetElement, 'se-container') && offsetElement !== wysiwyg) {
1187
- offsetLeft += offsetElement.offsetLeft;
1188
- offsetTop += offsetElement.offsetTop;
1189
- offsetElement = offsetElement.offsetParent;
1190
- }
1191
-
1192
- const iframe = wysiwygFrame && /iframe/i.test(wysiwygFrame.nodeName);
1193
-
1194
- return {
1195
- left: offsetLeft + (iframe ? wysiwygFrame.parentElement.offsetLeft : 0),
1196
- top: (offsetTop - (wysiwyg ? wysiwyg.scrollTop : 0)) + (iframe ? wysiwygFrame.parentElement.offsetTop : 0)
1197
- };
1198
- },
1199
-
1200
- /**
1201
- * @description It compares the start and end indexes of "a" and "b" and returns the number of overlapping indexes in the range.
1202
- * ex) 1, 5, 4, 6 => "2" (4 ~ 5)
1203
- * @param {Number} aStart Start index of "a"
1204
- * @param {Number} aEnd End index of "a"
1205
- * @param {Number} bStart Start index of "b"
1206
- * @param {Number} bEnd Start index of "b"
1207
- * @returns {Number}
1208
- */
1209
- getOverlapRangeAtIndex: function (aStart, aEnd, bStart, bEnd) {
1210
- if (aStart <= bEnd ? aEnd < bStart : aEnd > bStart) return 0;
1211
-
1212
- const overlap = (aStart > bStart ? aStart : bStart) - (aEnd < bEnd ? aEnd : bEnd);
1213
- return (overlap < 0 ? overlap * -1 : overlap) + 1;
1214
- },
1215
-
1216
- /**
1217
- * @description Set the text content value of the argument value element
1218
- * @param {Node} element Element to replace text content
1219
- * @param {String} txt Text to be applied
1220
- */
1221
- changeTxt: function (element, txt) {
1222
- if (!element || !txt) return;
1223
- element.textContent = txt;
1224
- },
1225
-
1226
- /**
1227
- * @description Replace element
1228
- * @param {Element} element Target element
1229
- * @param {String|Element} newElement String or element of the new element to apply
1230
- */
1231
- changeElement: function (element, newElement) {
1232
- if (typeof newElement === 'string') {
1233
- if (element.outerHTML) {
1234
- element.outerHTML = newElement;
1235
- } else {
1236
- const doc = this.createElement('DIV');
1237
- doc.innerHTML = newElement;
1238
- newElement = doc.firstChild;
1239
- element.parentNode.replaceChild(newElement, element);
1240
- }
1241
- } else if (newElement.nodeType === 1) {
1242
- element.parentNode.replaceChild(newElement, element);
1243
- }
1244
- },
1245
-
1246
- /**
1247
- * @description Set style, if all styles are deleted, the style properties are deleted.
1248
- * @param {Element} element Element to set style
1249
- * @param {String} styleName Style attribute name (marginLeft, textAlign...)
1250
- * @param {String|Number} value Style value
1251
- */
1252
- setStyle: function (element, styleName, value) {
1253
- element.style[styleName] = value;
1254
-
1255
- if (!value && !element.style.cssText) {
1256
- element.removeAttribute('style');
1257
- }
1258
- },
1259
-
1260
- /**
1261
- * @description Determine whether any of the matched elements are assigned the given class
1262
- * @param {Element} element Elements to search class name
1263
- * @param {String} className Class name to search for
1264
- * @returns {Boolean}
1265
- */
1266
- hasClass: function (element, className) {
1267
- if (!element) return;
1268
-
1269
- return (new this._w.RegExp(className)).test(element.className);
1270
- },
1271
-
1272
- /**
1273
- * @description Append the className value of the argument value element
1274
- * @param {Element} element Elements to add class name
1275
- * @param {String} className Class name to be add
1276
- */
1277
- addClass: function (element, className) {
1278
- if (!element) return;
1279
-
1280
- const check = new this._w.RegExp('(\\s|^)' + className + '(\\s|$)');
1281
- if (check.test(element.className)) return;
1282
-
1283
- element.className += (element.className.length > 0 ? ' ' : '') + className;
1284
- },
1285
-
1286
- /**
1287
- * @description Delete the className value of the argument value element
1288
- * @param {Element} element Elements to remove class name
1289
- * @param {String} className Class name to be remove
1290
- */
1291
- removeClass: function (element, className) {
1292
- if (!element) return;
1293
-
1294
- const check = new this._w.RegExp('(\\s|^)' + className + '(\\s|$)');
1295
- element.className = element.className.replace(check, ' ').trim();
1296
-
1297
- if (!element.className.trim()) element.removeAttribute('class');
1298
- },
1299
-
1300
- /**
1301
- * @description Argument value If there is no class name, insert it and delete the class name if it exists
1302
- * @param {Element} element Elements to replace class name
1303
- * @param {String} className Class name to be change
1304
- * @returns {Boolean|undefined}
1305
- */
1306
- toggleClass: function (element, className) {
1307
- if (!element) return;
1308
- let result = false;
1309
-
1310
- const check = new this._w.RegExp('(\\s|^)' + className + '(\\s|$)');
1311
- if (check.test(element.className)) {
1312
- element.className = element.className.replace(check, ' ').trim();
1313
- } else {
1314
- element.className += ' ' + className;
1315
- result = true;
1316
- }
1317
-
1318
- if (!element.className.trim()) element.removeAttribute('class');
1319
-
1320
- return result;
1321
- },
1322
-
1323
- /**
1324
- * @description Checks if element can't be easily enabled
1325
- * @param {Element} element Element to check for
1326
- */
1327
- isImportantDisabled: function (element) {
1328
- return element.hasAttribute('data-important-disabled');
1329
- },
1330
-
1331
- /**
1332
- * @description In the predefined code view mode, the buttons except the executable button are changed to the 'disabled' state.
1333
- * core.codeViewDisabledButtons (An array of buttons whose class name is not "se-code-view-enabled")
1334
- * core.resizingDisabledButtons (An array of buttons whose class name is not "se-resizing-enabled")
1335
- * @param {Boolean} disabled Disabled value
1336
- * @param {Array|HTMLCollection|NodeList} buttonList Button array
1337
- * @param {Boolean} important If priveleged mode should be used (Necessary to switch importantDisabled buttons)
1338
- */
1339
- setDisabledButtons: function (disabled, buttonList, important) {
1340
- for (let i = 0, len = buttonList.length; i < len; i++) {
1341
- let button = buttonList[i];
1342
- if (important || !this.isImportantDisabled(button)) button.disabled = disabled;
1343
- if (important) {
1344
- if (disabled) {
1345
- button.setAttribute('data-important-disabled', '');
1346
- } else {
1347
- button.removeAttribute('data-important-disabled');
1348
- }
1349
- }
1350
- }
1351
- },
1352
-
1353
- /**
1354
- * @description Delete argumenu value element
1355
- * @param {Node} item Node to be remove
1356
- */
1357
- removeItem: function (item) {
1358
- if (!item) return;
1359
-
1360
- if(typeof item.remove === 'function') item.remove();
1361
- else if (item.parentNode) item.parentNode.removeChild(item);
1362
- },
1363
-
1364
- /**
1365
- * @description Delete all parent nodes that match the condition.
1366
- * Returns an {sc: previousSibling, ec: nextSibling}(the deleted node reference) or null.
1367
- * @param {Node} item Node to be remove
1368
- * @param {Function|null} validation Validation function. default(Deleted if it only have breakLine and blanks)
1369
- * @param {Element|null} stopParent Stop when the parent node reaches stopParent
1370
- * @returns {Object|null} {sc: previousSibling, ec: nextSibling}
1371
- */
1372
- removeItemAllParents: function (item, validation, stopParent) {
1373
- if (!item) return null;
1374
- let cc = null;
1375
- if (!validation) {
1376
- validation = function (current) {
1377
- if (current === stopParent || this.isComponent(current)) return false;
1378
- const text = current.textContent.trim();
1379
- return text.length === 0 || /^(\n|\u200B)+$/.test(text);
1380
- }.bind(this);
1381
- }
1382
-
1383
- (function recursionFunc (element) {
1384
- if (!util.isWysiwygDiv(element)) {
1385
- const parent = element.parentNode;
1386
- if (parent && validation(element)) {
1387
- cc = {
1388
- sc: element.previousElementSibling,
1389
- ec: element.nextElementSibling
1390
- };
1391
- util.removeItem(element);
1392
- recursionFunc(parent);
1393
- }
1394
- }
1395
- }(item));
1396
-
1397
- return cc;
1398
- },
1399
-
1400
- /**
1401
- * @description Detach Nested all nested lists under the "baseNode".
1402
- * Returns a list with nested removed.
1403
- * @param {Node} baseNode Element on which to base.
1404
- * @param {Boolean} all If true, it also detach all nested lists of a returned list.
1405
- * @returns {Element}
1406
- */
1407
- detachNestedList: function (baseNode, all) {
1408
- const rNode = this._deleteNestedList(baseNode);
1409
- let rangeElement, cNodes;
1410
-
1411
- if (rNode) {
1412
- rangeElement = rNode.cloneNode(false);
1413
- cNodes = rNode.childNodes;
1414
- const index = this.getPositionIndex(baseNode);
1415
- while (cNodes[index]) {
1416
- rangeElement.appendChild(cNodes[index]);
1417
- }
1418
- } else {
1419
- rangeElement = baseNode;
1420
- }
1421
-
1422
- let rChildren;
1423
- if (!all) {
1424
- const depth = this.getElementDepth(baseNode) + 2;
1425
- rChildren = this.getListChildren(baseNode, function (current) { return this.isListCell(current) && !current.previousElementSibling && this.getElementDepth(current) === depth; }.bind(this));
1426
- } else {
1427
- rChildren = this.getListChildren(rangeElement, function (current) { return this.isListCell(current) && !current.previousElementSibling; }.bind(this));
1428
- }
1429
-
1430
- for (let i = 0, len = rChildren.length; i < len; i++) {
1431
- this._deleteNestedList(rChildren[i]);
1432
- }
1433
-
1434
- if (rNode) {
1435
- rNode.parentNode.insertBefore(rangeElement, rNode.nextSibling);
1436
- if (cNodes && cNodes.length === 0) this.removeItem(rNode);
1437
- }
1438
-
1439
- return rangeElement === baseNode ? rangeElement.parentNode : rangeElement;
1440
- },
1441
-
1442
- /**
1443
- * @description Sub function of util.detachNestedList method.
1444
- * @private
1445
- */
1446
- _deleteNestedList: function (baseNode) {
1447
- const baseParent = baseNode.parentNode;
1448
- let sibling = baseParent;
1449
- let parent = sibling.parentNode;
1450
- let liSibling, liParent, child, index, c;
1451
-
1452
- while (this.isListCell(parent)) {
1453
- index = this.getPositionIndex(baseNode);
1454
- liSibling = parent.nextElementSibling;
1455
- liParent = parent.parentNode;
1456
- child = sibling;
1457
- while(child) {
1458
- sibling = sibling.nextSibling;
1459
- if (this.isList(child)) {
1460
- c = child.childNodes;
1461
- while (c[index]) {
1462
- liParent.insertBefore(c[index], liSibling);
1463
- }
1464
- if (c.length === 0) this.removeItem(child);
1465
- } else {
1466
- liParent.appendChild(child);
1467
- }
1468
- child = sibling;
1469
- }
1470
- sibling = liParent;
1471
- parent = liParent.parentNode;
1472
- }
1473
-
1474
- if (baseParent.children.length === 0) this.removeItem(baseParent);
1475
-
1476
- return liParent;
1477
- },
1478
-
1479
- /**
1480
- * @description Split all tags based on "baseNode"
1481
- * Returns the last element of the splited tag.
1482
- * @param {Node} baseNode Element or text node on which to base
1483
- * @param {Number|Node|null} offset Text offset of "baseNode" (Only valid when "baseNode" is a text node)
1484
- * @param {Number} depth The nesting depth of the element being split. (default: 0)
1485
- * @returns {Element}
1486
- */
1487
- splitElement: function (baseNode, offset, depth) {
1488
- if (this.isWysiwygDiv(baseNode)) return baseNode;
1489
-
1490
- if (offset && !this.isNumber(offset)) {
1491
- const children = baseNode.childNodes;
1492
- let index = this.getPositionIndex(offset);
1493
- const prev = baseNode.cloneNode(false);
1494
- const next = baseNode.cloneNode(false);
1495
- for (let i = 0, len = children.length; i < len; i++) {
1496
- if (i < index) prev.appendChild(children[i]);
1497
- else if (i > index) next.appendChild(children[i]);
1498
- else continue;
1499
- i--;
1500
- len--;
1501
- index--;
1502
- }
1503
-
1504
- if (prev.childNodes.length > 0) baseNode.parentNode.insertBefore(prev, baseNode);
1505
- if (next.childNodes.length > 0) baseNode.parentNode.insertBefore(next, baseNode.nextElementSibling);
1506
-
1507
- return baseNode;
1508
- }
1509
-
1510
- const bp = baseNode.parentNode;
1511
- let index = 0;
1512
- let suffixIndex = 1;
1513
- let next = true;
1514
- let newEl, children, temp;
1515
- if (!depth || depth < 0) depth = 0;
1516
-
1517
- if (baseNode.nodeType === 3) {
1518
- index = this.getPositionIndex(baseNode);
1519
- if (offset >= 0 && baseNode.length !== offset) {
1520
- baseNode.splitText(offset);
1521
- const after = this.getNodeFromPath([index + 1], bp);
1522
- if (this.onlyZeroWidthSpace(after)) after.data = this.zeroWidthSpace;
1523
- }
1524
- } else if (baseNode.nodeType === 1) {
1525
- if (offset === 0) {
1526
- while (baseNode.firstChild) {
1527
- baseNode = baseNode.firstChild;
1528
- }
1529
- if (baseNode.nodeType === 3) {
1530
- const after = this.createTextNode(this.zeroWidthSpace);
1531
- baseNode.parentNode.insertBefore(after, baseNode);
1532
- baseNode = after;
1533
- }
1534
- }
1535
- if (!baseNode.previousSibling) {
1536
- if (this.getElementDepth(baseNode) === depth) next = false;
1537
- } else {
1538
- baseNode = baseNode.previousSibling;
1539
- }
1540
- }
1541
-
1542
- if (baseNode.nodeType === 1) suffixIndex = 0;
1543
- let depthEl = baseNode;
1544
- while (this.getElementDepth(depthEl) > depth) {
1545
- index = this.getPositionIndex(depthEl) + suffixIndex;
1546
- depthEl = depthEl.parentNode;
1547
-
1548
- temp = newEl;
1549
- newEl = depthEl.cloneNode(false);
1550
- children = depthEl.childNodes;
1551
-
1552
- if (temp) {
1553
- if (this.isListCell(newEl) && this.isList(temp) && temp.firstElementChild) {
1554
- newEl.innerHTML = temp.firstElementChild.innerHTML;
1555
- util.removeItem(temp.firstElementChild);
1556
- if (temp.children.length > 0) newEl.appendChild(temp);
1557
- } else {
1558
- newEl.appendChild(temp);
1559
- }
1560
- }
1561
-
1562
- while (children[index]) {
1563
- newEl.appendChild(children[index]);
1564
- }
1565
- }
1566
-
1567
- if (depthEl.childNodes.length <= 1 && (!depthEl.firstChild || depthEl.firstChild.textContent.length === 0)) depthEl.innerHTML = '<br>';
1568
-
1569
- const pElement = depthEl.parentNode;
1570
- if (next) depthEl = depthEl.nextSibling;
1571
- if (!newEl) return depthEl;
1572
-
1573
- this.mergeSameTags(newEl, null, false);
1574
- this.mergeNestedTags(newEl, function (current) { return this.isList(current); }.bind(this));
1575
-
1576
- if (newEl.childNodes.length > 0) pElement.insertBefore(newEl, depthEl);
1577
- else newEl = depthEl;
1578
-
1579
- if (this.isListCell(newEl) && newEl.children && this.isList(newEl.children[0])) {
1580
- newEl.insertBefore(this.createElement('BR'), newEl.children[0]);
1581
- }
1582
-
1583
- if (bp.childNodes.length === 0) this.removeItem(bp);
1584
-
1585
- return newEl;
1586
- },
1587
-
1588
- /**
1589
- * @description Use with "npdePath (util.getNodePath)" to merge the same attributes and tags if they are present and modify the nodepath.
1590
- * If "offset" has been changed, it will return as much "offset" as it has been modified.
1591
- * An array containing change offsets is returned in the order of the "nodePathArray" array.
1592
- * @param {Element} element Element
1593
- * @param {Array|null} nodePathArray Array of NodePath object ([util.getNodePath(), ..])
1594
- * @param {Boolean} onlyText If true, non-text nodes(!util._isIgnoreNodeChange) like 'span', 'strong'.. are ignored.
1595
- * @returns {Array} [offset, ..]
1596
- */
1597
- mergeSameTags: function (element, nodePathArray, onlyText) {
1598
- const inst = this;
1599
- const nodePathLen = nodePathArray ? nodePathArray.length : 0;
1600
- let offsets = null;
1601
-
1602
- if (nodePathLen) {
1603
- offsets = this._w.Array.apply(null, new this._w.Array(nodePathLen)).map(this._w.Number.prototype.valueOf, 0);
1604
- }
1605
-
1606
- (function recursionFunc(current, depth, depthIndex) {
1607
- const children = current.childNodes;
1608
-
1609
- for (let i = 0, len = children.length, child, next; i < len; i++) {
1610
- child = children[i];
1611
- next = children[i + 1];
1612
- if (!child) break;
1613
- if((onlyText && inst._isIgnoreNodeChange(child)) || (!onlyText && (inst.isTable(child) || inst.isListCell(child) || (inst.isFormatElement(child) && !inst.isFreeFormatElement(child))))) {
1614
- if (inst.isTable(child) || inst.isListCell(child)) {
1615
- recursionFunc(child, depth + 1, i);
1616
- }
1617
- continue;
1618
- }
1619
- if (len === 1 && current.nodeName === child.nodeName && current.parentNode) {
1620
- // update nodePath
1621
- if (nodePathLen) {
1622
- let path, c, p, cDepth, spliceDepth;
1623
- for (let n = 0; n < nodePathLen; n++) {
1624
- path = nodePathArray[n];
1625
- if (path && path[depth] === i) {
1626
- c = child, p = current, cDepth = depth, spliceDepth = true;
1627
- while (cDepth >= 0) {
1628
- if (inst.getArrayIndex(p.childNodes, c) !== path[cDepth]) {
1629
- spliceDepth = false;
1630
- break;
1631
- }
1632
- c = child.parentNode;
1633
- p = c.parentNode;
1634
- cDepth--;
1635
- }
1636
- if (spliceDepth) {
1637
- path.splice(depth, 1);
1638
- path[depth] = i;
1639
- }
1640
- }
1641
- }
1642
- }
1643
-
1644
- // merge tag
1645
- inst.copyTagAttributes(child, current);
1646
- current.parentNode.insertBefore(child, current);
1647
- inst.removeItem(current);
1648
- }
1649
- if (!next) {
1650
- if (child.nodeType === 1) recursionFunc(child, depth + 1, i);
1651
- break;
1652
- }
1653
-
1654
- if (child.nodeName === next.nodeName && inst.isSameAttributes(child, next) && child.href === next.href) {
1655
- const childs = child.childNodes;
1656
- let childLength = 0;
1657
- for (let n = 0, nLen = childs.length; n < nLen; n++) {
1658
- if (childs[n].textContent.length > 0) childLength++;
1659
- }
1660
-
1661
- const l = child.lastChild;
1662
- const r = next.firstChild;
1663
- let addOffset = 0;
1664
- if (l && r) {
1665
- const textOffset = l.nodeType === 3 && r.nodeType === 3;
1666
- addOffset = l.textContent.length;
1667
- let tempL = l.previousSibling;
1668
- while(tempL && tempL.nodeType === 3) {
1669
- addOffset += tempL.textContent.length;
1670
- tempL = tempL.previousSibling;
1671
- }
1672
-
1673
- if (childLength > 0 && l.nodeType === 3 && r.nodeType === 3 && (l.textContent.length > 0 || r.textContent.length > 0)) childLength--;
1674
-
1675
- if (nodePathLen) {
1676
- let path = null;
1677
- for (let n = 0; n < nodePathLen; n++) {
1678
- path = nodePathArray[n];
1679
- if (path && path[depth] > i) {
1680
- if (depth > 0 && path[depth - 1] !== depthIndex) continue;
1681
-
1682
- path[depth] -= 1;
1683
- if (path[depth + 1] >= 0 && path[depth] === i) {
1684
- path[depth + 1] += childLength;
1685
- if (textOffset) {
1686
- if (l && l.nodeType === 3 && r && r.nodeType === 3) {
1687
- offsets[n] += addOffset;
1688
- }
1689
- }
1690
- }
1691
- }
1692
- }
1693
- }
1694
- }
1695
-
1696
- if (child.nodeType === 3) {
1697
- addOffset = child.textContent.length;
1698
- child.textContent += next.textContent;
1699
- if (nodePathLen) {
1700
- let path = null;
1701
- for (let n = 0; n < nodePathLen; n++) {
1702
- path = nodePathArray[n];
1703
- if (path && path[depth] > i) {
1704
- if (depth > 0 && path[depth - 1] !== depthIndex) continue;
1705
-
1706
- path[depth] -= 1;
1707
- if (path[depth + 1] >= 0 && path[depth] === i) {
1708
- path[depth + 1] += childLength;
1709
- offsets[n] += addOffset;
1710
- }
1711
- }
1712
- }
1713
- }
1714
- } else {
1715
- child.innerHTML += next.innerHTML;
1716
- }
1717
-
1718
- inst.removeItem(next);
1719
- i--;
1720
- } else if (child.nodeType === 1) {
1721
- recursionFunc(child, depth + 1, i);
1722
- }
1723
- }
1724
- })(element, 0, 0);
1725
-
1726
- return offsets;
1727
- },
1728
-
1729
- /**
1730
- * @description Remove nested tags without other child nodes.
1731
- * @param {Element} element Element object
1732
- * @param {Function|String|null} validation Validation function / String("tag1|tag2..") / If null, all tags are applicable.
1733
- */
1734
- mergeNestedTags: function (element, validation) {
1735
- if (typeof validation === 'string') {
1736
- validation = function (current) { return this.test(current.tagName); }.bind(new this._w.RegExp('^(' + (validation ? validation : '.+') + ')$', 'i'));
1737
- } else if (typeof validation !== 'function') {
1738
- validation = function () { return true; };
1739
- }
1740
-
1741
- (function recursionFunc(current) {
1742
- let children = current.children;
1743
- if (children.length === 1 && children[0].nodeName === current.nodeName && validation(current)) {
1744
- const temp = children[0];
1745
- children = temp.children;
1746
- while (children[0]) {
1747
- current.appendChild(children[0]);
1748
- }
1749
- current.removeChild(temp);
1750
- }
1751
-
1752
- for (let i = 0, len = current.children.length; i < len; i++) {
1753
- recursionFunc(current.children[i]);
1754
- }
1755
- })(element);
1756
- },
1757
-
1758
- /**
1759
- * @description Delete a empty child node of argument element.
1760
- * @param {Element} element Element node
1761
- * @param {Node|null} notRemoveNode Do not remove node
1762
- * @param {boolean} forceDelete When all child nodes are deleted, the parent node is also deleted.
1763
- */
1764
- removeEmptyNode: function (element, notRemoveNode, forceDelete) {
1765
- const inst = this;
1766
-
1767
- if (notRemoveNode) {
1768
- notRemoveNode = inst.getParentElement(notRemoveNode, function (current) {
1769
- return element === current.parentElement;
1770
- });
1771
- }
1772
-
1773
- (function recursionFunc(current) {
1774
- if (inst._notTextNode(current) || current === notRemoveNode || inst.isNonEditable(current)) return 0;
1775
- if (current !== element && inst.onlyZeroWidthSpace(current.textContent) && (!current.firstChild || !inst.isBreak(current.firstChild)) && !current.querySelector(inst._allowedEmptyNodeList)) {
1776
- if (current.parentNode) {
1777
- current.parentNode.removeChild(current);
1778
- return -1;
1779
- }
1780
- } else {
1781
- const children = current.children;
1782
- for (let i = 0, len = children.length, r = 0; i < len; i++) {
1783
- if (!children[i + r] || inst.isComponent(children[i + r])) continue;
1784
- r += recursionFunc(children[i + r]);
1785
- }
1786
- }
1787
-
1788
- return 0;
1789
- })(element);
1790
-
1791
- if (element.childNodes.length === 0) {
1792
- if (forceDelete) {
1793
- this.removeItem(element);
1794
- } else {
1795
- element.innerHTML = '<br>';
1796
- }
1797
- }
1798
- },
1799
-
1800
- /**
1801
- * @description Remove whitespace between tags in HTML string.
1802
- * @param {String} html HTML string
1803
- * @returns {String}
1804
- */
1805
- htmlRemoveWhiteSpace: function (html) {
1806
- if (!html) return '';
1807
- return html.trim().replace(/<\/?(?!strong|span|font|b|var|i|em|u|ins|s|strike|del|sub|sup|mark|a|label|code|summary)[^>^<]+>\s+(?=<)/ig, function (m) { return m.replace(/\n/g, '').replace(/\s+/, ' '); });
1808
- },
1809
-
1810
- /**
1811
- * @description HTML code compression
1812
- * @param {string} html HTML string
1813
- * @returns {string} HTML string
1814
- */
1815
- htmlCompress: function (html) {
1816
- return html.replace(/\n/g, '').replace(/(>)(?:\s+)(<)/g, '$1$2');
1817
- },
1818
-
1819
- /**
1820
- * @description Sort a element array by depth of element.
1821
- * @param {Array} array Array object
1822
- * @param {Boolean} des true: descending order / false: ascending order
1823
- */
1824
- sortByDepth: function (array, des) {
1825
- const t = !des ? -1 : 1;
1826
- const f = t * -1;
1827
-
1828
- array.sort(function (a, b) {
1829
- if (!this.isListCell(a) || !this.isListCell(b)) return 0;
1830
- a = this.getElementDepth(a);
1831
- b = this.getElementDepth(b);
1832
- return a > b ? t : a < b ? f : 0;
1833
- }.bind(this));
1834
- },
1835
-
1836
- /**
1837
- * @description Escape a string for safe use in regular expressions.
1838
- * @param {String} string String to escape
1839
- * @returns {String}
1840
- */
1841
- escapeStringRegexp: function (string) {
1842
- if (typeof string !== 'string') {
1843
- throw new TypeError('Expected a string');
1844
- }
1845
-
1846
- // Escape characters with special meaning either inside or outside character sets.
1847
- // Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
1848
- return string
1849
- .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
1850
- .replace(/-/g, '\\x2d');
1851
- },
1852
-
1853
- _isExcludeSelectionElement: function (element) {
1854
- return !/FIGCAPTION/i.test(element.nodeName) && (this.isComponent(element) || /FIGURE/i.test(element.nodeName));
1855
- },
1856
-
1857
- /**
1858
- * @description Nodes that need to be added without modification when changing text nodes
1859
- * @param {Node} element Element to check
1860
- * @returns {Boolean}
1861
- * @private
1862
- */
1863
- _isIgnoreNodeChange: function (element) {
1864
- return element && element.nodeType !== 3 && (this.isNonEditable(element) || !this.isTextStyleElement(element));
1865
- },
1866
-
1867
- /**
1868
- * @description Nodes that must remain undetached when changing text nodes (A, Label, Code, Span:font-size)
1869
- * @param {Node|String} element Element to check
1870
- * @returns {Boolean}
1871
- * @private
1872
- */
1873
- _isMaintainedNode: function (element) {
1874
- return element && element.nodeType !== 3 && /^(a|label|code|summary)$/i.test(typeof element === 'string' ? element : element.nodeName);
1875
- },
1876
-
1877
- /**
1878
- * @description Node with font-size style
1879
- * @param {Node} element Element to check
1880
- * @returns {Boolean}
1881
- * @private
1882
- */
1883
- _isSizeNode: function (element) {
1884
- return element && element.nodeType !== 3 && this.isTextStyleElement(element) && !!element.style.fontSize;
1885
- },
1886
-
1887
- /**
1888
- * @description Nodes without text
1889
- * @param {Node} element Element to check
1890
- * @returns {Boolean}
1891
- * @private
1892
- */
1893
- _notTextNode: function (element) {
1894
- return element && element.nodeType !== 3 && (this.isComponent(element) || /^(br|input|select|canvas|img|iframe|audio|video)$/i.test(typeof element === 'string' ? element : element.nodeName));
1895
- },
1896
-
1897
- /**
1898
- * @deprecated
1899
- * @description Check disallowed tags
1900
- * @param {Node} element Element to check
1901
- * @returns {Boolean}
1902
- * @private
1903
- */
1904
- _disallowedTags: function (element) {
1905
- return /^(meta|script|link|style|[a-z]+\:[a-z]+)$/i.test(element.nodeName);
1906
- },
1907
-
1908
- /**
1909
- * @description Create whitelist RegExp object.
1910
- * Return RegExp format: new RegExp("<\\/?\\b(?!" + list + ")\\b[^>^<]*+>", "gi")
1911
- * @param {String} list Tags list ("br|p|div|pre...")
1912
- * @returns {RegExp}
1913
- */
1914
- createTagsWhitelist: function (list) {
1915
- return new RegExp('<\\/?\\b(?!\\b' + (list || '').replace(/\|/g, '\\b|\\b') + '\\b)[^>]*>', 'gi');
1916
- },
1917
-
1918
- /**
1919
- * @description Create blacklist RegExp object.
1920
- * Return RegExp format: new RegExp("<\\/?\\b(?:" + list + ")\\b[^>^<]*+>", "gi")
1921
- * @param {String} list Tags list ("br|p|div|pre...")
1922
- * @returns {RegExp}
1923
- */
1924
- createTagsBlacklist: function (list) {
1925
- return new RegExp('<\\/?\\b(?:\\b' + (list || '^').replace(/\|/g, '\\b|\\b') + '\\b)[^>]*>', 'gi');
1926
- },
1927
-
1928
- /**
1929
- * @description Fix tags that do not fit the editor format.
1930
- * @param {Element} documentFragment Document fragment "DOCUMENT_FRAGMENT_NODE" (nodeType === 11)
1931
- * @param {RegExp} htmlCheckWhitelistRegExp Editor tags whitelist (core._htmlCheckWhitelistRegExp)
1932
- * @param {RegExp} htmlCheckBlacklistRegExp Editor tags blacklist (core._htmlCheckBlacklistRegExp)
1933
- * @param {Function} classNameFilter Class name filter function
1934
- * @private
1935
- */
1936
- _consistencyCheckOfHTML: function (documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, classNameFilter) {
1937
- /**
1938
- * It is can use ".children(util.getListChildren)" to exclude text nodes, but "documentFragment.children" is not supported in IE.
1939
- * So check the node type and exclude the text no (current.nodeType !== 1)
1940
- */
1941
- const removeTags = [], emptyTags = [], wrongList = [], withoutFormatCells = [];
1942
-
1943
- // wrong position
1944
- const wrongTags = this.getListChildNodes(documentFragment, function (current) {
1945
- if (current.nodeType !== 1) {
1946
- if (this.isList(current.parentElement)) removeTags.push(current);
1947
- return false;
1948
- }
1949
-
1950
- // white list
1951
- if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && this.isNotCheckingNode(current))) {
1952
- removeTags.push(current);
1953
- return false;
1954
- }
1955
-
1956
- // empty tags
1957
- const nrtag = !this.getParentElement(current, this.isNotCheckingNode);
1958
- if ((!this.isTable(current) && !this.isListCell(current) && !this.isAnchor(current)) && (this.isFormatElement(current) || this.isRangeFormatElement(current) || this.isTextStyleElement(current)) && current.childNodes.length === 0 && nrtag) {
1959
- emptyTags.push(current);
1960
- return false;
1961
- }
1962
-
1963
- // wrong list
1964
- if (this.isList(current.parentNode) && !this.isList(current) && !this.isListCell(current)) {
1965
- wrongList.push(current);
1966
- return false;
1967
- }
1968
-
1969
- // table cells
1970
- if (this.isCell(current)) {
1971
- const fel = current.firstElementChild;
1972
- if (!this.isFormatElement(fel) && !this.isRangeFormatElement(fel) && !this.isComponent(fel)) {
1973
- withoutFormatCells.push(current);
1974
- return false;
1975
- }
1976
- }
1977
-
1978
- // class filter
1979
- if (nrtag && current.className) {
1980
- const className = new this._w.Array(current.classList).map(classNameFilter).join(' ').trim();
1981
- if (className) current.className = className;
1982
- else current.removeAttribute('class');
1983
- }
1984
-
1985
- const result = current.parentNode !== documentFragment && nrtag &&
1986
- ((this.isListCell(current) && !this.isList(current.parentNode)) ||
1987
- ((this.isFormatElement(current) || this.isComponent(current)) && !this.isRangeFormatElement(current.parentNode) && !this.getParentElement(current, this.isComponent)));
1988
-
1989
- return result;
1990
- }.bind(this));
1991
-
1992
- for (let i = 0, len = removeTags.length; i < len; i++) {
1993
- this.removeItem(removeTags[i]);
1994
- }
1995
-
1996
- const checkTags = [];
1997
- for (let i = 0, len = wrongTags.length, t, p; i < len; i++) {
1998
- t = wrongTags[i];
1999
- p = t.parentNode;
2000
- if (!p || !p.parentNode) continue;
2001
-
2002
- if (this.getParentElement(t, this.isListCell)) {
2003
- const cellChildren = t.childNodes;
2004
- for (let j = cellChildren.length - 1; len >= 0; j--) {
2005
- p.insertBefore(t, cellChildren[j]);
2006
- }
2007
- checkTags.push(t);
2008
- } else {
2009
- p.parentNode.insertBefore(t, p);
2010
- checkTags.push(p);
2011
- }
2012
- }
2013
-
2014
- for (let i = 0, len = checkTags.length, t; i < len; i++) {
2015
- t = checkTags[i];
2016
- if (this.onlyZeroWidthSpace(t.textContent.trim())) {
2017
- this.removeItem(t);
2018
- }
2019
- }
2020
-
2021
- for (let i = 0, len = emptyTags.length; i < len; i++) {
2022
- this.removeItem(emptyTags[i]);
2023
- }
2024
-
2025
- for (let i = 0, len = wrongList.length, t, tp, children, p; i < len; i++) {
2026
- t = wrongList[i];
2027
- p = t.parentNode;
2028
- if (!p) continue;
2029
-
2030
- tp = this.createElement('LI');
2031
-
2032
- if (this.isFormatElement(t)) {
2033
- children = t.childNodes;
2034
- while (children[0]) {
2035
- tp.appendChild(children[0]);
2036
- }
2037
- p.insertBefore(tp, t);
2038
- this.removeItem(t);
2039
- } else {
2040
- t = t.nextSibling;
2041
- tp.appendChild(wrongList[i]);
2042
- p.insertBefore(tp, t);
2043
- }
2044
- }
2045
-
2046
- for (let i = 0, len = withoutFormatCells.length, t, f; i < len; i++) {
2047
- t = withoutFormatCells[i];
2048
- f = this.createElement('DIV');
2049
- f.innerHTML = (t.textContent.trim().length === 0 && t.children.length === 0) ? '<br>' : t.innerHTML;
2050
- t.innerHTML = f.outerHTML;
2051
- }
2052
- },
2053
-
2054
- _setDefaultOptionStyle: function (options, defaultStyle) {
2055
- let optionStyle = '';
2056
- if (options.height) optionStyle += 'height:' + options.height + ';';
2057
- if (options.minHeight) optionStyle += 'min-height:' + options.minHeight + ';';
2058
- if (options.maxHeight) optionStyle += 'max-height:' + options.maxHeight + ';';
2059
- if (options.position) optionStyle += 'position:' + options.position + ';';
2060
- if (options.width) optionStyle += 'width:' + options.width + ';';
2061
- if (options.minWidth) optionStyle += 'min-width:' + options.minWidth + ';';
2062
- if (options.maxWidth) optionStyle += 'max-width:' + options.maxWidth + ';';
2063
-
2064
- let top = '', frame = '', editor = '';
2065
- defaultStyle = optionStyle + defaultStyle;
2066
- const styleArr = defaultStyle.split(';');
2067
- for (let i = 0, len = styleArr.length, s; i < len; i++) {
2068
- s = styleArr[i].trim();
2069
- if (!s) continue;
2070
- if (/^(min-|max-)?width\s*:/.test(s) || /^(z-index|position)\s*:/.test(s)) {
2071
- top += s + ';';
2072
- continue;
2073
- }
2074
- if (/^(min-|max-)?height\s*:/.test(s)) {
2075
- if (/^height/.test(s) && s.split(':')[1].trim() === 'auto') {
2076
- options.height = 'auto';
2077
- }
2078
- frame += s + ';';
2079
- continue;
2080
- }
2081
- editor += s + ';';
2082
- }
2083
-
2084
- return {
2085
- top: top,
2086
- frame: frame,
2087
- editor: editor
2088
- };
2089
- },
2090
-
2091
- _setIframeDocument: function (frame, options) {
2092
- frame.setAttribute('scrolling', 'auto');
2093
- frame.contentDocument.head.innerHTML = '' +
2094
- '<meta charset="utf-8" />' +
2095
- '<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">' +
2096
- this._setIframeCssTags(options);
2097
- frame.contentDocument.body.className = options._editableClass;
2098
- frame.contentDocument.body.setAttribute('contenteditable', true);
2099
- frame.contentDocument.body.setAttribute('autocorrect', "off");
2100
- },
2101
-
2102
- _setIframeCssTags: function (options) {
2103
- const linkNames = options.iframeCSSFileName;
2104
- const wRegExp = this._w.RegExp;
2105
- let tagString = '';
2106
-
2107
- for (let f = 0, len = linkNames.length, path; f < len; f++) {
2108
- path = [];
2109
-
2110
- if (/(^https?:\/\/)|(^data:text\/css,)/.test(linkNames[f])) {
2111
- path.push(linkNames[f]);
2112
- } else {
2113
- const CSSFileName = new wRegExp('(^|.*[\\/])' + linkNames[f] + '(\\..+)?\\.css(?:\\?.*|;.*)?$', 'i');
2114
- for (let c = document.getElementsByTagName('link'), i = 0, len = c.length, styleTag; i < len; i++) {
2115
- styleTag = c[i].href.match(CSSFileName);
2116
- if (styleTag) path.push(styleTag[0]);
2117
- }
2118
- }
2119
-
2120
- if (!path || path.length === 0) throw '[SUNEDITOR.constructor.iframe.fail] The suneditor CSS files installation path could not be automatically detected. Please set the option property "iframeCSSFileName" before creating editor instances.';
2121
-
2122
- for (let i = 0, len = path.length; i < len; i++) {
2123
- tagString += '<link href="' + path[i] + '" rel="stylesheet">';
2124
- }
2125
- }
2126
-
2127
- return tagString + (options.height === 'auto' ? '<style>\n/** Iframe height auto */\nbody{height: min-content; overflow: hidden;}\n</style>' : '');
2128
- }
2129
- };
2130
-
2131
- export default util;