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.
- package/.eslintignore +7 -0
- package/.eslintrc.json +64 -0
- package/CONTRIBUTING.md +36 -0
- package/LICENSE.txt +1 -1
- package/README.md +11 -1560
- package/package.json +94 -70
- package/src/assets/icons/_default.js +194 -0
- package/src/assets/suneditor-content.css +642 -0
- package/src/assets/suneditor.css +3378 -0
- package/src/core/base/eventHandlers/handler_toolbar.js +114 -0
- package/src/core/base/eventHandlers/handler_ww_clipboard.js +31 -0
- package/src/core/base/eventHandlers/handler_ww_dragDrop.js +69 -0
- package/src/core/base/eventHandlers/handler_ww_key_input.js +978 -0
- package/src/core/base/eventHandlers/handler_ww_mouse.js +118 -0
- package/src/core/base/eventManager.js +1129 -0
- package/src/core/base/events.js +320 -0
- package/src/core/base/history.js +301 -0
- package/src/core/class/char.js +146 -0
- package/src/core/class/component.js +624 -0
- package/src/core/class/format.js +3255 -0
- package/src/core/class/html.js +1621 -0
- package/src/core/class/menu.js +260 -0
- package/src/core/class/nodeTransform.js +379 -0
- package/src/core/class/notice.js +42 -0
- package/src/core/class/offset.js +578 -0
- package/src/core/class/selection.js +508 -0
- package/src/core/class/shortcuts.js +38 -0
- package/src/core/class/toolbar.js +440 -0
- package/src/core/class/viewer.js +646 -0
- package/src/core/editor.js +1588 -0
- package/src/core/section/actives.js +107 -0
- package/src/core/section/constructor.js +1237 -0
- package/src/core/section/context.js +97 -0
- package/src/editorInjector/_classes.js +22 -0
- package/src/editorInjector/_core.js +28 -0
- package/src/editorInjector/index.js +13 -0
- package/src/helper/converter.js +313 -0
- package/src/helper/domUtils.js +1177 -0
- package/src/helper/env.js +250 -0
- package/src/helper/index.js +19 -0
- package/src/helper/numbers.js +68 -0
- package/src/helper/unicode.js +43 -0
- package/src/langs/ckb.js +161 -0
- package/src/langs/cs.js +161 -0
- package/src/langs/da.js +161 -0
- package/src/langs/de.js +162 -0
- package/src/langs/en.js +199 -0
- package/src/langs/es.js +162 -0
- package/src/langs/fa.js +159 -0
- package/src/langs/fr.js +161 -0
- package/src/langs/he.js +162 -0
- package/src/{lang → langs}/index.js +0 -2
- package/src/langs/it.js +162 -0
- package/src/langs/ja.js +162 -0
- package/src/langs/ko.js +162 -0
- package/src/langs/lv.js +162 -0
- package/src/langs/nl.js +162 -0
- package/src/langs/pl.js +162 -0
- package/src/langs/pt_br.js +162 -0
- package/src/langs/ro.js +162 -0
- package/src/langs/ru.js +162 -0
- package/src/langs/se.js +162 -0
- package/src/langs/tr.js +159 -0
- package/src/langs/ua.js +162 -0
- package/src/langs/ur.js +162 -0
- package/src/langs/zh_cn.js +162 -0
- package/src/modules/ApiManager.js +168 -0
- package/src/modules/ColorPicker.js +302 -0
- package/src/modules/Controller.js +315 -0
- package/src/modules/Figure.js +1174 -0
- package/src/modules/FileBrowser.js +271 -0
- package/src/modules/FileManager.js +290 -0
- package/src/modules/HueSlider.js +513 -0
- package/src/modules/Modal.js +177 -0
- package/src/modules/ModalAnchorEditor.js +494 -0
- package/src/modules/SelectMenu.js +447 -0
- package/src/modules/_DragHandle.js +16 -0
- package/src/modules/index.js +14 -0
- package/src/plugins/command/blockquote.js +47 -47
- package/src/plugins/command/exportPdf.js +168 -0
- package/src/plugins/command/fileUpload.js +389 -0
- package/src/plugins/command/list_bulleted.js +112 -0
- package/src/plugins/command/list_numbered.js +115 -0
- package/src/plugins/dropdown/align.js +143 -0
- package/src/plugins/dropdown/backgroundColor.js +73 -0
- package/src/plugins/dropdown/font.js +113 -0
- package/src/plugins/dropdown/fontColor.js +73 -0
- package/src/plugins/dropdown/formatBlock.js +141 -0
- package/src/plugins/dropdown/hr.js +111 -0
- package/src/plugins/dropdown/layout.js +72 -0
- package/src/plugins/dropdown/lineHeight.js +114 -0
- package/src/plugins/dropdown/list.js +107 -0
- package/src/plugins/dropdown/paragraphStyle.js +117 -0
- package/src/plugins/dropdown/table.js +2810 -0
- package/src/plugins/dropdown/template.js +71 -0
- package/src/plugins/dropdown/textStyle.js +137 -0
- package/src/plugins/field/mention.js +172 -0
- package/src/plugins/fileBrowser/imageGallery.js +76 -59
- package/src/plugins/index.js +86 -24
- package/src/plugins/input/fontSize.js +357 -0
- package/src/plugins/modal/audio.js +492 -0
- package/src/plugins/modal/image.js +1062 -0
- package/src/plugins/modal/link.js +211 -0
- package/src/plugins/modal/math.js +347 -0
- package/src/plugins/modal/video.js +870 -0
- package/src/suneditor.js +62 -67
- package/src/themes/test.css +61 -0
- package/typings/CommandPlugin.d.ts +8 -0
- package/typings/DialogPlugin.d.ts +20 -0
- package/typings/FileBrowserPlugin.d.ts +30 -0
- package/typings/Lang.d.ts +124 -0
- package/typings/Module.d.ts +15 -0
- package/typings/Plugin.d.ts +42 -0
- package/typings/SubmenuPlugin.d.ts +8 -0
- package/typings/_classes.d.ts +17 -0
- package/typings/_colorPicker.d.ts +60 -0
- package/typings/_core.d.ts +55 -0
- package/typings/align.d.ts +5 -0
- package/{src/plugins/dialog → typings}/audio.d.ts +1 -1
- package/typings/backgroundColor.d.ts +5 -0
- package/{src/plugins/command → typings}/blockquote.d.ts +1 -1
- package/typings/char.d.ts +39 -0
- package/typings/component.d.ts +38 -0
- package/typings/context.d.ts +39 -0
- package/typings/converter.d.ts +33 -0
- package/typings/dialog.d.ts +28 -0
- package/typings/domUtils.d.ts +361 -0
- package/typings/editor.d.ts +7 -0
- package/typings/editor.ts +542 -0
- package/typings/env.d.ts +70 -0
- package/typings/eventManager.d.ts +37 -0
- package/typings/events.d.ts +262 -0
- package/typings/fileBrowser.d.ts +42 -0
- package/typings/fileManager.d.ts +67 -0
- package/typings/font.d.ts +5 -0
- package/typings/fontColor.d.ts +5 -0
- package/typings/fontSize.d.ts +5 -0
- package/typings/format.d.ts +191 -0
- package/typings/formatBlock.d.ts +5 -0
- package/typings/history.d.ts +48 -0
- package/typings/horizontalRule.d.ts +5 -0
- package/{src/plugins/dialog → typings}/image.d.ts +1 -1
- package/{src/plugins/fileBrowser → typings}/imageGallery.d.ts +1 -1
- package/typings/index.d.ts +21 -0
- package/{src/plugins/modules/index.d.ts → typings/index.modules.d.ts} +3 -3
- package/typings/index.plugins.d.ts +58 -0
- package/typings/lineHeight.d.ts +5 -0
- package/{src/plugins/dialog → typings}/link.d.ts +1 -1
- package/typings/list.d.ts +5 -0
- package/{src/plugins/dialog → typings}/math.d.ts +1 -1
- package/typings/mediaContainer.d.ts +25 -0
- package/typings/node.d.ts +57 -0
- package/typings/notice.d.ts +16 -0
- package/typings/numbers.d.ts +29 -0
- package/typings/offset.d.ts +24 -0
- package/typings/options.d.ts +589 -0
- package/typings/paragraphStyle.d.ts +5 -0
- package/typings/resizing.d.ts +141 -0
- package/typings/selection.d.ts +94 -0
- package/typings/shortcuts.d.ts +13 -0
- package/typings/suneditor.d.ts +9 -0
- package/typings/table.d.ts +5 -0
- package/typings/template.d.ts +5 -0
- package/typings/textStyle.d.ts +5 -0
- package/typings/toolbar.d.ts +32 -0
- package/typings/unicode.d.ts +25 -0
- package/{src/plugins/dialog → typings}/video.d.ts +1 -1
- package/dist/css/suneditor.min.css +0 -1
- package/dist/suneditor.min.js +0 -2
- package/src/assets/css/suneditor-contents.css +0 -562
- package/src/assets/css/suneditor.css +0 -566
- package/src/assets/defaultIcons.js +0 -103
- package/src/lang/Lang.d.ts +0 -144
- package/src/lang/ckb.d.ts +0 -5
- package/src/lang/ckb.js +0 -188
- package/src/lang/cs.d.ts +0 -5
- package/src/lang/cs.js +0 -188
- package/src/lang/da.d.ts +0 -5
- package/src/lang/da.js +0 -191
- package/src/lang/de.d.ts +0 -5
- package/src/lang/de.js +0 -188
- package/src/lang/en.d.ts +0 -5
- package/src/lang/en.js +0 -188
- package/src/lang/es.d.ts +0 -5
- package/src/lang/es.js +0 -188
- package/src/lang/fa.d.ts +0 -5
- package/src/lang/fa.js +0 -188
- package/src/lang/fr.d.ts +0 -5
- package/src/lang/fr.js +0 -188
- package/src/lang/he.d.ts +0 -5
- package/src/lang/he.js +0 -188
- package/src/lang/index.d.ts +0 -23
- package/src/lang/it.d.ts +0 -5
- package/src/lang/it.js +0 -188
- package/src/lang/ja.d.ts +0 -5
- package/src/lang/ja.js +0 -188
- package/src/lang/ko.d.ts +0 -5
- package/src/lang/ko.js +0 -188
- package/src/lang/lv.d.ts +0 -5
- package/src/lang/lv.js +0 -188
- package/src/lang/nl.d.ts +0 -5
- package/src/lang/nl.js +0 -188
- package/src/lang/pl.d.ts +0 -5
- package/src/lang/pl.js +0 -188
- package/src/lang/pt_br.d.ts +0 -5
- package/src/lang/pt_br.js +0 -189
- package/src/lang/ro.d.ts +0 -5
- package/src/lang/ro.js +0 -188
- package/src/lang/ru.d.ts +0 -5
- package/src/lang/ru.js +0 -188
- package/src/lang/se.d.ts +0 -5
- package/src/lang/se.js +0 -191
- package/src/lang/tr.d.ts +0 -5
- package/src/lang/tr.js +0 -191
- package/src/lang/ua.d.ts +0 -5
- package/src/lang/ua.js +0 -188
- package/src/lang/ur.d.ts +0 -5
- package/src/lang/ur.js +0 -188
- package/src/lang/zh_cn.d.ts +0 -5
- package/src/lang/zh_cn.js +0 -187
- package/src/lib/constructor.js +0 -954
- package/src/lib/context.d.ts +0 -42
- package/src/lib/context.js +0 -71
- package/src/lib/core.d.ts +0 -1135
- package/src/lib/core.js +0 -9395
- package/src/lib/history.d.ts +0 -48
- package/src/lib/history.js +0 -219
- package/src/lib/util.d.ts +0 -678
- package/src/lib/util.js +0 -2131
- package/src/options.d.ts +0 -608
- package/src/plugins/CommandPlugin.d.ts +0 -8
- package/src/plugins/DialogPlugin.d.ts +0 -20
- package/src/plugins/FileBrowserPlugin.d.ts +0 -30
- package/src/plugins/Module.d.ts +0 -15
- package/src/plugins/Plugin.d.ts +0 -42
- package/src/plugins/SubmenuPlugin.d.ts +0 -8
- package/src/plugins/dialog/audio.js +0 -559
- package/src/plugins/dialog/image.js +0 -1126
- package/src/plugins/dialog/link.js +0 -223
- package/src/plugins/dialog/math.js +0 -295
- package/src/plugins/dialog/mention.js +0 -242
- package/src/plugins/dialog/video.js +0 -979
- package/src/plugins/index.d.ts +0 -79
- package/src/plugins/modules/_anchor.js +0 -461
- package/src/plugins/modules/_colorPicker.d.ts +0 -60
- package/src/plugins/modules/_colorPicker.js +0 -201
- package/src/plugins/modules/_notice.d.ts +0 -21
- package/src/plugins/modules/_notice.js +0 -72
- package/src/plugins/modules/_selectMenu.js +0 -119
- package/src/plugins/modules/component.d.ts +0 -25
- package/src/plugins/modules/component.js +0 -81
- package/src/plugins/modules/dialog.d.ts +0 -28
- package/src/plugins/modules/dialog.js +0 -175
- package/src/plugins/modules/fileBrowser.d.ts +0 -42
- package/src/plugins/modules/fileBrowser.js +0 -374
- package/src/plugins/modules/fileManager.d.ts +0 -67
- package/src/plugins/modules/fileManager.js +0 -326
- package/src/plugins/modules/index.js +0 -9
- package/src/plugins/modules/resizing.d.ts +0 -154
- package/src/plugins/modules/resizing.js +0 -903
- package/src/plugins/submenu/align.d.ts +0 -5
- package/src/plugins/submenu/align.js +0 -160
- package/src/plugins/submenu/font.d.ts +0 -5
- package/src/plugins/submenu/font.js +0 -123
- package/src/plugins/submenu/fontColor.d.ts +0 -5
- package/src/plugins/submenu/fontColor.js +0 -101
- package/src/plugins/submenu/fontSize.d.ts +0 -5
- package/src/plugins/submenu/fontSize.js +0 -112
- package/src/plugins/submenu/formatBlock.d.ts +0 -5
- package/src/plugins/submenu/formatBlock.js +0 -273
- package/src/plugins/submenu/hiliteColor.d.ts +0 -5
- package/src/plugins/submenu/hiliteColor.js +0 -102
- package/src/plugins/submenu/horizontalRule.d.ts +0 -5
- package/src/plugins/submenu/horizontalRule.js +0 -98
- package/src/plugins/submenu/lineHeight.d.ts +0 -5
- package/src/plugins/submenu/lineHeight.js +0 -104
- package/src/plugins/submenu/list.d.ts +0 -5
- package/src/plugins/submenu/list.js +0 -456
- package/src/plugins/submenu/paragraphStyle.d.ts +0 -5
- package/src/plugins/submenu/paragraphStyle.js +0 -135
- package/src/plugins/submenu/table.d.ts +0 -5
- package/src/plugins/submenu/table.js +0 -1431
- package/src/plugins/submenu/template.d.ts +0 -5
- package/src/plugins/submenu/template.js +0 -72
- package/src/plugins/submenu/textStyle.d.ts +0 -5
- package/src/plugins/submenu/textStyle.js +0 -167
- package/src/suneditor.d.ts +0 -9
- package/src/suneditor_build.js +0 -18
- /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 = {'&': '&', '\u00A0': ' ', '\'': ''', '"': '"', '<': '<', '>': '>'};
|
|
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;
|