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