suneditor 3.0.0-alpha.9 → 3.0.0-beta.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/CONTRIBUTING.md +170 -22
- package/{LICENSE.txt → LICENSE} +9 -9
- package/README.md +168 -30
- package/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +47 -21
- package/src/assets/design/color.css +121 -0
- package/src/assets/design/index.css +3 -0
- package/src/assets/design/size.css +35 -0
- package/src/assets/design/typography.css +37 -0
- package/src/assets/icons/defaultIcons.js +232 -0
- package/src/assets/suneditor-contents.css +181 -46
- package/src/assets/suneditor.css +1403 -650
- package/src/core/base/eventHandlers/handler_toolbar.js +35 -14
- package/src/core/base/eventHandlers/handler_ww_clipboard.js +23 -4
- package/src/core/base/eventHandlers/handler_ww_dragDrop.js +49 -10
- package/src/core/base/eventHandlers/handler_ww_key_input.js +422 -224
- package/src/core/base/eventHandlers/handler_ww_mouse.js +83 -36
- package/src/core/base/eventManager.js +520 -179
- package/src/core/base/history.js +95 -41
- package/src/core/class/char.js +26 -11
- package/src/core/class/component.js +345 -137
- package/src/core/class/format.js +683 -519
- package/src/core/class/html.js +485 -305
- package/src/core/class/menu.js +133 -47
- package/src/core/class/nodeTransform.js +90 -71
- package/src/core/class/offset.js +408 -92
- package/src/core/class/selection.js +216 -106
- package/src/core/class/shortcuts.js +68 -8
- package/src/core/class/toolbar.js +106 -116
- package/src/core/class/ui.js +422 -0
- package/src/core/class/viewer.js +178 -74
- package/src/core/editor.js +496 -389
- package/src/core/section/actives.js +123 -27
- package/src/core/section/constructor.js +615 -206
- package/src/core/section/context.js +28 -23
- package/src/core/section/documentType.js +561 -0
- package/src/editorInjector/_classes.js +19 -5
- package/src/editorInjector/_core.js +71 -7
- package/src/editorInjector/index.js +63 -1
- package/src/events.js +622 -0
- package/src/helper/clipboard.js +59 -0
- package/src/helper/converter.js +202 -26
- package/src/helper/dom/domCheck.js +304 -0
- package/src/helper/dom/domQuery.js +669 -0
- package/src/helper/dom/domUtils.js +557 -0
- package/src/helper/dom/index.js +12 -0
- package/src/helper/env.js +46 -56
- package/src/helper/index.js +10 -4
- package/src/helper/keyCodeMap.js +183 -0
- package/src/helper/numbers.js +12 -8
- package/src/helper/unicode.js +9 -5
- package/src/langs/ckb.js +74 -4
- package/src/langs/cs.js +72 -2
- package/src/langs/da.js +73 -3
- package/src/langs/de.js +73 -4
- package/src/langs/en.js +23 -3
- package/src/langs/es.js +73 -4
- package/src/langs/fa.js +75 -3
- package/src/langs/fr.js +73 -3
- package/src/langs/he.js +73 -4
- package/src/langs/hu.js +230 -0
- package/src/langs/index.js +7 -3
- package/src/langs/it.js +70 -1
- package/src/langs/ja.js +72 -4
- package/src/langs/km.js +230 -0
- package/src/langs/ko.js +22 -2
- package/src/langs/lv.js +74 -5
- package/src/langs/nl.js +73 -4
- package/src/langs/pl.js +73 -4
- package/src/langs/pt_br.js +70 -1
- package/src/langs/ro.js +74 -5
- package/src/langs/ru.js +73 -4
- package/src/langs/se.js +73 -4
- package/src/langs/tr.js +73 -1
- package/src/langs/{ua.js → uk.js} +75 -6
- package/src/langs/ur.js +77 -8
- package/src/langs/zh_cn.js +74 -5
- package/src/modules/ApiManager.js +77 -54
- package/src/modules/Browser.js +667 -0
- package/src/modules/ColorPicker.js +162 -102
- package/src/modules/Controller.js +273 -142
- package/src/modules/Figure.js +925 -484
- package/src/modules/FileManager.js +121 -69
- package/src/modules/HueSlider.js +113 -61
- package/src/modules/Modal.js +291 -122
- package/src/modules/ModalAnchorEditor.js +383 -234
- package/src/modules/SelectMenu.js +270 -168
- package/src/modules/_DragHandle.js +2 -1
- package/src/modules/index.js +3 -3
- package/src/plugins/browser/audioGallery.js +83 -0
- package/src/plugins/browser/fileBrowser.js +103 -0
- package/src/plugins/browser/fileGallery.js +83 -0
- package/src/plugins/browser/imageGallery.js +81 -0
- package/src/plugins/browser/videoGallery.js +103 -0
- package/src/plugins/command/blockquote.js +40 -27
- package/src/plugins/command/exportPDF.js +134 -0
- package/src/plugins/command/fileUpload.js +229 -162
- package/src/plugins/command/list_bulleted.js +83 -47
- package/src/plugins/command/list_numbered.js +83 -47
- package/src/plugins/dropdown/align.js +66 -54
- package/src/plugins/dropdown/backgroundColor.js +63 -49
- package/src/plugins/dropdown/font.js +71 -47
- package/src/plugins/dropdown/fontColor.js +63 -48
- package/src/plugins/dropdown/formatBlock.js +70 -33
- package/src/plugins/dropdown/hr.js +92 -51
- package/src/plugins/dropdown/layout.js +37 -26
- package/src/plugins/dropdown/lineHeight.js +54 -38
- package/src/plugins/dropdown/list.js +60 -45
- package/src/plugins/dropdown/paragraphStyle.js +51 -30
- package/src/plugins/dropdown/table.js +2003 -813
- package/src/plugins/dropdown/template.js +38 -26
- package/src/plugins/dropdown/textStyle.js +43 -31
- package/src/plugins/field/mention.js +147 -86
- package/src/plugins/index.js +32 -6
- package/src/plugins/input/fontSize.js +161 -108
- package/src/plugins/input/pageNavigator.js +70 -0
- package/src/plugins/modal/audio.js +358 -173
- package/src/plugins/modal/drawing.js +531 -0
- package/src/plugins/modal/embed.js +886 -0
- package/src/plugins/modal/image.js +674 -362
- package/src/plugins/modal/link.js +100 -71
- package/src/plugins/modal/math.js +367 -167
- package/src/plugins/modal/video.js +691 -335
- package/src/plugins/popup/anchor.js +222 -0
- package/src/suneditor.js +50 -13
- package/src/themes/dark.css +122 -0
- package/src/typedef.js +130 -0
- package/types/assets/icons/defaultIcons.d.ts +153 -0
- package/types/core/base/eventHandlers/handler_toolbar.d.ts +41 -0
- package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +40 -0
- package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +35 -0
- package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +45 -0
- package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +39 -0
- package/types/core/base/eventManager.d.ts +385 -0
- package/types/core/base/history.d.ts +81 -0
- package/types/core/class/char.d.ts +60 -0
- package/types/core/class/component.d.ts +212 -0
- package/types/core/class/format.d.ts +616 -0
- package/types/core/class/html.d.ts +422 -0
- package/types/core/class/menu.d.ts +126 -0
- package/types/core/class/nodeTransform.d.ts +93 -0
- package/types/core/class/offset.d.ts +522 -0
- package/types/core/class/selection.d.ts +188 -0
- package/types/core/class/shortcuts.d.ts +142 -0
- package/types/core/class/toolbar.d.ts +189 -0
- package/types/core/class/ui.d.ts +164 -0
- package/types/core/class/viewer.d.ts +140 -0
- package/types/core/editor.d.ts +610 -0
- package/types/core/section/actives.d.ts +46 -0
- package/types/core/section/constructor.d.ts +777 -0
- package/types/core/section/context.d.ts +45 -0
- package/types/core/section/documentType.d.ts +178 -0
- package/types/editorInjector/_classes.d.ts +41 -0
- package/types/editorInjector/_core.d.ts +92 -0
- package/types/editorInjector/index.d.ts +71 -0
- package/types/events.d.ts +273 -0
- package/types/helper/clipboard.d.ts +12 -0
- package/types/helper/converter.d.ts +197 -0
- package/types/helper/dom/domCheck.d.ts +189 -0
- package/types/helper/dom/domQuery.d.ts +223 -0
- package/types/helper/dom/domUtils.d.ts +226 -0
- package/types/helper/dom/index.d.ts +9 -0
- package/types/helper/env.d.ts +132 -0
- package/types/helper/index.d.ts +174 -0
- package/types/helper/keyCodeMap.d.ts +110 -0
- package/types/helper/numbers.d.ts +46 -0
- package/types/helper/unicode.d.ts +28 -0
- package/types/index.d.ts +120 -0
- package/{typings/Lang.d.ts → types/langs/_Lang.d.ts} +173 -103
- package/types/langs/ckb.d.ts +3 -0
- package/types/langs/cs.d.ts +3 -0
- package/types/langs/da.d.ts +3 -0
- package/types/langs/de.d.ts +3 -0
- package/types/langs/en.d.ts +3 -0
- package/types/langs/es.d.ts +3 -0
- package/types/langs/fa.d.ts +3 -0
- package/types/langs/fr.d.ts +3 -0
- package/types/langs/he.d.ts +3 -0
- package/types/langs/hu.d.ts +3 -0
- package/types/langs/index.d.ts +54 -0
- package/types/langs/it.d.ts +3 -0
- package/types/langs/ja.d.ts +3 -0
- package/types/langs/km.d.ts +3 -0
- package/types/langs/ko.d.ts +3 -0
- package/types/langs/lv.d.ts +3 -0
- package/types/langs/nl.d.ts +3 -0
- package/types/langs/pl.d.ts +3 -0
- package/types/langs/pt_br.d.ts +3 -0
- package/types/langs/ro.d.ts +3 -0
- package/types/langs/ru.d.ts +3 -0
- package/types/langs/se.d.ts +3 -0
- package/types/langs/tr.d.ts +3 -0
- package/types/langs/uk.d.ts +3 -0
- package/types/langs/ur.d.ts +3 -0
- package/types/langs/zh_cn.d.ts +3 -0
- package/types/modules/ApiManager.d.ts +125 -0
- package/types/modules/Browser.d.ts +326 -0
- package/types/modules/ColorPicker.d.ts +131 -0
- package/types/modules/Controller.d.ts +251 -0
- package/types/modules/Figure.d.ts +517 -0
- package/types/modules/FileManager.d.ts +202 -0
- package/types/modules/HueSlider.d.ts +136 -0
- package/types/modules/Modal.d.ts +111 -0
- package/types/modules/ModalAnchorEditor.d.ts +236 -0
- package/types/modules/SelectMenu.d.ts +194 -0
- package/types/modules/_DragHandle.d.ts +7 -0
- package/types/modules/index.d.ts +26 -0
- package/types/plugins/browser/audioGallery.d.ts +55 -0
- package/types/plugins/browser/fileBrowser.d.ts +64 -0
- package/types/plugins/browser/fileGallery.d.ts +55 -0
- package/types/plugins/browser/imageGallery.d.ts +51 -0
- package/types/plugins/browser/videoGallery.d.ts +57 -0
- package/types/plugins/command/blockquote.d.ts +28 -0
- package/types/plugins/command/exportPDF.d.ts +46 -0
- package/types/plugins/command/fileUpload.d.ts +156 -0
- package/types/plugins/command/list_bulleted.d.ts +46 -0
- package/types/plugins/command/list_numbered.d.ts +46 -0
- package/types/plugins/dropdown/align.d.ts +60 -0
- package/types/plugins/dropdown/backgroundColor.d.ts +63 -0
- package/types/plugins/dropdown/font.d.ts +54 -0
- package/types/plugins/dropdown/fontColor.d.ts +63 -0
- package/types/plugins/dropdown/formatBlock.d.ts +54 -0
- package/types/plugins/dropdown/hr.d.ts +71 -0
- package/types/plugins/dropdown/layout.d.ts +40 -0
- package/types/plugins/dropdown/lineHeight.d.ts +50 -0
- package/types/plugins/dropdown/list.d.ts +39 -0
- package/types/plugins/dropdown/paragraphStyle.d.ts +54 -0
- package/types/plugins/dropdown/table.d.ts +627 -0
- package/types/plugins/dropdown/template.d.ts +40 -0
- package/types/plugins/dropdown/textStyle.d.ts +41 -0
- package/types/plugins/field/mention.d.ts +102 -0
- package/types/plugins/index.d.ts +107 -0
- package/types/plugins/input/fontSize.d.ts +170 -0
- package/types/plugins/input/pageNavigator.d.ts +28 -0
- package/types/plugins/modal/audio.d.ts +269 -0
- package/types/plugins/modal/drawing.d.ts +246 -0
- package/types/plugins/modal/embed.d.ts +387 -0
- package/types/plugins/modal/image.d.ts +451 -0
- package/types/plugins/modal/link.d.ts +128 -0
- package/types/plugins/modal/math.d.ts +193 -0
- package/types/plugins/modal/video.d.ts +485 -0
- package/types/plugins/popup/anchor.d.ts +56 -0
- package/types/suneditor.d.ts +51 -0
- package/types/typedef.d.ts +233 -0
- package/.eslintignore +0 -7
- package/.eslintrc.json +0 -64
- package/src/assets/icons/_default.js +0 -194
- package/src/core/base/events.js +0 -320
- package/src/core/class/notice.js +0 -42
- package/src/helper/domUtils.js +0 -1177
- package/src/modules/FileBrowser.js +0 -271
- package/src/plugins/command/exportPdf.js +0 -168
- package/src/plugins/fileBrowser/imageGallery.js +0 -81
- package/src/themes/test.css +0 -61
- package/typings/CommandPlugin.d.ts +0 -8
- package/typings/DialogPlugin.d.ts +0 -20
- package/typings/FileBrowserPlugin.d.ts +0 -30
- package/typings/Module.d.ts +0 -15
- package/typings/Plugin.d.ts +0 -42
- package/typings/SubmenuPlugin.d.ts +0 -8
- package/typings/_classes.d.ts +0 -17
- package/typings/_colorPicker.d.ts +0 -60
- package/typings/_core.d.ts +0 -55
- package/typings/align.d.ts +0 -5
- package/typings/audio.d.ts +0 -5
- package/typings/backgroundColor.d.ts +0 -5
- package/typings/blockquote.d.ts +0 -5
- package/typings/char.d.ts +0 -39
- package/typings/component.d.ts +0 -38
- package/typings/context.d.ts +0 -39
- package/typings/converter.d.ts +0 -33
- package/typings/dialog.d.ts +0 -28
- package/typings/domUtils.d.ts +0 -361
- package/typings/editor.d.ts +0 -7
- package/typings/editor.ts +0 -542
- package/typings/env.d.ts +0 -70
- package/typings/eventManager.d.ts +0 -37
- package/typings/events.d.ts +0 -262
- package/typings/fileBrowser.d.ts +0 -42
- package/typings/fileManager.d.ts +0 -67
- package/typings/font.d.ts +0 -5
- package/typings/fontColor.d.ts +0 -5
- package/typings/fontSize.d.ts +0 -5
- package/typings/format.d.ts +0 -191
- package/typings/formatBlock.d.ts +0 -5
- package/typings/history.d.ts +0 -48
- package/typings/horizontalRule.d.ts +0 -5
- package/typings/image.d.ts +0 -5
- package/typings/imageGallery.d.ts +0 -5
- package/typings/index.d.ts +0 -21
- package/typings/index.modules.d.ts +0 -11
- package/typings/index.plugins.d.ts +0 -58
- package/typings/lineHeight.d.ts +0 -5
- package/typings/link.d.ts +0 -5
- package/typings/list.d.ts +0 -5
- package/typings/math.d.ts +0 -5
- package/typings/mediaContainer.d.ts +0 -25
- package/typings/mention.d.ts +0 -5
- package/typings/node.d.ts +0 -57
- package/typings/notice.d.ts +0 -16
- package/typings/numbers.d.ts +0 -29
- package/typings/offset.d.ts +0 -24
- package/typings/options.d.ts +0 -589
- package/typings/paragraphStyle.d.ts +0 -5
- package/typings/resizing.d.ts +0 -141
- package/typings/selection.d.ts +0 -94
- package/typings/shortcuts.d.ts +0 -13
- package/typings/suneditor.d.ts +0 -9
- package/typings/table.d.ts +0 -5
- package/typings/template.d.ts +0 -5
- package/typings/textStyle.d.ts +0 -5
- package/typings/toolbar.d.ts +0 -32
- package/typings/unicode.d.ts +0 -25
- package/typings/video.d.ts +0 -5
package/src/core/class/html.js
CHANGED
|
@@ -3,31 +3,51 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import CoreInjector from '../../editorInjector/_core';
|
|
6
|
-
import {
|
|
6
|
+
import { dom, converter, numbers, unicode, clipboard } from '../../helper';
|
|
7
7
|
|
|
8
8
|
const REQUIRED_DATA_ATTRS = 'data-se-[^\\s]+';
|
|
9
9
|
const V2_MIG_DATA_ATTRS = '|data-index|data-file-size|data-file-name|data-exp|data-font-size';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Omit<HTML & Partial<__se__EditorInjector>, 'html'>} HTMLThis
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @constructor
|
|
17
|
+
* @this {HTMLThis}
|
|
18
|
+
* @description All HTML related classes involved in the editing area
|
|
19
|
+
* @param {__se__EditorCore} editor - The root editor instance
|
|
20
|
+
*/
|
|
21
|
+
function HTML(editor) {
|
|
12
22
|
CoreInjector.call(this, editor);
|
|
23
|
+
const options = this.options;
|
|
13
24
|
|
|
14
25
|
// members
|
|
15
|
-
|
|
26
|
+
this.fontSizeUnitRegExp = null;
|
|
27
|
+
|
|
16
28
|
this._isAllowedClassName = function (v) {
|
|
17
29
|
return this.test(v) ? v : '';
|
|
18
|
-
}.bind(
|
|
30
|
+
}.bind(options.get('allowedClassName'));
|
|
19
31
|
this._allowHTMLComment = null;
|
|
20
32
|
this._disallowedStyleNodesRegExp = null;
|
|
21
33
|
this._htmlCheckWhitelistRegExp = null;
|
|
22
34
|
this._htmlCheckBlacklistRegExp = null;
|
|
23
35
|
this._elementWhitelistRegExp = null;
|
|
24
36
|
this._elementBlacklistRegExp = null;
|
|
37
|
+
/** @type {Object<string, RegExp>} */
|
|
25
38
|
this._attributeWhitelist = null;
|
|
26
|
-
|
|
39
|
+
/** @type {Object<string, RegExp>} */
|
|
27
40
|
this._attributeBlacklist = null;
|
|
41
|
+
this._attributeWhitelistRegExp = null;
|
|
28
42
|
this._attributeBlacklistRegExp = null;
|
|
43
|
+
this._cleanStyleTagKeyRegExp = null;
|
|
44
|
+
this._cleanStyleRegExpMap = null;
|
|
29
45
|
this._textStyleTags = options.get('_textStyleTags');
|
|
46
|
+
/** @type {Object<string, *>} */
|
|
30
47
|
this._autoStyleify = null;
|
|
48
|
+
this.__disallowedTagsRegExp = null;
|
|
49
|
+
this.__disallowedTagNameRegExp = null;
|
|
50
|
+
this.__allowedTagNameRegExp = null;
|
|
31
51
|
|
|
32
52
|
// clean styles
|
|
33
53
|
const tagStyles = options.get('tagStyles');
|
|
@@ -61,7 +81,7 @@ const HTML = function (editor) {
|
|
|
61
81
|
this._cleanStyleRegExpMap = stylesMap;
|
|
62
82
|
|
|
63
83
|
// font size unit
|
|
64
|
-
this.fontSizeUnitRegExp = new RegExp('\\d+(' +
|
|
84
|
+
this.fontSizeUnitRegExp = new RegExp('\\d+(' + options.get('fontSizeUnits').join('|') + ')$', 'i');
|
|
65
85
|
|
|
66
86
|
// extra tags
|
|
67
87
|
const allowedExtraTags = options.get('_allowedExtraTag');
|
|
@@ -96,6 +116,8 @@ const HTML = function (editor) {
|
|
|
96
116
|
// attributes
|
|
97
117
|
const regEndStr = '\\s*=\\s*(")[^"]*\\1';
|
|
98
118
|
const _wAttr = options.get('attributeWhitelist');
|
|
119
|
+
|
|
120
|
+
/** @type {Object<string, RegExp>} */
|
|
99
121
|
let tagsAttr = {};
|
|
100
122
|
let allAttr = '';
|
|
101
123
|
if (_wAttr) {
|
|
@@ -109,7 +131,7 @@ const HTML = function (editor) {
|
|
|
109
131
|
}
|
|
110
132
|
}
|
|
111
133
|
|
|
112
|
-
this._attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + (
|
|
134
|
+
this._attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + (options.get('v2Migration') ? V2_MIG_DATA_ATTRS : '') + ')' + regEndStr, 'ig');
|
|
113
135
|
this._attributeWhitelist = tagsAttr;
|
|
114
136
|
|
|
115
137
|
// blacklist
|
|
@@ -130,10 +152,10 @@ const HTML = function (editor) {
|
|
|
130
152
|
this._attributeBlacklist = tagsAttr;
|
|
131
153
|
|
|
132
154
|
// autoStyleify
|
|
133
|
-
if (
|
|
134
|
-
const convertTextTags =
|
|
155
|
+
if (options.get('autoStyleify').length > 0) {
|
|
156
|
+
const convertTextTags = options.get('convertTextTags');
|
|
135
157
|
const styleToTag = {};
|
|
136
|
-
|
|
158
|
+
options.get('autoStyleify').forEach((style) => {
|
|
137
159
|
switch (style) {
|
|
138
160
|
case 'bold':
|
|
139
161
|
styleToTag.bold = { regex: /font-weight\s*:\s*bold/i, tag: convertTextTags.bold };
|
|
@@ -151,18 +173,24 @@ const HTML = function (editor) {
|
|
|
151
173
|
});
|
|
152
174
|
this._autoStyleify = styleToTag;
|
|
153
175
|
}
|
|
154
|
-
}
|
|
176
|
+
}
|
|
155
177
|
|
|
156
178
|
HTML.prototype = {
|
|
157
179
|
/**
|
|
158
|
-
* @
|
|
159
|
-
* @
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* @param {string}
|
|
163
|
-
* @param {
|
|
164
|
-
* @param {
|
|
165
|
-
* @
|
|
180
|
+
* @this {HTMLThis}
|
|
181
|
+
* @description Filters an HTML string based on allowed and disallowed tags, with optional custom validation.
|
|
182
|
+
* - Removes blacklisted tags and keeps only whitelisted tags.
|
|
183
|
+
* - Allows custom validation functions to replace, modify, or remove elements.
|
|
184
|
+
* @param {string} html - The HTML string to be filtered.
|
|
185
|
+
* @param {Object} params - Filtering parameters.
|
|
186
|
+
* @param {string} [params.tagWhitelist] - Allowed tags, specified as a string with tags separated by '|'. (e.g. "div|p|span").
|
|
187
|
+
* @param {string} [params.tagBlacklist] - Disallowed tags, specified as a string with tags separated by '|'. (e.g. "script|iframe").
|
|
188
|
+
* @param {(node: Node) => Node | string | null} [params.validate] - Function to validate and modify individual nodes.
|
|
189
|
+
* - Return `null` to remove the node.
|
|
190
|
+
* - Return a `Node` to replace the current node.
|
|
191
|
+
* - Return a `string` to replace the node's outerHTML.
|
|
192
|
+
* @param {boolean} [params.validateAll] - Whether to apply validation to all nodes.
|
|
193
|
+
* @returns {string} - The filtered HTML string.
|
|
166
194
|
*/
|
|
167
195
|
filter(html, { tagWhitelist, tagBlacklist, validate, validateAll }) {
|
|
168
196
|
if (tagWhitelist) {
|
|
@@ -189,9 +217,7 @@ HTML.prototype = {
|
|
|
189
217
|
} else if (validateAll) {
|
|
190
218
|
const parseDocument = new DOMParser().parseFromString(html, 'text/html');
|
|
191
219
|
const compClass = ['.se-component', '.se-flex-component'];
|
|
192
|
-
const closestAny =
|
|
193
|
-
return compClass.some((selector) => element.closest(selector));
|
|
194
|
-
};
|
|
220
|
+
const closestAny = (element) => compClass.some((selector) => element.closest(selector));
|
|
195
221
|
parseDocument.body.querySelectorAll('*').forEach((node) => {
|
|
196
222
|
if (!closestAny(node)) {
|
|
197
223
|
const result = validate(node);
|
|
@@ -211,16 +237,19 @@ HTML.prototype = {
|
|
|
211
237
|
},
|
|
212
238
|
|
|
213
239
|
/**
|
|
214
|
-
* @
|
|
215
|
-
* @
|
|
216
|
-
* @param {
|
|
217
|
-
* @param {
|
|
218
|
-
*
|
|
219
|
-
* @param {string|RegExp|null}
|
|
220
|
-
* RegExp object
|
|
221
|
-
* @
|
|
240
|
+
* @this {HTMLThis}
|
|
241
|
+
* @description Cleans and compresses HTML code to suit the editor format.
|
|
242
|
+
* @param {string} html HTML string to clean and compress
|
|
243
|
+
* @param {Object} [options] Cleaning options
|
|
244
|
+
* @param {boolean} [options.forceFormat=false] If true, wraps text nodes without a format node in the format tag.
|
|
245
|
+
* @param {string|RegExp|null} [options.whitelist=null] Regular expression of allowed tags.
|
|
246
|
+
* Create RegExp object using helper.converter.createElementWhitelist method.
|
|
247
|
+
* @param {string|RegExp|null} [options.blacklist=null] Regular expression of disallowed tags.
|
|
248
|
+
* Create RegExp object using helper.converter.createElementBlacklist method.
|
|
249
|
+
* @param {boolean} [options._freeCodeViewMode=false] If true, the free code view mode is enabled.
|
|
250
|
+
* @returns {string} Cleaned and compressed HTML string
|
|
222
251
|
*/
|
|
223
|
-
clean(html,
|
|
252
|
+
clean(html, { forceFormat, whitelist, blacklist, _freeCodeViewMode } = {}) {
|
|
224
253
|
const { tagFilter, formatFilter, classFilter, styleNodeFilter, attrFilter, styleFilter } = this.options.get('strictMode');
|
|
225
254
|
let cleanData = '';
|
|
226
255
|
|
|
@@ -232,9 +261,9 @@ HTML.prototype = {
|
|
|
232
261
|
}
|
|
233
262
|
|
|
234
263
|
if (this._autoStyleify) {
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
html =
|
|
264
|
+
const domParser = new DOMParser().parseFromString(html, 'text/html');
|
|
265
|
+
dom.query.getListChildNodes(domParser.body, converter.spanToStyleNode.bind(null, this._autoStyleify));
|
|
266
|
+
html = domParser.body.innerHTML;
|
|
238
267
|
}
|
|
239
268
|
|
|
240
269
|
if (attrFilter || styleFilter) {
|
|
@@ -242,19 +271,33 @@ HTML.prototype = {
|
|
|
242
271
|
}
|
|
243
272
|
|
|
244
273
|
// get dom tree
|
|
245
|
-
const
|
|
274
|
+
const domParser = this._d.createRange().createContextualFragment(html);
|
|
246
275
|
|
|
247
276
|
if (tagFilter) {
|
|
248
277
|
try {
|
|
249
|
-
this._consistencyCheckOfHTML(
|
|
278
|
+
this._consistencyCheckOfHTML(domParser, this._htmlCheckWhitelistRegExp, this._htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode);
|
|
250
279
|
} catch (error) {
|
|
251
280
|
console.warn('[SUNEDITOR.html.clean.fail]', error.message);
|
|
252
281
|
}
|
|
253
282
|
}
|
|
254
283
|
|
|
284
|
+
// iframe placeholder parsing
|
|
285
|
+
const iframePlaceholders = domParser.querySelectorAll('[data-se-iframe-holder]');
|
|
286
|
+
for (let i = 0, len = iframePlaceholders.length; i < len; i++) {
|
|
287
|
+
/** @type {HTMLIFrameElement} */
|
|
288
|
+
const iframe = dom.utils.createElement('iframe');
|
|
289
|
+
|
|
290
|
+
const attrs = JSON.parse(iframePlaceholders[i].getAttribute('data-se-iframe-holder-attrs'));
|
|
291
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
292
|
+
iframe.setAttribute(key, value);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
iframePlaceholders[i].replaceWith(iframe);
|
|
296
|
+
}
|
|
297
|
+
|
|
255
298
|
if (this.options.get('__pluginRetainFilter')) {
|
|
256
299
|
this.editor._MELInfo.forEach((method, query) => {
|
|
257
|
-
const infoLst =
|
|
300
|
+
const infoLst = domParser.querySelectorAll(query);
|
|
258
301
|
for (let i = 0, len = infoLst.length; i < len; i++) {
|
|
259
302
|
method(infoLst[i]);
|
|
260
303
|
}
|
|
@@ -262,17 +305,17 @@ HTML.prototype = {
|
|
|
262
305
|
}
|
|
263
306
|
|
|
264
307
|
if (formatFilter) {
|
|
265
|
-
let domTree =
|
|
266
|
-
if (!
|
|
267
|
-
if (
|
|
308
|
+
let domTree = domParser.childNodes;
|
|
309
|
+
if (!forceFormat) forceFormat = this._isFormatData(domTree);
|
|
310
|
+
if (forceFormat) domTree = this._editFormat(domParser).childNodes;
|
|
268
311
|
|
|
269
312
|
for (let i = 0, len = domTree.length, t; i < len; i++) {
|
|
270
313
|
t = domTree[i];
|
|
271
314
|
if (this.__allowedTagNameRegExp.test(t.nodeName)) {
|
|
272
|
-
cleanData += t.outerHTML;
|
|
315
|
+
cleanData += /** @type {HTMLElement} */ (t).outerHTML;
|
|
273
316
|
continue;
|
|
274
317
|
}
|
|
275
|
-
cleanData += this._makeLine(t,
|
|
318
|
+
cleanData += this._makeLine(t, forceFormat);
|
|
276
319
|
}
|
|
277
320
|
}
|
|
278
321
|
|
|
@@ -293,29 +336,32 @@ HTML.prototype = {
|
|
|
293
336
|
},
|
|
294
337
|
|
|
295
338
|
/**
|
|
296
|
-
* @
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
* @param {
|
|
300
|
-
* @param {
|
|
301
|
-
* @param {boolean}
|
|
339
|
+
* @this {HTMLThis}
|
|
340
|
+
* @description Inserts an (HTML element / HTML string / plain string) at the selection range.
|
|
341
|
+
* - If "frameOptions.get('charCounter_max')" is exceeded when "html" is added, null is returned without addition.
|
|
342
|
+
* @param {Node|string} html HTML Element or HTML string or plain string
|
|
343
|
+
* @param {Object} [options] Options
|
|
344
|
+
* @param {boolean} [options.selectInserted=false] If true, selects the range of the inserted node.
|
|
345
|
+
* @param {boolean} [options.skipCharCount=false] If true, inserts even if "frameOptions.get('charCounter_max')" is exceeded.
|
|
346
|
+
* @param {boolean} [options.skipCleaning=false] If true, inserts the HTML string without refining it with html.clean.
|
|
347
|
+
* @returns {HTMLElement|null} The inserted element or null if insertion failed
|
|
302
348
|
*/
|
|
303
|
-
insert(html,
|
|
304
|
-
if (!this.editor.frameContext.get('
|
|
349
|
+
insert(html, { selectInserted, skipCharCount, skipCleaning } = {}) {
|
|
350
|
+
if (!this.editor.frameContext.get('wysiwyg').contains(this.selection.get().focusNode)) this.editor.focus();
|
|
305
351
|
|
|
306
352
|
if (typeof html === 'string') {
|
|
307
|
-
if (!
|
|
353
|
+
if (!skipCleaning) html = this.clean(html, { forceFormat: false, whitelist: null, blacklist: null });
|
|
308
354
|
try {
|
|
309
|
-
if (
|
|
310
|
-
const
|
|
311
|
-
const domTree =
|
|
355
|
+
if (dom.check.isListCell(this.format.getLine(this.selection.getNode(), null))) {
|
|
356
|
+
const domParser = this._d.createRange().createContextualFragment(html);
|
|
357
|
+
const domTree = domParser.childNodes;
|
|
312
358
|
if (this._isFormatData(domTree)) html = this._convertListCell(domTree);
|
|
313
359
|
}
|
|
314
360
|
|
|
315
|
-
const
|
|
316
|
-
const domTree =
|
|
361
|
+
const domParser = this._d.createRange().createContextualFragment(html);
|
|
362
|
+
const domTree = domParser.childNodes;
|
|
317
363
|
|
|
318
|
-
if (!
|
|
364
|
+
if (!skipCharCount) {
|
|
319
365
|
const type = this.editor.frameOptions.get('charCounter_type') === 'byte-html' ? 'outerHTML' : 'textContent';
|
|
320
366
|
let checkHTML = '';
|
|
321
367
|
for (let i = 0, len = domTree.length; i < len; i++) {
|
|
@@ -326,12 +372,12 @@ HTML.prototype = {
|
|
|
326
372
|
|
|
327
373
|
let c, a, t, prev, firstCon;
|
|
328
374
|
while ((c = domTree[0])) {
|
|
329
|
-
if (prev?.nodeType === 3 && a?.nodeType === 1 &&
|
|
375
|
+
if (prev?.nodeType === 3 && a?.nodeType === 1 && dom.check.isBreak(c)) {
|
|
330
376
|
prev = c;
|
|
331
|
-
|
|
377
|
+
dom.utils.removeItem(c);
|
|
332
378
|
continue;
|
|
333
379
|
}
|
|
334
|
-
t = this.insertNode(c, a, true);
|
|
380
|
+
t = this.insertNode(c, { afterNode: a, skipCharCount: true });
|
|
335
381
|
a = t.container || t;
|
|
336
382
|
if (!firstCon) firstCon = t;
|
|
337
383
|
prev = c;
|
|
@@ -340,24 +386,24 @@ HTML.prototype = {
|
|
|
340
386
|
if (prev?.nodeType === 3 && a?.nodeType === 1) a = prev;
|
|
341
387
|
const offset = a.nodeType === 3 ? t.endOffset || a.textContent.length : a.childNodes.length;
|
|
342
388
|
|
|
343
|
-
if (
|
|
389
|
+
if (selectInserted) {
|
|
344
390
|
this.selection.setRange(firstCon.container || firstCon, firstCon.startOffset || 0, a, offset);
|
|
345
391
|
} else if (!this.component.is(a)) {
|
|
346
392
|
this.selection.setRange(a, offset, a, offset);
|
|
347
393
|
}
|
|
348
394
|
} catch (error) {
|
|
349
395
|
if (this.editor.frameContext.get('isReadOnly') || this.editor.frameContext.get('isDisabled')) return;
|
|
350
|
-
throw Error(
|
|
396
|
+
throw Error(`[SUNEDITOR.html.insert.error] ${error.message}`);
|
|
351
397
|
}
|
|
352
398
|
} else {
|
|
353
399
|
if (this.component.is(html)) {
|
|
354
|
-
this.component.insert(html,
|
|
400
|
+
this.component.insert(html, { skipCharCount, skipSelection: false, skipHistory: false });
|
|
355
401
|
} else {
|
|
356
402
|
let afterNode = null;
|
|
357
|
-
if (this.format.isLine(html) ||
|
|
403
|
+
if (this.format.isLine(html) || dom.check.isMedia(html)) {
|
|
358
404
|
afterNode = this.format.getLine(this.selection.getNode(), null);
|
|
359
405
|
}
|
|
360
|
-
this.insertNode(html, afterNode,
|
|
406
|
+
this.insertNode(html, { afterNode, skipCharCount });
|
|
361
407
|
}
|
|
362
408
|
}
|
|
363
409
|
|
|
@@ -367,23 +413,26 @@ HTML.prototype = {
|
|
|
367
413
|
},
|
|
368
414
|
|
|
369
415
|
/**
|
|
416
|
+
* @this {HTMLThis}
|
|
370
417
|
* @description Delete selected node and insert argument value node and return.
|
|
371
|
-
* If the "afterNode" exists, it is inserted after the "afterNode"
|
|
372
|
-
* Inserting a text node merges with both text nodes on both sides and returns a new "{ container, startOffset, endOffset }".
|
|
418
|
+
* - If the "afterNode" exists, it is inserted after the "afterNode"
|
|
419
|
+
* - Inserting a text node merges with both text nodes on both sides and returns a new "{ container, startOffset, endOffset }".
|
|
373
420
|
* @param {Node} oNode Node to be inserted
|
|
374
|
-
* @param {
|
|
375
|
-
* @param {
|
|
421
|
+
* @param {Object} [options] Options
|
|
422
|
+
* @param {Node} [options.afterNode=null] If the node exists, it is inserted after the node
|
|
423
|
+
* @param {boolean} [options.skipCharCount=null] If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
|
|
376
424
|
* @returns {Object|Node|null}
|
|
377
425
|
*/
|
|
378
|
-
insertNode(oNode, afterNode,
|
|
426
|
+
insertNode(oNode, { afterNode, skipCharCount } = {}) {
|
|
379
427
|
let result = null;
|
|
380
|
-
if (this.editor.frameContext.get('isReadOnly') || (!
|
|
428
|
+
if (this.editor.frameContext.get('isReadOnly') || (!skipCharCount && !this.char.check(oNode))) {
|
|
381
429
|
return result;
|
|
382
430
|
}
|
|
383
431
|
|
|
432
|
+
let fNode = null;
|
|
384
433
|
let range = this.selection.getRange();
|
|
385
|
-
let line =
|
|
386
|
-
let insertListCell =
|
|
434
|
+
let line = dom.check.isListCell(range.commonAncestorContainer) ? range.commonAncestorContainer : this.format.getLine(this.selection.getNode(), null);
|
|
435
|
+
let insertListCell = dom.check.isListCell(line) && (dom.check.isListCell(oNode) || dom.check.isList(oNode));
|
|
387
436
|
|
|
388
437
|
let parentNode,
|
|
389
438
|
originAfter,
|
|
@@ -393,12 +442,12 @@ HTML.prototype = {
|
|
|
393
442
|
const isFormats = (!freeFormat && (this.format.isLine(oNode) || this.format.isBlock(oNode))) || this.component.isBasic(oNode);
|
|
394
443
|
|
|
395
444
|
if (insertListCell) {
|
|
396
|
-
tempAfterNode = afterNode ||
|
|
397
|
-
tempParentNode =
|
|
445
|
+
tempAfterNode = afterNode || dom.check.isList(oNode) ? line.lastChild : line.nextElementSibling;
|
|
446
|
+
tempParentNode = dom.check.isList(oNode) ? line : (tempAfterNode || line).parentNode;
|
|
398
447
|
}
|
|
399
448
|
|
|
400
|
-
if (!afterNode && (isFormats || this.component.isBasic(oNode) ||
|
|
401
|
-
const isEdge =
|
|
449
|
+
if (!afterNode && (isFormats || this.component.isBasic(oNode) || dom.check.isMedia(oNode))) {
|
|
450
|
+
const isEdge = dom.check.isEdgePoint(range.endContainer, range.endOffset, 'end');
|
|
402
451
|
const r = this.remove();
|
|
403
452
|
const container = r.container;
|
|
404
453
|
const prevContainer = container === r.prevContainer && range.collapsed ? null : r.prevContainer;
|
|
@@ -416,30 +465,30 @@ HTML.prototype = {
|
|
|
416
465
|
} else {
|
|
417
466
|
tempAfterNode = null;
|
|
418
467
|
}
|
|
419
|
-
} else if (insertListCell &&
|
|
420
|
-
line =
|
|
468
|
+
} else if (insertListCell && dom.check.isListCell(container) && !line.parentElement) {
|
|
469
|
+
line = dom.utils.createElement('LI');
|
|
421
470
|
tempParentNode.appendChild(line);
|
|
422
471
|
container.appendChild(tempParentNode);
|
|
423
472
|
tempAfterNode = null;
|
|
424
|
-
} else if (container.nodeType === 3 ||
|
|
425
|
-
const depthFormat =
|
|
426
|
-
return this.format.isBlock(current) ||
|
|
473
|
+
} else if (container.nodeType === 3 || dom.check.isBreak(container) || insertListCell) {
|
|
474
|
+
const depthFormat = dom.query.getParentElement(container, (current) => {
|
|
475
|
+
return this.format.isBlock(current) || dom.check.isListCell(current);
|
|
427
476
|
});
|
|
428
|
-
afterNode = this.nodeTransform.split(container, r.offset, !depthFormat ? 0 :
|
|
477
|
+
afterNode = this.nodeTransform.split(container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
|
|
429
478
|
if (!afterNode) {
|
|
430
479
|
tempAfterNode = afterNode = line;
|
|
431
480
|
} else if (insertListCell) {
|
|
432
481
|
if (line.contains(container)) {
|
|
433
|
-
const subList =
|
|
482
|
+
const subList = dom.check.isList(line.lastElementChild);
|
|
434
483
|
let newCell = null;
|
|
435
484
|
if (!isEdge) {
|
|
436
485
|
newCell = line.cloneNode(false);
|
|
437
|
-
newCell.appendChild(afterNode.textContent.trim() ? afterNode :
|
|
486
|
+
newCell.appendChild(afterNode.textContent.trim() ? afterNode : dom.utils.createTextNode(unicode.zeroWidthSpace));
|
|
438
487
|
}
|
|
439
488
|
if (subList) {
|
|
440
489
|
if (!newCell) {
|
|
441
490
|
newCell = line.cloneNode(false);
|
|
442
|
-
newCell.appendChild(
|
|
491
|
+
newCell.appendChild(dom.utils.createTextNode(unicode.zeroWidthSpace));
|
|
443
492
|
}
|
|
444
493
|
newCell.appendChild(line.lastElementChild);
|
|
445
494
|
}
|
|
@@ -472,18 +521,18 @@ HTML.prototype = {
|
|
|
472
521
|
/** No Select range node */
|
|
473
522
|
if (range.collapsed) {
|
|
474
523
|
if (commonCon.nodeType === 3) {
|
|
475
|
-
if (commonCon.textContent.length > endOff) afterNode = commonCon.splitText(endOff);
|
|
524
|
+
if (commonCon.textContent.length > endOff) afterNode = /** @type {Text} */ (commonCon).splitText(endOff);
|
|
476
525
|
else afterNode = commonCon.nextSibling;
|
|
477
526
|
} else {
|
|
478
|
-
if (!
|
|
527
|
+
if (!dom.check.isBreak(parentNode)) {
|
|
479
528
|
const c = parentNode.childNodes[startOff];
|
|
480
|
-
const focusNode = c?.nodeType === 3 &&
|
|
529
|
+
const focusNode = c?.nodeType === 3 && dom.check.isZeroWidth(c) && dom.check.isBreak(c.nextSibling) ? c.nextSibling : c;
|
|
481
530
|
if (focusNode) {
|
|
482
|
-
if (!focusNode.nextSibling &&
|
|
531
|
+
if (!focusNode.nextSibling && dom.check.isBreak(focusNode)) {
|
|
483
532
|
parentNode.removeChild(focusNode);
|
|
484
533
|
afterNode = null;
|
|
485
534
|
} else {
|
|
486
|
-
afterNode =
|
|
535
|
+
afterNode = dom.check.isBreak(focusNode) && !dom.check.isBreak(oNode) ? focusNode : focusNode.nextSibling;
|
|
487
536
|
}
|
|
488
537
|
} else {
|
|
489
538
|
afterNode = null;
|
|
@@ -498,15 +547,15 @@ HTML.prototype = {
|
|
|
498
547
|
const isSameContainer = startCon === endCon;
|
|
499
548
|
|
|
500
549
|
if (isSameContainer) {
|
|
501
|
-
if (
|
|
502
|
-
else afterNode = endCon.splitText(endOff);
|
|
550
|
+
if (dom.check.isEdgePoint(endCon, endOff)) afterNode = endCon.nextSibling;
|
|
551
|
+
else afterNode = /** @type {Text} */ (endCon).splitText(endOff);
|
|
503
552
|
|
|
504
553
|
let removeNode = startCon;
|
|
505
|
-
if (!
|
|
554
|
+
if (!dom.check.isEdgePoint(startCon, startOff)) removeNode = /** @type {Text} */ (startCon).splitText(startOff);
|
|
506
555
|
|
|
507
556
|
parentNode.removeChild(removeNode);
|
|
508
557
|
if (parentNode.childNodes.length === 0 && isFormats) {
|
|
509
|
-
parentNode.innerHTML = '<br>';
|
|
558
|
+
/** @type {HTMLElement} */ (parentNode).innerHTML = '<br>';
|
|
510
559
|
}
|
|
511
560
|
} else {
|
|
512
561
|
const removedTag = this.remove();
|
|
@@ -521,7 +570,7 @@ HTML.prototype = {
|
|
|
521
570
|
}
|
|
522
571
|
}
|
|
523
572
|
|
|
524
|
-
if (
|
|
573
|
+
if (dom.check.isListCell(container) && oNode.nodeType === 3) {
|
|
525
574
|
parentNode = container;
|
|
526
575
|
afterNode = null;
|
|
527
576
|
} else if (!isFormats && prevContainer) {
|
|
@@ -537,8 +586,8 @@ HTML.prototype = {
|
|
|
537
586
|
} else {
|
|
538
587
|
afterNode = null;
|
|
539
588
|
}
|
|
540
|
-
} else if (
|
|
541
|
-
parentNode = container.appendChild(
|
|
589
|
+
} else if (dom.check.isWysiwygFrame(container) && !this.format.isLine(oNode)) {
|
|
590
|
+
parentNode = container.appendChild(dom.utils.createElement(this.options.get('defaultLine')));
|
|
542
591
|
afterNode = null;
|
|
543
592
|
} else {
|
|
544
593
|
afterNode = isFormats ? endCon : container === prevContainer ? container.nextSibling : container;
|
|
@@ -562,37 +611,38 @@ HTML.prototype = {
|
|
|
562
611
|
// set node
|
|
563
612
|
const wysiwyg = this.editor.frameContext.get('wysiwyg');
|
|
564
613
|
if (!insertListCell) {
|
|
565
|
-
if (
|
|
614
|
+
if (dom.check.isWysiwygFrame(afterNode) || parentNode === wysiwyg.parentNode) {
|
|
566
615
|
parentNode = wysiwyg;
|
|
567
616
|
afterNode = null;
|
|
568
617
|
}
|
|
569
618
|
|
|
570
|
-
if (this.format.isLine(oNode) || this.format.isBlock(oNode) || (!
|
|
619
|
+
if (this.format.isLine(oNode) || this.format.isBlock(oNode) || (!dom.check.isListCell(parentNode) && this.component.isBasic(oNode))) {
|
|
571
620
|
const oldParent = parentNode;
|
|
572
|
-
if (
|
|
621
|
+
if (dom.check.isList(afterNode)) {
|
|
573
622
|
parentNode = afterNode;
|
|
574
623
|
afterNode = null;
|
|
575
|
-
} else if (
|
|
624
|
+
} else if (dom.check.isListCell(afterNode)) {
|
|
576
625
|
parentNode = afterNode.previousElementSibling || afterNode;
|
|
577
626
|
} else if (!originAfter && !afterNode) {
|
|
578
627
|
const r = this.remove();
|
|
579
|
-
const container = r.container.nodeType === 3 ? (
|
|
580
|
-
const rangeCon =
|
|
628
|
+
const container = r.container.nodeType === 3 ? (dom.check.isListCell(this.format.getLine(r.container, null)) ? r.container : this.format.getLine(r.container, null) || r.container.parentNode) : r.container;
|
|
629
|
+
const rangeCon = dom.check.isWysiwygFrame(container) || this.format.isBlock(container);
|
|
581
630
|
parentNode = rangeCon ? container : container.parentNode;
|
|
582
631
|
afterNode = rangeCon ? null : container.nextSibling;
|
|
583
632
|
}
|
|
584
633
|
|
|
585
|
-
if (oldParent.childNodes.length === 0 && parentNode !== oldParent)
|
|
634
|
+
if (oldParent.childNodes.length === 0 && parentNode !== oldParent) dom.utils.removeItem(oldParent);
|
|
586
635
|
}
|
|
587
636
|
|
|
588
|
-
if (isFormats && !freeFormat && !this.format.isBlock(parentNode) && !
|
|
637
|
+
if (isFormats && !freeFormat && !this.format.isBlock(parentNode) && !dom.check.isListCell(parentNode) && !dom.check.isWysiwygFrame(parentNode)) {
|
|
589
638
|
afterNode = parentNode.nextElementSibling;
|
|
590
639
|
parentNode = parentNode.parentNode;
|
|
591
640
|
}
|
|
592
641
|
|
|
593
|
-
if (
|
|
594
|
-
const
|
|
595
|
-
|
|
642
|
+
if (dom.check.isWysiwygFrame(parentNode) && (oNode.nodeType === 3 || dom.check.isBreak(oNode))) {
|
|
643
|
+
const formatNode = dom.utils.createElement(this.options.get('defaultLine'), null, oNode);
|
|
644
|
+
fNode = oNode;
|
|
645
|
+
oNode = formatNode;
|
|
596
646
|
}
|
|
597
647
|
}
|
|
598
648
|
|
|
@@ -609,12 +659,12 @@ HTML.prototype = {
|
|
|
609
659
|
afterNode = parentNode === afterNode ? parentNode.lastChild : afterNode;
|
|
610
660
|
}
|
|
611
661
|
|
|
612
|
-
if (
|
|
613
|
-
if (
|
|
662
|
+
if (dom.check.isListCell(oNode) && !dom.check.isList(parentNode)) {
|
|
663
|
+
if (dom.check.isListCell(parentNode)) {
|
|
614
664
|
afterNode = parentNode.nextElementSibling;
|
|
615
665
|
parentNode = parentNode.parentNode;
|
|
616
666
|
} else {
|
|
617
|
-
const ul =
|
|
667
|
+
const ul = dom.utils.createElement('ol');
|
|
618
668
|
parentNode.insertBefore(ul, afterNode);
|
|
619
669
|
parentNode = ul;
|
|
620
670
|
afterNode = null;
|
|
@@ -626,11 +676,11 @@ HTML.prototype = {
|
|
|
626
676
|
parentNode.insertBefore(oNode, afterNode);
|
|
627
677
|
|
|
628
678
|
if (insertListCell) {
|
|
629
|
-
if (
|
|
630
|
-
|
|
679
|
+
if (dom.check.isZeroWidth(line.textContent.trim())) {
|
|
680
|
+
dom.utils.removeItem(line);
|
|
631
681
|
oNode = oNode.lastChild;
|
|
632
682
|
} else {
|
|
633
|
-
const chList =
|
|
683
|
+
const chList = dom.utils.arrayFind(line.children, dom.check.isList);
|
|
634
684
|
if (chList) {
|
|
635
685
|
if (oNode !== chList) {
|
|
636
686
|
oNode.appendChild(chList);
|
|
@@ -640,8 +690,8 @@ HTML.prototype = {
|
|
|
640
690
|
oNode = parentNode;
|
|
641
691
|
}
|
|
642
692
|
|
|
643
|
-
if (
|
|
644
|
-
|
|
693
|
+
if (dom.check.isZeroWidth(line.textContent.trim())) {
|
|
694
|
+
dom.utils.removeItem(line);
|
|
645
695
|
}
|
|
646
696
|
}
|
|
647
697
|
}
|
|
@@ -650,6 +700,8 @@ HTML.prototype = {
|
|
|
650
700
|
parentNode.appendChild(oNode);
|
|
651
701
|
console.warn('[SUNEDITOR.html.insertNode.warn]', error);
|
|
652
702
|
} finally {
|
|
703
|
+
if (fNode) oNode = fNode;
|
|
704
|
+
|
|
653
705
|
const dupleNodes = parentNode.querySelectorAll('[data-duple]');
|
|
654
706
|
if (dupleNodes.length > 0) {
|
|
655
707
|
for (let i = 0, len = dupleNodes.length, d, c, ch, parent; i < len; i++) {
|
|
@@ -663,14 +715,14 @@ HTML.prototype = {
|
|
|
663
715
|
}
|
|
664
716
|
|
|
665
717
|
if (d === oNode) oNode = c;
|
|
666
|
-
|
|
718
|
+
dom.utils.removeItem(d);
|
|
667
719
|
}
|
|
668
720
|
}
|
|
669
721
|
|
|
670
722
|
if ((this.format.isLine(oNode) || this.component.isBasic(oNode)) && startCon === endCon) {
|
|
671
723
|
const cItem = this.format.getLine(commonCon, null);
|
|
672
|
-
if (cItem?.nodeType === 1 &&
|
|
673
|
-
|
|
724
|
+
if (cItem?.nodeType === 1 && dom.check.isEmptyLine(cItem)) {
|
|
725
|
+
dom.utils.removeItem(cItem);
|
|
674
726
|
}
|
|
675
727
|
}
|
|
676
728
|
|
|
@@ -681,39 +733,17 @@ HTML.prototype = {
|
|
|
681
733
|
if (!this.component.isBasic(oNode)) {
|
|
682
734
|
let offset = 1;
|
|
683
735
|
if (oNode.nodeType === 3) {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const nextText = !next || next.nodeType === 1 || domUtils.isZeroWith(next) ? '' : next.textContent;
|
|
688
|
-
|
|
689
|
-
if (previous && previousText.length > 0) {
|
|
690
|
-
oNode.textContent = previousText + oNode.textContent;
|
|
691
|
-
domUtils.removeItem(previous);
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (next && nextText.length > 0) {
|
|
695
|
-
oNode.textContent += nextText;
|
|
696
|
-
domUtils.removeItem(next);
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
const newRange = {
|
|
700
|
-
container: oNode,
|
|
701
|
-
startOffset: previousText.length,
|
|
702
|
-
endOffset: oNode.textContent.length - nextText.length
|
|
703
|
-
};
|
|
704
|
-
|
|
705
|
-
this.selection.setRange(oNode, newRange.startOffset, oNode, newRange.endOffset);
|
|
706
|
-
|
|
707
|
-
result = newRange;
|
|
708
|
-
} else if (!domUtils.isBreak(oNode) && !domUtils.isListCell(oNode) && this.format.isLine(parentNode)) {
|
|
736
|
+
offset = oNode.textContent.length;
|
|
737
|
+
this.selection.setRange(oNode, offset, oNode, offset);
|
|
738
|
+
} else if (!dom.check.isBreak(oNode) && !dom.check.isListCell(oNode) && this.format.isLine(parentNode)) {
|
|
709
739
|
let zeroWidth = null;
|
|
710
|
-
if (!oNode.previousSibling ||
|
|
711
|
-
zeroWidth =
|
|
740
|
+
if (!oNode.previousSibling || dom.check.isBreak(oNode.previousSibling)) {
|
|
741
|
+
zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
712
742
|
oNode.parentNode.insertBefore(zeroWidth, oNode);
|
|
713
743
|
}
|
|
714
744
|
|
|
715
|
-
if (!oNode.nextSibling ||
|
|
716
|
-
zeroWidth =
|
|
745
|
+
if (!oNode.nextSibling || dom.check.isBreak(oNode.nextSibling)) {
|
|
746
|
+
zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
717
747
|
oNode.parentNode.insertBefore(zeroWidth, oNode.nextSibling);
|
|
718
748
|
}
|
|
719
749
|
|
|
@@ -726,26 +756,27 @@ HTML.prototype = {
|
|
|
726
756
|
}
|
|
727
757
|
}
|
|
728
758
|
|
|
729
|
-
|
|
730
|
-
this.history.push(true);
|
|
731
|
-
result = oNode;
|
|
732
|
-
}
|
|
759
|
+
result = oNode;
|
|
733
760
|
}
|
|
734
761
|
|
|
735
762
|
return result;
|
|
736
763
|
},
|
|
737
764
|
|
|
738
765
|
/**
|
|
766
|
+
* @this {HTMLThis}
|
|
739
767
|
* @description Delete the selected range.
|
|
740
|
-
*
|
|
741
|
-
*
|
|
768
|
+
* @returns {{container: Node, offset: number, commonCon?: Node|null, prevContainer?: Node|null}}
|
|
769
|
+
* - container: "the last element after deletion"
|
|
770
|
+
* - offset: "offset"
|
|
771
|
+
* - commonCon: "commonAncestorContainer"
|
|
772
|
+
* - prevContainer: "previousElementSibling Of the deleted area"
|
|
742
773
|
*/
|
|
743
774
|
remove() {
|
|
744
775
|
this.selection._resetRangeToTextNode();
|
|
745
776
|
|
|
746
777
|
const range = this.selection.getRange();
|
|
747
778
|
const isStartEdge = range.startOffset === 0;
|
|
748
|
-
const isEndEdge =
|
|
779
|
+
const isEndEdge = dom.check.isEdgePoint(range.endContainer, range.endOffset, 'end');
|
|
749
780
|
let prevContainer = null;
|
|
750
781
|
let startPrevEl = null;
|
|
751
782
|
let endNextEl = null;
|
|
@@ -765,62 +796,66 @@ HTML.prototype = {
|
|
|
765
796
|
let endCon = range.endContainer;
|
|
766
797
|
let startOff = range.startOffset;
|
|
767
798
|
let endOff = range.endOffset;
|
|
768
|
-
const commonCon = range.commonAncestorContainer.nodeType === 3 && range.commonAncestorContainer.parentNode === startCon.parentNode ? startCon.parentNode : range.commonAncestorContainer;
|
|
799
|
+
const commonCon = /** @type {HTMLElement} */ (range.commonAncestorContainer.nodeType === 3 && range.commonAncestorContainer.parentNode === startCon.parentNode ? startCon.parentNode : range.commonAncestorContainer);
|
|
769
800
|
if (commonCon === startCon && commonCon === endCon) {
|
|
770
801
|
if (this.component.isBasic(commonCon)) {
|
|
771
802
|
const compInfo = this.component.get(commonCon);
|
|
772
803
|
const compContainer = compInfo.container;
|
|
773
|
-
const parent = compContainer.
|
|
804
|
+
const parent = compContainer.parentElement;
|
|
774
805
|
|
|
775
806
|
const next = compContainer.nextSibling || compContainer.previousSibling;
|
|
776
807
|
const nextOffset = next === compContainer.previousSibling ? next?.textContent?.length || 1 : 0;
|
|
777
808
|
const parentNext = parent.nextElementSibling || parent.previousElementSibling;
|
|
778
809
|
const parentNextOffset = parentNext === parent.previousElementSibling ? parentNext?.textContent?.length || 1 : 0;
|
|
779
810
|
|
|
780
|
-
|
|
811
|
+
dom.utils.removeItem(compContainer);
|
|
781
812
|
|
|
782
813
|
if (this.format.isLine(parent)) {
|
|
783
814
|
if (parent.childNodes.length === 0) {
|
|
784
|
-
|
|
815
|
+
dom.utils.removeItem(parent);
|
|
785
816
|
return {
|
|
786
817
|
container: parentNext,
|
|
787
|
-
offset: parentNextOffset
|
|
818
|
+
offset: parentNextOffset,
|
|
819
|
+
commonCon
|
|
788
820
|
};
|
|
789
821
|
} else {
|
|
790
822
|
return {
|
|
791
823
|
container: next,
|
|
792
|
-
offset: nextOffset
|
|
824
|
+
offset: nextOffset,
|
|
825
|
+
commonCon
|
|
793
826
|
};
|
|
794
827
|
}
|
|
795
828
|
} else {
|
|
796
829
|
return {
|
|
797
830
|
container: parentNext,
|
|
798
|
-
offset: parentNextOffset
|
|
831
|
+
offset: parentNextOffset,
|
|
832
|
+
commonCon
|
|
799
833
|
};
|
|
800
834
|
}
|
|
801
835
|
} else {
|
|
802
836
|
if ((commonCon.nodeType === 1 && startOff === 0 && endOff === 1) || (commonCon.nodeType === 3 && startOff === 0 && endOff === commonCon.textContent.length)) {
|
|
803
|
-
const nextEl =
|
|
804
|
-
const prevEl =
|
|
837
|
+
const nextEl = dom.query.getNextDeepestNode(commonCon, this.editor.frameContext.get('wysiwyg'));
|
|
838
|
+
const prevEl = dom.query.getPreviousDeepestNode(commonCon, this.editor.frameContext.get('wysiwyg'));
|
|
805
839
|
const line = this.format.getLine(commonCon);
|
|
806
|
-
|
|
840
|
+
dom.utils.removeItem(commonCon);
|
|
807
841
|
|
|
808
842
|
let rEl = nextEl || prevEl;
|
|
809
843
|
let rOffset = nextEl ? 0 : rEl?.nodeType === 3 ? rEl.textContent.length : 1;
|
|
810
844
|
|
|
811
845
|
const npEl = this.format.getLine(rEl) || this.component.get(rEl);
|
|
812
846
|
if (line !== npEl) {
|
|
813
|
-
rEl = npEl;
|
|
847
|
+
rEl = /** @type {Node} */ (npEl);
|
|
814
848
|
rOffset = rOffset === 0 ? 0 : 1;
|
|
815
849
|
}
|
|
816
850
|
|
|
817
|
-
if (
|
|
818
|
-
|
|
851
|
+
if (dom.check.isZeroWidth(line) && !line.contains(rEl)) {
|
|
852
|
+
dom.utils.removeItem(line);
|
|
819
853
|
}
|
|
820
854
|
|
|
821
855
|
return {
|
|
822
856
|
container: rEl,
|
|
823
|
-
offset: rOffset
|
|
857
|
+
offset: rOffset,
|
|
858
|
+
commonCon
|
|
824
859
|
};
|
|
825
860
|
}
|
|
826
861
|
|
|
@@ -833,15 +868,17 @@ HTML.prototype = {
|
|
|
833
868
|
if (!startCon || !endCon)
|
|
834
869
|
return {
|
|
835
870
|
container: commonCon,
|
|
836
|
-
offset: 0
|
|
871
|
+
offset: 0,
|
|
872
|
+
commonCon
|
|
837
873
|
};
|
|
838
874
|
|
|
839
875
|
if (startCon === endCon && range.collapsed) {
|
|
840
|
-
if (
|
|
876
|
+
if (dom.check.isZeroWidth(startCon.textContent?.substring(startOff))) {
|
|
841
877
|
return {
|
|
842
878
|
container: startCon,
|
|
843
879
|
offset: startOff,
|
|
844
|
-
prevContainer: startCon && startCon.parentNode ? startCon : null
|
|
880
|
+
prevContainer: startCon && startCon.parentNode ? startCon : null,
|
|
881
|
+
commonCon
|
|
845
882
|
};
|
|
846
883
|
}
|
|
847
884
|
}
|
|
@@ -849,9 +886,9 @@ HTML.prototype = {
|
|
|
849
886
|
let beforeNode = null;
|
|
850
887
|
let afterNode = null;
|
|
851
888
|
|
|
852
|
-
const childNodes =
|
|
853
|
-
let startIndex =
|
|
854
|
-
let endIndex =
|
|
889
|
+
const childNodes = dom.query.getListChildNodes(commonCon, null);
|
|
890
|
+
let startIndex = dom.utils.getArrayIndex(childNodes, startCon);
|
|
891
|
+
let endIndex = dom.utils.getArrayIndex(childNodes, endCon);
|
|
855
892
|
|
|
856
893
|
if (childNodes.length > 0 && startIndex > -1 && endIndex > -1) {
|
|
857
894
|
for (let i = startIndex + 1, startNode = startCon; i >= 0; i--) {
|
|
@@ -870,25 +907,28 @@ HTML.prototype = {
|
|
|
870
907
|
}
|
|
871
908
|
} else {
|
|
872
909
|
if (childNodes.length === 0) {
|
|
873
|
-
if (this.format.isLine(commonCon) || this.format.isBlock(commonCon) ||
|
|
910
|
+
if (this.format.isLine(commonCon) || this.format.isBlock(commonCon) || dom.check.isWysiwygFrame(commonCon) || dom.check.isBreak(commonCon) || dom.check.isMedia(commonCon)) {
|
|
874
911
|
return {
|
|
875
912
|
container: commonCon,
|
|
876
|
-
offset: 0
|
|
913
|
+
offset: 0,
|
|
914
|
+
commonCon
|
|
877
915
|
};
|
|
878
|
-
} else if (commonCon
|
|
916
|
+
} else if (dom.check.isText(commonCon)) {
|
|
879
917
|
return {
|
|
880
918
|
container: commonCon,
|
|
881
|
-
offset: endOff
|
|
919
|
+
offset: endOff,
|
|
920
|
+
commonCon
|
|
882
921
|
};
|
|
883
922
|
}
|
|
884
923
|
childNodes.push(commonCon);
|
|
885
924
|
startCon = endCon = commonCon;
|
|
886
925
|
} else {
|
|
887
926
|
startCon = endCon = childNodes[0];
|
|
888
|
-
if (
|
|
927
|
+
if (dom.check.isBreak(startCon) || dom.check.isZeroWidth(startCon)) {
|
|
889
928
|
return {
|
|
890
|
-
container:
|
|
891
|
-
offset: 0
|
|
929
|
+
container: dom.check.isMedia(commonCon) ? commonCon : startCon,
|
|
930
|
+
offset: 0,
|
|
931
|
+
commonCon
|
|
892
932
|
};
|
|
893
933
|
}
|
|
894
934
|
}
|
|
@@ -896,29 +936,33 @@ HTML.prototype = {
|
|
|
896
936
|
startIndex = endIndex = 0;
|
|
897
937
|
}
|
|
898
938
|
|
|
939
|
+
const _isText = dom.check.isText;
|
|
940
|
+
const _isElement = dom.check.isElement;
|
|
899
941
|
for (let i = startIndex; i <= endIndex; i++) {
|
|
900
|
-
const item = childNodes[i];
|
|
942
|
+
const item = /** @type {Text} */ (childNodes[i]);
|
|
901
943
|
|
|
902
|
-
if (item
|
|
944
|
+
if (_isText(item) && (item.data === undefined || item.length === 0)) {
|
|
903
945
|
this._nodeRemoveListItem(item);
|
|
904
946
|
continue;
|
|
905
947
|
}
|
|
906
948
|
|
|
907
949
|
if (item === startCon) {
|
|
908
|
-
if (startCon
|
|
950
|
+
if (_isElement(startCon)) {
|
|
909
951
|
if (this.component.is(startCon)) continue;
|
|
910
|
-
else beforeNode =
|
|
952
|
+
else beforeNode = dom.utils.createTextNode(startCon.textContent);
|
|
911
953
|
} else {
|
|
954
|
+
const sc = /** @type {Text} */ (startCon);
|
|
955
|
+
const ec = /** @type {Text} */ (endCon);
|
|
912
956
|
if (item === endCon) {
|
|
913
|
-
beforeNode =
|
|
957
|
+
beforeNode = dom.utils.createTextNode(sc.substringData(0, startOff) + ec.substringData(endOff, ec.length - endOff));
|
|
914
958
|
offset = startOff;
|
|
915
959
|
} else {
|
|
916
|
-
beforeNode =
|
|
960
|
+
beforeNode = dom.utils.createTextNode(sc.substringData(0, startOff));
|
|
917
961
|
}
|
|
918
962
|
}
|
|
919
963
|
|
|
920
964
|
if (beforeNode.length > 0) {
|
|
921
|
-
startCon.data = beforeNode.data;
|
|
965
|
+
/** @type {Text} */ (startCon).data = beforeNode.data;
|
|
922
966
|
} else {
|
|
923
967
|
this._nodeRemoveListItem(startCon);
|
|
924
968
|
}
|
|
@@ -928,15 +972,15 @@ HTML.prototype = {
|
|
|
928
972
|
}
|
|
929
973
|
|
|
930
974
|
if (item === endCon) {
|
|
931
|
-
if (endCon
|
|
932
|
-
|
|
933
|
-
else afterNode = domUtils.createTextNode(endCon.textContent);
|
|
975
|
+
if (_isText(endCon)) {
|
|
976
|
+
afterNode = dom.utils.createTextNode(endCon.substringData(endOff, endCon.length - endOff));
|
|
934
977
|
} else {
|
|
935
|
-
|
|
978
|
+
if (this.component.is(endCon)) continue;
|
|
979
|
+
else afterNode = dom.utils.createTextNode(endCon.textContent);
|
|
936
980
|
}
|
|
937
981
|
|
|
938
982
|
if (afterNode.length > 0) {
|
|
939
|
-
endCon.data = afterNode.data;
|
|
983
|
+
/** @type {Text} */ (endCon).data = afterNode.data;
|
|
940
984
|
} else {
|
|
941
985
|
this._nodeRemoveListItem(endCon);
|
|
942
986
|
}
|
|
@@ -947,14 +991,25 @@ HTML.prototype = {
|
|
|
947
991
|
this._nodeRemoveListItem(item);
|
|
948
992
|
}
|
|
949
993
|
|
|
950
|
-
const endUl =
|
|
951
|
-
const startLi =
|
|
994
|
+
const endUl = dom.query.getParentElement(endCon, 'ul');
|
|
995
|
+
const startLi = dom.query.getParentElement(startCon, 'li');
|
|
952
996
|
if (endUl && startLi && startLi.contains(endUl)) {
|
|
953
997
|
container = endUl.previousSibling;
|
|
954
998
|
offset = container.textContent.length;
|
|
955
999
|
} else {
|
|
956
1000
|
container = endCon && endCon.parentNode ? endCon : startCon && startCon.parentNode ? startCon : range.endContainer || range.startContainer;
|
|
957
|
-
|
|
1001
|
+
if (isStartEdge || isEndEdge) {
|
|
1002
|
+
if (isEndEdge) {
|
|
1003
|
+
if (container.nodeType === 1 && container.childNodes.length === 0) {
|
|
1004
|
+
container.appendChild(dom.utils.createElement('BR'));
|
|
1005
|
+
offset = 1;
|
|
1006
|
+
} else {
|
|
1007
|
+
offset = container.textContent.length;
|
|
1008
|
+
}
|
|
1009
|
+
} else {
|
|
1010
|
+
offset = 0;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
958
1013
|
}
|
|
959
1014
|
|
|
960
1015
|
if (!this.format.getLine(container) && !(startCon && startCon.parentNode)) {
|
|
@@ -967,7 +1022,7 @@ HTML.prototype = {
|
|
|
967
1022
|
}
|
|
968
1023
|
}
|
|
969
1024
|
|
|
970
|
-
if (!
|
|
1025
|
+
if (!dom.check.isWysiwygFrame(container) && container.childNodes.length === 0) {
|
|
971
1026
|
const rc = this.nodeTransform.removeAllParents(container, null, null);
|
|
972
1027
|
if (rc) container = rc.sc || rc.ec || this.editor.frameContext.get('wysiwyg');
|
|
973
1028
|
}
|
|
@@ -976,21 +1031,24 @@ HTML.prototype = {
|
|
|
976
1031
|
this.selection.setRange(container, offset, container, offset);
|
|
977
1032
|
|
|
978
1033
|
return {
|
|
979
|
-
container
|
|
980
|
-
offset
|
|
981
|
-
prevContainer
|
|
1034
|
+
container,
|
|
1035
|
+
offset,
|
|
1036
|
+
prevContainer,
|
|
1037
|
+
commonCon
|
|
982
1038
|
};
|
|
983
1039
|
},
|
|
984
1040
|
|
|
985
1041
|
/**
|
|
1042
|
+
* @this {HTMLThis}
|
|
986
1043
|
* @description Gets the current content
|
|
987
|
-
* @param {
|
|
1044
|
+
* @param {Object} [options] Options
|
|
1045
|
+
* @param {boolean} [options.withFrame=false] Gets the current content with containing parent div.sun-editor-editable (<div class="sun-editor-editable">{content}</div>).
|
|
988
1046
|
* Ignored for targetOptions.get('iframe_fullPage') is true.
|
|
989
|
-
* @param {boolean} includeFullPage Return only the content of the body without headers when the "iframe_fullPage" option is true
|
|
990
|
-
* @param {number|Array
|
|
991
|
-
* @returns {string|
|
|
1047
|
+
* @param {boolean} [options.includeFullPage=false] Return only the content of the body without headers when the "iframe_fullPage" option is true
|
|
1048
|
+
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1049
|
+
* @returns {string|Object<*, string>}
|
|
992
1050
|
*/
|
|
993
|
-
get(withFrame, includeFullPage, rootKey) {
|
|
1051
|
+
get({ withFrame, includeFullPage, rootKey } = {}) {
|
|
994
1052
|
if (!rootKey) rootKey = [this.status.rootKey];
|
|
995
1053
|
else if (!Array.isArray(rootKey)) rootKey = [rootKey];
|
|
996
1054
|
|
|
@@ -1000,23 +1058,23 @@ HTML.prototype = {
|
|
|
1000
1058
|
this.editor.changeFrameContext(rootKey[i]);
|
|
1001
1059
|
|
|
1002
1060
|
const fc = this.editor.frameContext;
|
|
1003
|
-
const renderHTML =
|
|
1004
|
-
const editableEls =
|
|
1061
|
+
const renderHTML = dom.utils.createElement('DIV', null, this._convertToCode(fc.get('wysiwyg'), true));
|
|
1062
|
+
const editableEls = dom.query.getListChildren(renderHTML, (current) => current.hasAttribute('contenteditable'));
|
|
1005
1063
|
|
|
1006
1064
|
for (let j = 0, jlen = editableEls.length; j < jlen; j++) {
|
|
1007
1065
|
editableEls[j].removeAttribute('contenteditable');
|
|
1008
1066
|
}
|
|
1009
1067
|
|
|
1010
|
-
const content =
|
|
1068
|
+
const content = renderHTML.innerHTML;
|
|
1011
1069
|
if (this.editor.frameOptions.get('iframe_fullPage')) {
|
|
1012
1070
|
if (includeFullPage) {
|
|
1013
|
-
const attrs =
|
|
1014
|
-
r =
|
|
1071
|
+
const attrs = dom.utils.getAttributesToString(fc.get('_wd').body, ['contenteditable']);
|
|
1072
|
+
r = `<!DOCTYPE html><html>${fc.get('_wd').head.outerHTML}<body ${attrs}>${content}</body></html>`;
|
|
1015
1073
|
} else {
|
|
1016
1074
|
r = content;
|
|
1017
1075
|
}
|
|
1018
1076
|
} else {
|
|
1019
|
-
r = withFrame ?
|
|
1077
|
+
r = withFrame ? `<div class="${this.options.get('_editableClass') + '' + (this.options.get('_rtl') ? ' se-rtl' : '')}">${content}</div>` : renderHTML.innerHTML;
|
|
1020
1078
|
}
|
|
1021
1079
|
|
|
1022
1080
|
resultValue[rootKey[i]] = r;
|
|
@@ -1027,13 +1085,15 @@ HTML.prototype = {
|
|
|
1027
1085
|
},
|
|
1028
1086
|
|
|
1029
1087
|
/**
|
|
1030
|
-
* @
|
|
1031
|
-
* @
|
|
1032
|
-
* @param {
|
|
1088
|
+
* @this {HTMLThis}
|
|
1089
|
+
* @description Sets the HTML string to the editor content
|
|
1090
|
+
* @param {string} html HTML string
|
|
1091
|
+
* @param {Object} [options] Options
|
|
1092
|
+
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1033
1093
|
*/
|
|
1034
|
-
set(html, rootKey) {
|
|
1094
|
+
set(html, { rootKey } = {}) {
|
|
1035
1095
|
this.selection.removeRange();
|
|
1036
|
-
const convertValue = html === null || html === undefined ? '' : this.clean(html, true, null, null);
|
|
1096
|
+
const convertValue = html === null || html === undefined ? '' : this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
|
|
1037
1097
|
|
|
1038
1098
|
if (!rootKey) rootKey = [this.status.rootKey];
|
|
1039
1099
|
else if (!Array.isArray(rootKey)) rootKey = [rootKey];
|
|
@@ -1053,20 +1113,22 @@ HTML.prototype = {
|
|
|
1053
1113
|
},
|
|
1054
1114
|
|
|
1055
1115
|
/**
|
|
1116
|
+
* @this {HTMLThis}
|
|
1056
1117
|
* @description Add content to the end of content.
|
|
1057
|
-
* @param {string}
|
|
1058
|
-
* @param {
|
|
1118
|
+
* @param {string} html Content to Input
|
|
1119
|
+
* @param {Object} [options] Options
|
|
1120
|
+
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1059
1121
|
*/
|
|
1060
|
-
add(
|
|
1122
|
+
add(html, { rootKey } = {}) {
|
|
1061
1123
|
if (!rootKey) rootKey = [this.status.rootKey];
|
|
1062
1124
|
else if (!Array.isArray(rootKey)) rootKey = [rootKey];
|
|
1063
1125
|
|
|
1064
1126
|
for (let i = 0; i < rootKey.length; i++) {
|
|
1065
1127
|
this.editor.changeFrameContext(rootKey[i]);
|
|
1066
|
-
const convertValue = this.clean(
|
|
1128
|
+
const convertValue = this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
|
|
1067
1129
|
|
|
1068
1130
|
if (!this.editor.frameContext.get('isCodeView')) {
|
|
1069
|
-
const temp =
|
|
1131
|
+
const temp = dom.utils.createElement('DIV', null, convertValue);
|
|
1070
1132
|
const children = temp.children;
|
|
1071
1133
|
const len = children.length;
|
|
1072
1134
|
for (let j = 0; j < len; j++) {
|
|
@@ -1082,11 +1144,54 @@ HTML.prototype = {
|
|
|
1082
1144
|
},
|
|
1083
1145
|
|
|
1084
1146
|
/**
|
|
1147
|
+
* @this {HTMLThis}
|
|
1148
|
+
* @description Gets the current content to JSON data
|
|
1149
|
+
* @param {Object} [options] Options
|
|
1150
|
+
* @param {boolean} [options.withFrame=false] Gets the current content with containing parent div.sun-editor-editable (<div class="sun-editor-editable">{content}</div>).
|
|
1151
|
+
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1152
|
+
* @returns {Object<string, *>} JSON data
|
|
1153
|
+
*/
|
|
1154
|
+
getJson({ withFrame, rootKey } = {}) {
|
|
1155
|
+
return converter.htmlToJson(this.get({ withFrame, rootKey }));
|
|
1156
|
+
},
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* @this {HTMLThis}
|
|
1160
|
+
* @description Sets the JSON data to the editor content
|
|
1161
|
+
* @param {Object<string, *>} jsdonData HTML string
|
|
1162
|
+
* @param {Object} [options] Options
|
|
1163
|
+
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1164
|
+
*/
|
|
1165
|
+
setJson(jsdonData, { rootKey } = {}) {
|
|
1166
|
+
this.set(converter.jsonToHtml(jsdonData), { rootKey });
|
|
1167
|
+
},
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* @this {HTMLThis}
|
|
1171
|
+
* @description Call "clipboard.write" to copy the contents and display a success/failure toast message.
|
|
1172
|
+
* @param {Element|Text|string} content Content to be copied to the clipboard
|
|
1173
|
+
* @returns {Promise<boolean>} Success or failure
|
|
1174
|
+
*/
|
|
1175
|
+
async copy(content) {
|
|
1176
|
+
try {
|
|
1177
|
+
await clipboard.write(content);
|
|
1178
|
+
this.editor.ui.showToast(this.lang.message_copy_success, this.options.get('toastMessageTime').copy);
|
|
1179
|
+
return true;
|
|
1180
|
+
} catch (err) {
|
|
1181
|
+
console.error('[SUNEDITOR.html.copy.fail] :', err);
|
|
1182
|
+
this.editor.ui.showToast(this.lang.message_copy_fail, this.options.get('toastMessageTime').copy, 'error');
|
|
1183
|
+
return false;
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* @this {HTMLThis}
|
|
1085
1189
|
* @description Sets the content of the iframe's head tag and body tag when using the "iframe" or "iframe_fullPage" option.
|
|
1086
|
-
* @param {
|
|
1087
|
-
* @param {
|
|
1190
|
+
* @param {{head: string, body: string}} ctx { head: HTML string, body: HTML string}
|
|
1191
|
+
* @param {Object} [options] Options
|
|
1192
|
+
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1088
1193
|
*/
|
|
1089
|
-
setFullPage(ctx, rootKey) {
|
|
1194
|
+
setFullPage(ctx, { rootKey } = {}) {
|
|
1090
1195
|
if (!this.editor.frameOptions.get('iframe')) return false;
|
|
1091
1196
|
|
|
1092
1197
|
if (!rootKey) rootKey = [this.status.rootKey];
|
|
@@ -1095,24 +1200,30 @@ HTML.prototype = {
|
|
|
1095
1200
|
for (let i = 0; i < rootKey.length; i++) {
|
|
1096
1201
|
this.editor.changeFrameContext(rootKey[i]);
|
|
1097
1202
|
if (ctx.head) this.editor.frameContext.get('_wd').head.innerHTML = ctx.head.replace(this.__disallowedTagsRegExp, '');
|
|
1098
|
-
if (ctx.body) this.editor.frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, true, null, null);
|
|
1203
|
+
if (ctx.body) this.editor.frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, { forceFormat: true, whitelist: null, blacklist: null });
|
|
1099
1204
|
this.editor._resetComponents();
|
|
1100
1205
|
}
|
|
1101
1206
|
},
|
|
1102
1207
|
|
|
1103
1208
|
/**
|
|
1209
|
+
* @this {HTMLThis}
|
|
1104
1210
|
* @description HTML code compression
|
|
1105
1211
|
* @param {string} html HTML string
|
|
1106
1212
|
* @returns {string} HTML string
|
|
1107
1213
|
*/
|
|
1108
1214
|
compress(html) {
|
|
1109
|
-
return html
|
|
1215
|
+
return html
|
|
1216
|
+
.replace(/\n/g, '')
|
|
1217
|
+
.replace(/(>)(?:\s+)(<)/g, '$1$2')
|
|
1218
|
+
.trim();
|
|
1110
1219
|
},
|
|
1111
1220
|
|
|
1112
1221
|
/**
|
|
1222
|
+
* @private
|
|
1223
|
+
* @this {HTMLThis}
|
|
1113
1224
|
* @description construct wysiwyg area element to html string
|
|
1114
|
-
* @param {
|
|
1115
|
-
* @param {
|
|
1225
|
+
* @param {Node|string} html WYSIWYG element (this.editor.frameContext.get('wysiwyg')) or HTML string.
|
|
1226
|
+
* @param {boolean} comp If true, does not line break and indentation of tags.
|
|
1116
1227
|
* @returns {string}
|
|
1117
1228
|
*/
|
|
1118
1229
|
_convertToCode(html, comp) {
|
|
@@ -1125,8 +1236,8 @@ HTML.prototype = {
|
|
|
1125
1236
|
};
|
|
1126
1237
|
const brChar = comp ? '' : '\n';
|
|
1127
1238
|
|
|
1128
|
-
|
|
1129
|
-
indentSize =
|
|
1239
|
+
const codeSize = comp ? 0 : this.status.codeIndentSize * 1;
|
|
1240
|
+
const indentSize = codeSize > 0 ? new Array(codeSize + 1).join(' ') : '';
|
|
1130
1241
|
|
|
1131
1242
|
(function recursionFunc(element, indent) {
|
|
1132
1243
|
const children = element.childNodes;
|
|
@@ -1144,22 +1255,22 @@ HTML.prototype = {
|
|
|
1144
1255
|
continue;
|
|
1145
1256
|
}
|
|
1146
1257
|
if (node.nodeType === 3) {
|
|
1147
|
-
if (!
|
|
1258
|
+
if (!dom.check.isList(node.parentElement)) returnHTML += converter.htmlToEntity(/^\n+$/.test(/** @type {Text} */ (node).data) ? '' : /** @type {Text} */ (node).data);
|
|
1148
1259
|
continue;
|
|
1149
1260
|
}
|
|
1150
1261
|
if (node.childNodes.length === 0) {
|
|
1151
|
-
returnHTML += (/^HR$/i.test(node.nodeName) ? brChar : '') + (/^PRE$/i.test(node.parentElement.nodeName) && /^BR$/i.test(node.nodeName) ? '' : elementIndent) + node.outerHTML + br;
|
|
1262
|
+
returnHTML += (/^HR$/i.test(node.nodeName) ? brChar : '') + (/^PRE$/i.test(node.parentElement.nodeName) && /^BR$/i.test(node.nodeName) ? '' : elementIndent) + /** @type {HTMLElement} */ (node).outerHTML + br;
|
|
1152
1263
|
continue;
|
|
1153
1264
|
}
|
|
1154
1265
|
|
|
1155
|
-
if (!node.outerHTML) {
|
|
1266
|
+
if (!(/** @type {HTMLElement} */ (node).outerHTML)) {
|
|
1156
1267
|
returnHTML += new XMLSerializer().serializeToString(node);
|
|
1157
1268
|
} else {
|
|
1158
1269
|
tag = node.nodeName.toLowerCase();
|
|
1159
1270
|
tagIndent = elementIndent || nodeRegTest ? indent : '';
|
|
1160
|
-
returnHTML += (lineBR || (elementRegTest ? '' : br)) + tagIndent + node.outerHTML.match(wRegExp('<' + tag + '[^>]*>', 'i'))[0] + br;
|
|
1161
|
-
recursionFunc(node, indent + indentSize
|
|
1162
|
-
returnHTML += (/\n$/.test(returnHTML) ? tagIndent : '') + '</' + tag + '>' + (lineBR || br || elementRegTest ? brChar :
|
|
1271
|
+
returnHTML += (lineBR || (elementRegTest ? '' : br)) + tagIndent + /** @type {HTMLElement} */ (node).outerHTML.match(wRegExp('<' + tag + '[^>]*>', 'i'))[0] + br;
|
|
1272
|
+
recursionFunc(node, indent + indentSize + '');
|
|
1273
|
+
returnHTML += (/\n$/.test(returnHTML) ? tagIndent : '') + '</' + tag + '>' + (lineBR || br || elementRegTest ? brChar : /^(TH|TD)$/i.test(node.nodeName) ? brChar : '');
|
|
1163
1274
|
}
|
|
1164
1275
|
}
|
|
1165
1276
|
})(wDoc, '');
|
|
@@ -1167,24 +1278,31 @@ HTML.prototype = {
|
|
|
1167
1278
|
return returnHTML.trim() + brChar;
|
|
1168
1279
|
},
|
|
1169
1280
|
|
|
1281
|
+
/**
|
|
1282
|
+
* @private
|
|
1283
|
+
* @this {HTMLThis}
|
|
1284
|
+
* @description Checks whether the given list item node should be removed and handles necessary clean-up.
|
|
1285
|
+
* @param {Node} item The list item node to be checked.
|
|
1286
|
+
*/
|
|
1170
1287
|
_nodeRemoveListItem(item) {
|
|
1171
1288
|
const line = this.format.getLine(item, null);
|
|
1172
|
-
|
|
1289
|
+
dom.utils.removeItem(item);
|
|
1173
1290
|
|
|
1174
|
-
if (!
|
|
1291
|
+
if (!dom.check.isListCell(line)) return;
|
|
1175
1292
|
|
|
1176
1293
|
this.nodeTransform.removeAllParents(line, null, null);
|
|
1177
1294
|
|
|
1178
|
-
if (
|
|
1179
|
-
line.insertBefore(
|
|
1295
|
+
if (dom.check.isList(line?.firstChild)) {
|
|
1296
|
+
line.insertBefore(dom.utils.createTextNode(unicode.zeroWidthSpace), line.firstChild);
|
|
1180
1297
|
}
|
|
1181
1298
|
},
|
|
1182
1299
|
|
|
1183
1300
|
/**
|
|
1301
|
+
* @private
|
|
1302
|
+
* @this {HTMLThis}
|
|
1184
1303
|
* @description Recursive function when used to place a node in "BrLine" in "html.insertNode"
|
|
1185
1304
|
* @param {Node} oNode Node to be inserted
|
|
1186
1305
|
* @returns {Node} "oNode"
|
|
1187
|
-
* @private
|
|
1188
1306
|
*/
|
|
1189
1307
|
_setIntoFreeFormat(oNode) {
|
|
1190
1308
|
const parentNode = oNode.parentNode;
|
|
@@ -1206,8 +1324,8 @@ HTML.prototype = {
|
|
|
1206
1324
|
parentNode.insertBefore(lastONode, oNode);
|
|
1207
1325
|
}
|
|
1208
1326
|
|
|
1209
|
-
if (oNode.childNodes.length === 0)
|
|
1210
|
-
oNode =
|
|
1327
|
+
if (oNode.childNodes.length === 0) dom.utils.removeItem(oNode);
|
|
1328
|
+
oNode = dom.utils.createElement('BR');
|
|
1211
1329
|
parentNode.insertBefore(oNode, lastONode.nextSibling);
|
|
1212
1330
|
}
|
|
1213
1331
|
|
|
@@ -1215,43 +1333,47 @@ HTML.prototype = {
|
|
|
1215
1333
|
},
|
|
1216
1334
|
|
|
1217
1335
|
/**
|
|
1336
|
+
* @private
|
|
1337
|
+
* @this {HTMLThis}
|
|
1218
1338
|
* @description Returns HTML string according to tag type and configurati isExcludeFormat.
|
|
1219
1339
|
* @param {Node} node Node
|
|
1220
|
-
* @param {boolean}
|
|
1221
|
-
* @private
|
|
1340
|
+
* @param {boolean} forceFormat If true, text nodes that do not have a format node is wrapped with the format tag.
|
|
1222
1341
|
*/
|
|
1223
|
-
_makeLine(node,
|
|
1342
|
+
_makeLine(node, forceFormat) {
|
|
1224
1343
|
const defaultLine = this.options.get('defaultLine');
|
|
1225
1344
|
// element
|
|
1226
1345
|
if (node.nodeType === 1) {
|
|
1227
1346
|
if (this.__disallowedTagNameRegExp.test(node.nodeName)) return '';
|
|
1228
|
-
if (
|
|
1347
|
+
if (dom.check.isExcludeFormat(node)) return node.outerHTML;
|
|
1229
1348
|
|
|
1230
1349
|
const ch =
|
|
1231
|
-
|
|
1232
|
-
return
|
|
1350
|
+
dom.query.getListChildNodes(node, (current) => {
|
|
1351
|
+
return dom.check.isSpanWithoutAttr(current) && !dom.query.getParentElement(current, dom.check.isExcludeFormat);
|
|
1233
1352
|
}) || [];
|
|
1234
|
-
for (let i = ch.length - 1; i >= 0; i--) {
|
|
1235
|
-
|
|
1353
|
+
for (let i = ch.length - 1, c; i >= 0; i--) {
|
|
1354
|
+
c = /** @type {HTMLElement} */ (ch[i]);
|
|
1355
|
+
c.outerHTML = c.innerHTML;
|
|
1236
1356
|
}
|
|
1237
1357
|
|
|
1238
1358
|
if (
|
|
1239
|
-
!
|
|
1359
|
+
!forceFormat ||
|
|
1240
1360
|
this.format.isLine(node) ||
|
|
1241
1361
|
this.format.isBlock(node) ||
|
|
1242
1362
|
this.component.is(node) ||
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
(
|
|
1363
|
+
dom.check.isMedia(node) ||
|
|
1364
|
+
dom.check.isFigure(node) ||
|
|
1365
|
+
(dom.check.isAnchor(node) && dom.check.isMedia(node.firstElementChild))
|
|
1246
1366
|
) {
|
|
1247
|
-
|
|
1367
|
+
const n = /** @type {HTMLElement} */ (node);
|
|
1368
|
+
return dom.check.isSpanWithoutAttr(node) ? n.innerHTML : n.outerHTML;
|
|
1248
1369
|
} else {
|
|
1249
|
-
|
|
1370
|
+
const n = /** @type {HTMLElement} */ (node);
|
|
1371
|
+
return '<' + defaultLine + '>' + (dom.check.isSpanWithoutAttr(node) ? n.innerHTML : n.outerHTML) + '</' + defaultLine + '>';
|
|
1250
1372
|
}
|
|
1251
1373
|
}
|
|
1252
1374
|
// text
|
|
1253
1375
|
if (node.nodeType === 3) {
|
|
1254
|
-
if (!
|
|
1376
|
+
if (!forceFormat) return converter.htmlToEntity(node.textContent);
|
|
1255
1377
|
const textArray = node.textContent.split(/\n/g);
|
|
1256
1378
|
let html = '';
|
|
1257
1379
|
for (let i = 0, tLen = textArray.length, text; i < tLen; i++) {
|
|
@@ -1269,43 +1391,48 @@ HTML.prototype = {
|
|
|
1269
1391
|
},
|
|
1270
1392
|
|
|
1271
1393
|
/**
|
|
1394
|
+
* @private
|
|
1395
|
+
* @this {HTMLThis}
|
|
1272
1396
|
* @description Fix tags that do not fit the editor format.
|
|
1273
|
-
* @param {
|
|
1397
|
+
* @param {DocumentFragment} documentFragment Document fragment "DOCUMENT_FRAGMENT_NODE" (nodeType === 11)
|
|
1274
1398
|
* @param {RegExp} htmlCheckWhitelistRegExp Editor tags whitelist
|
|
1275
1399
|
* @param {RegExp} htmlCheckBlacklistRegExp Editor tags blacklist
|
|
1276
|
-
* @
|
|
1400
|
+
* @param {boolean} tagFilter Tag filter option
|
|
1401
|
+
* @param {boolean} formatFilter Format filter option
|
|
1402
|
+
* @param {boolean} classFilter Class name filter option
|
|
1403
|
+
* @param {boolean} _freeCodeViewMode Enforces strict HTML validation based on the editor`s policy
|
|
1277
1404
|
*/
|
|
1278
|
-
_consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter) {
|
|
1405
|
+
_consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode) {
|
|
1279
1406
|
const removeTags = [],
|
|
1280
1407
|
emptyTags = [],
|
|
1281
1408
|
wrongList = [],
|
|
1282
1409
|
withoutFormatCells = [];
|
|
1283
1410
|
|
|
1284
1411
|
// wrong position
|
|
1285
|
-
const wrongTags =
|
|
1412
|
+
const wrongTags = dom.query.getListChildNodes(documentFragment, (current) => {
|
|
1286
1413
|
if (formatFilter && current.nodeType !== 1) {
|
|
1287
|
-
if (
|
|
1414
|
+
if (dom.check.isList(current.parentElement)) removeTags.push(current);
|
|
1288
1415
|
return false;
|
|
1289
1416
|
}
|
|
1290
1417
|
|
|
1291
1418
|
// tag filter
|
|
1292
1419
|
if (tagFilter) {
|
|
1293
1420
|
// white list
|
|
1294
|
-
if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 &&
|
|
1421
|
+
if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && dom.check.isExcludeFormat(current))) {
|
|
1295
1422
|
removeTags.push(current);
|
|
1296
1423
|
return false;
|
|
1297
1424
|
}
|
|
1298
1425
|
}
|
|
1299
1426
|
|
|
1300
|
-
const nrtag = !
|
|
1427
|
+
const nrtag = !dom.query.getParentElement(current, dom.check.isExcludeFormat);
|
|
1301
1428
|
|
|
1302
1429
|
// formatFilter
|
|
1303
1430
|
if (formatFilter) {
|
|
1304
1431
|
// empty tags
|
|
1305
1432
|
if (
|
|
1306
|
-
!
|
|
1307
|
-
!
|
|
1308
|
-
!
|
|
1433
|
+
!dom.check.isTableElements(current) &&
|
|
1434
|
+
!dom.check.isListCell(current) &&
|
|
1435
|
+
!dom.check.isAnchor(current) &&
|
|
1309
1436
|
(this.format.isLine(current) || this.format.isBlock(current) || this.format.isTextStyleNode(current)) &&
|
|
1310
1437
|
current.childNodes.length === 0 &&
|
|
1311
1438
|
nrtag
|
|
@@ -1315,13 +1442,13 @@ HTML.prototype = {
|
|
|
1315
1442
|
}
|
|
1316
1443
|
|
|
1317
1444
|
// wrong list
|
|
1318
|
-
if (
|
|
1445
|
+
if (dom.check.isList(current.parentNode) && !dom.check.isList(current) && !dom.check.isListCell(current)) {
|
|
1319
1446
|
wrongList.push(current);
|
|
1320
1447
|
return false;
|
|
1321
1448
|
}
|
|
1322
1449
|
|
|
1323
1450
|
// table cells
|
|
1324
|
-
if (
|
|
1451
|
+
if (dom.check.isTableCell(current)) {
|
|
1325
1452
|
const fel = current.firstElementChild;
|
|
1326
1453
|
if (!this.format.isLine(fel) && !this.format.isBlock(fel) && !this.component.is(fel)) {
|
|
1327
1454
|
withoutFormatCells.push(current);
|
|
@@ -1345,16 +1472,17 @@ HTML.prototype = {
|
|
|
1345
1472
|
}
|
|
1346
1473
|
|
|
1347
1474
|
const result =
|
|
1475
|
+
!_freeCodeViewMode &&
|
|
1348
1476
|
current.parentNode !== documentFragment &&
|
|
1349
1477
|
nrtag &&
|
|
1350
|
-
((
|
|
1351
|
-
((this.format.isLine(current) || this.component.is(current)) && !this.format.isBlock(current.parentNode) && !
|
|
1478
|
+
((dom.check.isListCell(current) && !dom.check.isList(current.parentNode)) ||
|
|
1479
|
+
((this.format.isLine(current) || this.component.is(current)) && !this.format.isBlock(current.parentNode) && !dom.query.getParentElement(current, this.component.is.bind(this.component))));
|
|
1352
1480
|
|
|
1353
1481
|
return result;
|
|
1354
1482
|
});
|
|
1355
1483
|
|
|
1356
1484
|
for (let i = 0, len = removeTags.length; i < len; i++) {
|
|
1357
|
-
|
|
1485
|
+
dom.utils.removeItem(removeTags[i]);
|
|
1358
1486
|
}
|
|
1359
1487
|
|
|
1360
1488
|
const checkTags = [];
|
|
@@ -1363,7 +1491,7 @@ HTML.prototype = {
|
|
|
1363
1491
|
p = t.parentNode;
|
|
1364
1492
|
if (!p || !p.parentNode) continue;
|
|
1365
1493
|
|
|
1366
|
-
if (
|
|
1494
|
+
if (dom.query.getParentElement(t, dom.check.isListCell)) {
|
|
1367
1495
|
const cellChildren = t.childNodes;
|
|
1368
1496
|
for (let j = cellChildren.length - 1; len >= 0; j--) {
|
|
1369
1497
|
p.insertBefore(t, cellChildren[j]);
|
|
@@ -1377,13 +1505,13 @@ HTML.prototype = {
|
|
|
1377
1505
|
|
|
1378
1506
|
for (let i = 0, len = checkTags.length, t; i < len; i++) {
|
|
1379
1507
|
t = checkTags[i];
|
|
1380
|
-
if (
|
|
1381
|
-
|
|
1508
|
+
if (dom.check.isZeroWidth(t.textContent.trim())) {
|
|
1509
|
+
dom.utils.removeItem(t);
|
|
1382
1510
|
}
|
|
1383
1511
|
}
|
|
1384
1512
|
|
|
1385
1513
|
for (let i = 0, len = emptyTags.length; i < len; i++) {
|
|
1386
|
-
|
|
1514
|
+
dom.utils.removeItem(emptyTags[i]);
|
|
1387
1515
|
}
|
|
1388
1516
|
|
|
1389
1517
|
for (let i = 0, len = wrongList.length, t, tp, children, p; i < len; i++) {
|
|
@@ -1391,7 +1519,7 @@ HTML.prototype = {
|
|
|
1391
1519
|
p = t.parentNode;
|
|
1392
1520
|
if (!p) continue;
|
|
1393
1521
|
|
|
1394
|
-
tp =
|
|
1522
|
+
tp = dom.utils.createElement('LI');
|
|
1395
1523
|
|
|
1396
1524
|
if (this.format.isLine(t)) {
|
|
1397
1525
|
children = t.childNodes;
|
|
@@ -1399,7 +1527,7 @@ HTML.prototype = {
|
|
|
1399
1527
|
tp.appendChild(children[0]);
|
|
1400
1528
|
}
|
|
1401
1529
|
p.insertBefore(tp, t);
|
|
1402
|
-
|
|
1530
|
+
dom.utils.removeItem(t);
|
|
1403
1531
|
} else {
|
|
1404
1532
|
t = t.nextSibling;
|
|
1405
1533
|
tp.appendChild(wrongList[i]);
|
|
@@ -1409,17 +1537,18 @@ HTML.prototype = {
|
|
|
1409
1537
|
|
|
1410
1538
|
for (let i = 0, len = withoutFormatCells.length, t, f; i < len; i++) {
|
|
1411
1539
|
t = withoutFormatCells[i];
|
|
1412
|
-
f =
|
|
1540
|
+
f = dom.utils.createElement('DIV');
|
|
1413
1541
|
f.innerHTML = t.textContent.trim().length === 0 && t.children.length === 0 ? '<br>' : t.innerHTML;
|
|
1414
1542
|
t.innerHTML = f.outerHTML;
|
|
1415
1543
|
}
|
|
1416
1544
|
},
|
|
1417
1545
|
|
|
1418
1546
|
/**
|
|
1547
|
+
* @private
|
|
1548
|
+
* @this {HTMLThis}
|
|
1419
1549
|
* @description Removes attribute values such as style and converts tags that do not conform to the "html5" standard.
|
|
1420
1550
|
* @param {string} html HTML string
|
|
1421
1551
|
* @returns {string} HTML string
|
|
1422
|
-
* @private
|
|
1423
1552
|
*/
|
|
1424
1553
|
_styleNodeConvertor(html) {
|
|
1425
1554
|
if (!this._disallowedStyleNodesRegExp) return html;
|
|
@@ -1431,18 +1560,19 @@ HTML.prototype = {
|
|
|
1431
1560
|
},
|
|
1432
1561
|
|
|
1433
1562
|
/**
|
|
1434
|
-
* @description Determines if formatting is required and returns a domTree
|
|
1435
|
-
* @param {Element} dom documentFragment
|
|
1436
|
-
* @returns {Element}
|
|
1437
1563
|
* @private
|
|
1564
|
+
* @this {HTMLThis}
|
|
1565
|
+
* @description Determines if formatting is required and returns a domTree
|
|
1566
|
+
* @param {DocumentFragment} domFrag documentFragment
|
|
1567
|
+
* @returns {DocumentFragment}
|
|
1438
1568
|
*/
|
|
1439
|
-
_editFormat(
|
|
1569
|
+
_editFormat(domFrag) {
|
|
1440
1570
|
let value = '',
|
|
1441
1571
|
f;
|
|
1442
|
-
const tempTree =
|
|
1572
|
+
const tempTree = domFrag.childNodes;
|
|
1443
1573
|
|
|
1444
1574
|
for (let i = 0, len = tempTree.length, n; i < len; i++) {
|
|
1445
|
-
n = tempTree[i];
|
|
1575
|
+
n = /** @type {HTMLElement} */ (tempTree[i]);
|
|
1446
1576
|
if (this.__allowedTagNameRegExp.test(n.nodeName)) {
|
|
1447
1577
|
value += n.outerHTML;
|
|
1448
1578
|
continue;
|
|
@@ -1450,8 +1580,8 @@ HTML.prototype = {
|
|
|
1450
1580
|
|
|
1451
1581
|
if (n.nodeType === 8) {
|
|
1452
1582
|
value += '<!-- ' + n.textContent + ' -->';
|
|
1453
|
-
} else if (!this.format.isLine(n) && !this.format.isBlock(n) && !this.component.is(n) &&
|
|
1454
|
-
if (!f) f =
|
|
1583
|
+
} else if (!/meta/i.test(n.nodeName) && !this.format.isLine(n) && !this.format.isBlock(n) && !this.component.is(n) && !dom.check.isExcludeFormat(n)) {
|
|
1584
|
+
if (!f) f = dom.utils.createElement(this.options.get('defaultLine'));
|
|
1455
1585
|
f.appendChild(n);
|
|
1456
1586
|
i--;
|
|
1457
1587
|
len--;
|
|
@@ -1469,22 +1599,31 @@ HTML.prototype = {
|
|
|
1469
1599
|
return this._d.createRange().createContextualFragment(value);
|
|
1470
1600
|
},
|
|
1471
1601
|
|
|
1602
|
+
/**
|
|
1603
|
+
* @private
|
|
1604
|
+
* @this {HTMLThis}
|
|
1605
|
+
* @description Converts a list of DOM nodes into an HTML list structure.
|
|
1606
|
+
* - If the node is already a list, its innerHTML is used. If it is a block element,
|
|
1607
|
+
* - the function is called recursively.
|
|
1608
|
+
* @param {__se__NodeCollection} domTree List of DOM nodes to be converted.
|
|
1609
|
+
* @returns {string} The generated HTML list.
|
|
1610
|
+
*/
|
|
1472
1611
|
_convertListCell(domTree) {
|
|
1473
1612
|
let html = '';
|
|
1474
1613
|
|
|
1475
1614
|
for (let i = 0, len = domTree.length, node; i < len; i++) {
|
|
1476
1615
|
node = domTree[i];
|
|
1477
1616
|
if (node.nodeType === 1) {
|
|
1478
|
-
if (
|
|
1617
|
+
if (dom.check.isList(node)) {
|
|
1479
1618
|
html += node.innerHTML;
|
|
1480
|
-
} else if (
|
|
1619
|
+
} else if (dom.check.isListCell(node)) {
|
|
1481
1620
|
html += node.outerHTML;
|
|
1482
1621
|
} else if (this.format.isLine(node)) {
|
|
1483
1622
|
html += '<li>' + (node.innerHTML.trim() || '<br>') + '</li>';
|
|
1484
|
-
} else if (this.format.isBlock(node) && !
|
|
1485
|
-
html += this._convertListCell(node);
|
|
1623
|
+
} else if (this.format.isBlock(node) && !dom.check.isTableElements(node)) {
|
|
1624
|
+
html += this._convertListCell(node.children);
|
|
1486
1625
|
} else {
|
|
1487
|
-
html += '<li>' + node.outerHTML + '</li>';
|
|
1626
|
+
html += '<li>' + /** @type {HTMLElement} */ (node).outerHTML + '</li>';
|
|
1488
1627
|
}
|
|
1489
1628
|
} else {
|
|
1490
1629
|
html += '<li>' + (node.textContent || '<br>') + '</li>';
|
|
@@ -1494,12 +1633,19 @@ HTML.prototype = {
|
|
|
1494
1633
|
return html;
|
|
1495
1634
|
},
|
|
1496
1635
|
|
|
1636
|
+
/**
|
|
1637
|
+
* @private
|
|
1638
|
+
* @this {HTMLThis}
|
|
1639
|
+
* @description Checks whether the provided DOM nodes require formatting.
|
|
1640
|
+
* @param {NodeList} domTree List of DOM nodes to check.
|
|
1641
|
+
* @returns {boolean} True if formatting is required, otherwise false.
|
|
1642
|
+
*/
|
|
1497
1643
|
_isFormatData(domTree) {
|
|
1498
1644
|
let requireFormat = false;
|
|
1499
1645
|
|
|
1500
1646
|
for (let i = 0, len = domTree.length, t; i < len; i++) {
|
|
1501
1647
|
t = domTree[i];
|
|
1502
|
-
if (t.nodeType === 1 && !this.format.isTextStyleNode(t) && !
|
|
1648
|
+
if (t.nodeType === 1 && !this.format.isTextStyleNode(t) && !dom.check.isBreak(t) && !this.__disallowedTagNameRegExp.test(t.nodeName)) {
|
|
1503
1649
|
requireFormat = true;
|
|
1504
1650
|
break;
|
|
1505
1651
|
}
|
|
@@ -1508,6 +1654,16 @@ HTML.prototype = {
|
|
|
1508
1654
|
return requireFormat;
|
|
1509
1655
|
},
|
|
1510
1656
|
|
|
1657
|
+
/**
|
|
1658
|
+
* @private
|
|
1659
|
+
* @this {HTMLThis}
|
|
1660
|
+
* @description Cleans the inline style attributes of an HTML element.
|
|
1661
|
+
* - Extracts allowed styles and removes disallowed ones based on editor settings.
|
|
1662
|
+
* @param {string} m The full matched string from a regular expression.
|
|
1663
|
+
* @param {Array|null} v The list of allowed attributes.
|
|
1664
|
+
* @param {string} name The tag name of the element being cleaned.
|
|
1665
|
+
* @returns {Array} The updated list of allowed attributes including cleaned styles.
|
|
1666
|
+
*/
|
|
1511
1667
|
_cleanStyle(m, v, name) {
|
|
1512
1668
|
let sv = (m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/) || [])[0];
|
|
1513
1669
|
if (this._textStyleTags.includes(name) && !sv && (m.match(/<[^\s]+\s(.+)/) || [])[1]) {
|
|
@@ -1515,7 +1671,7 @@ HTML.prototype = {
|
|
|
1515
1671
|
const face = (m.match(/\sface="([^"]+)"/i) || [])[1];
|
|
1516
1672
|
const color = (m.match(/\scolor="([^"]+)"/i) || [])[1];
|
|
1517
1673
|
if (size || face || color) {
|
|
1518
|
-
sv = 'style="' + (size ? 'font-size:' + numbers.get(size / 3.333, 1) + 'rem;' : '') + (face ? 'font-family:' + face + ';' : '') + (color ? 'color:' + color + ';' : '') + '"';
|
|
1674
|
+
sv = 'style="' + (size ? 'font-size:' + numbers.get(Number(size) / 3.333, 1) + 'rem;' : '') + (face ? 'font-family:' + face + ';' : '') + (color ? 'color:' + color + ';' : '') + '"';
|
|
1519
1675
|
}
|
|
1520
1676
|
}
|
|
1521
1677
|
|
|
@@ -1538,8 +1694,8 @@ HTML.prototype = {
|
|
|
1538
1694
|
for (let i = 0, len = style.length, r; i < len; i++) {
|
|
1539
1695
|
r = style[i].match(/([a-zA-Z0-9-]+)(:)([^"']+)/);
|
|
1540
1696
|
if (r && !/inherit|initial|revert|unset/i.test(r[3])) {
|
|
1541
|
-
const k =
|
|
1542
|
-
const cs = this.editor.frameContext.get('wwComputedStyle')[k]
|
|
1697
|
+
const k = converter.kebabToCamelCase(r[1].trim());
|
|
1698
|
+
const cs = this.editor.frameContext.get('wwComputedStyle')[k]?.replace(/"/g, '');
|
|
1543
1699
|
const c = r[3].trim();
|
|
1544
1700
|
switch (k) {
|
|
1545
1701
|
case 'fontFamily':
|
|
@@ -1548,7 +1704,7 @@ HTML.prototype = {
|
|
|
1548
1704
|
case 'fontSize':
|
|
1549
1705
|
if (!this.plugins.fontSize) continue;
|
|
1550
1706
|
if (!this.fontSizeUnitRegExp.test(r[0])) {
|
|
1551
|
-
r[0] = r[0].replace((r[0].match(/:\s*([^;]+)/) || [])[1], converter.
|
|
1707
|
+
r[0] = r[0].replace((r[0].match(/:\s*([^;]+)/) || [])[1], converter.toFontUnit.bind(null, this.options.get('fontSizeUnits')[0]));
|
|
1552
1708
|
}
|
|
1553
1709
|
break;
|
|
1554
1710
|
case 'color':
|
|
@@ -1571,10 +1727,11 @@ HTML.prototype = {
|
|
|
1571
1727
|
},
|
|
1572
1728
|
|
|
1573
1729
|
/**
|
|
1730
|
+
* @private
|
|
1731
|
+
* @this {HTMLThis}
|
|
1574
1732
|
* @description Delete disallowed tags
|
|
1575
1733
|
* @param {string} html HTML string
|
|
1576
1734
|
* @returns {string}
|
|
1577
|
-
* @private
|
|
1578
1735
|
*/
|
|
1579
1736
|
_deleteDisallowedTags(html, whitelistRegExp, blacklistRegExp) {
|
|
1580
1737
|
if (whitelistRegExp.test('<font>')) {
|
|
@@ -1584,6 +1741,13 @@ HTML.prototype = {
|
|
|
1584
1741
|
return html.replace(whitelistRegExp, '').replace(blacklistRegExp, '');
|
|
1585
1742
|
},
|
|
1586
1743
|
|
|
1744
|
+
/**
|
|
1745
|
+
* @private
|
|
1746
|
+
* @this {HTMLThis}
|
|
1747
|
+
* @description Recursively checks for duplicate text style nodes within a given parent node.
|
|
1748
|
+
* @param {Node} oNode The node to check for duplicate styles.
|
|
1749
|
+
* @param {Node} parentNode The parent node where the duplicate check occurs.
|
|
1750
|
+
*/
|
|
1587
1751
|
_checkDuplicateNode(oNode, parentNode) {
|
|
1588
1752
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
1589
1753
|
const inst = this;
|
|
@@ -1596,6 +1760,15 @@ HTML.prototype = {
|
|
|
1596
1760
|
})(oNode);
|
|
1597
1761
|
},
|
|
1598
1762
|
|
|
1763
|
+
/**
|
|
1764
|
+
* @private
|
|
1765
|
+
* @this {HTMLThis}
|
|
1766
|
+
* @description Recursively checks for duplicate text style nodes within a given parent node.
|
|
1767
|
+
* - If duplicate styles are found, redundant attributes are removed.
|
|
1768
|
+
* @param {Node} oNode The node to check for duplicate styles.
|
|
1769
|
+
* @param {Node} parentNode The parent node where the duplicate check occurs.
|
|
1770
|
+
* @returns {Node} The cleaned node with redundant styles removed.
|
|
1771
|
+
*/
|
|
1599
1772
|
_dupleCheck(oNode, parentNode) {
|
|
1600
1773
|
if (!this.format.isTextStyleNode(oNode)) return;
|
|
1601
1774
|
|
|
@@ -1608,7 +1781,7 @@ HTML.prototype = {
|
|
|
1608
1781
|
const inst = this.format;
|
|
1609
1782
|
let duple = false;
|
|
1610
1783
|
(function recursionFunc(ancestor) {
|
|
1611
|
-
if (
|
|
1784
|
+
if (dom.check.isWysiwygFrame(ancestor) || !inst.isTextStyleNode(ancestor)) return;
|
|
1612
1785
|
if (ancestor.nodeName === nodeName) {
|
|
1613
1786
|
duple = true;
|
|
1614
1787
|
const styles = ancestor.style.cssText.match(/[^;]+;/g) || [];
|
|
@@ -1642,11 +1815,12 @@ HTML.prototype = {
|
|
|
1642
1815
|
};
|
|
1643
1816
|
|
|
1644
1817
|
/**
|
|
1818
|
+
* @private
|
|
1819
|
+
* @this {HTMLThis}
|
|
1645
1820
|
* @description Tag and tag attribute check RegExp function.
|
|
1646
1821
|
* @param {string} m RegExp value
|
|
1647
1822
|
* @param {string} t RegExp value
|
|
1648
1823
|
* @returns {string}
|
|
1649
|
-
* @private
|
|
1650
1824
|
*/
|
|
1651
1825
|
function CleanElements(attrFilter, styleFilter, m, t) {
|
|
1652
1826
|
if (/^<[a-z0-9]+:[a-z0-9]+/i.test(m)) return m;
|
|
@@ -1687,7 +1861,7 @@ function CleanElements(attrFilter, styleFilter, m, t) {
|
|
|
1687
1861
|
}
|
|
1688
1862
|
|
|
1689
1863
|
// figure
|
|
1690
|
-
if (
|
|
1864
|
+
if (dom.check.isMedia(tagName) || dom.check.isFigure(tagName)) {
|
|
1691
1865
|
const sv = m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/);
|
|
1692
1866
|
if (!v) v = [];
|
|
1693
1867
|
if (sv) v.push(sv[0]);
|
|
@@ -1703,6 +1877,12 @@ function CleanElements(attrFilter, styleFilter, m, t) {
|
|
|
1703
1877
|
return t;
|
|
1704
1878
|
}
|
|
1705
1879
|
|
|
1880
|
+
/**
|
|
1881
|
+
* @private
|
|
1882
|
+
* @description Get related list
|
|
1883
|
+
* @param {string} str Regular expression string
|
|
1884
|
+
* @param {string} str2 Regular expression string
|
|
1885
|
+
*/
|
|
1706
1886
|
function GetRegList(str, str2) {
|
|
1707
1887
|
return !str ? '^' : str === '*' ? '[a-z-]+' : !str2 ? str : str + '|' + str2;
|
|
1708
1888
|
}
|