suneditor 3.0.0-beta.9 → 3.0.0-rc.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/README.md +65 -57
- package/dist/suneditor-contents.min.css +1 -0
- package/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +110 -61
- package/src/assets/design/color.css +36 -17
- package/src/assets/design/size.css +2 -0
- package/src/assets/icons/defaultIcons.js +17 -2
- package/src/assets/suneditor-contents.css +51 -16
- package/src/assets/suneditor.css +116 -43
- package/src/core/config/contextProvider.js +288 -0
- package/src/core/config/eventManager.js +188 -0
- package/src/core/config/instanceCheck.js +59 -0
- package/src/core/config/optionProvider.js +452 -0
- package/src/core/editor.js +166 -1637
- package/src/core/event/actions/index.js +229 -0
- package/src/core/event/effects/common.registry.js +74 -0
- package/src/core/event/effects/keydown.registry.js +573 -0
- package/src/core/event/effects/ruleHelpers.js +148 -0
- package/src/core/event/eventOrchestrator.js +944 -0
- package/src/core/event/executor.js +27 -0
- package/src/core/{base/eventHandlers → event/handlers}/handler_toolbar.js +27 -28
- package/src/core/{base/eventHandlers → event/handlers}/handler_ww_clipboard.js +10 -8
- package/src/core/{base/eventHandlers → event/handlers}/handler_ww_dragDrop.js +22 -23
- package/src/core/event/handlers/handler_ww_input.js +75 -0
- package/src/core/event/handlers/handler_ww_key.js +228 -0
- package/src/core/event/handlers/handler_ww_mouse.js +166 -0
- package/src/core/event/ports.js +211 -0
- package/src/core/event/reducers/keydown.reducer.js +97 -0
- package/src/core/event/rules/keydown.rule.arrow.js +63 -0
- package/src/core/event/rules/keydown.rule.backspace.js +208 -0
- package/src/core/event/rules/keydown.rule.delete.js +132 -0
- package/src/core/event/rules/keydown.rule.enter.js +150 -0
- package/src/core/event/rules/keydown.rule.tab.js +35 -0
- package/src/core/event/support/defaultLineManager.js +136 -0
- package/src/core/event/support/selectionState.js +204 -0
- package/src/core/kernel/coreKernel.js +320 -0
- package/src/core/kernel/kernelInjector.js +19 -0
- package/src/core/kernel/store.js +173 -0
- package/src/core/{class → logic/dom}/char.js +42 -45
- package/src/core/logic/dom/format.js +1075 -0
- package/src/core/{class → logic/dom}/html.js +743 -624
- package/src/core/logic/dom/inline.js +1847 -0
- package/src/core/logic/dom/listFormat.js +601 -0
- package/src/core/{class → logic/dom}/nodeTransform.js +92 -72
- package/src/core/{class → logic/dom}/offset.js +254 -317
- package/src/core/logic/dom/selection.js +754 -0
- package/src/core/logic/panel/menu.js +389 -0
- package/src/core/logic/panel/toolbar.js +449 -0
- package/src/core/logic/panel/viewer.js +761 -0
- package/src/core/logic/shell/_commandExecutor.js +380 -0
- package/src/core/logic/shell/commandDispatcher.js +241 -0
- package/src/core/logic/shell/component.js +970 -0
- package/src/core/logic/shell/focusManager.js +110 -0
- package/src/core/{base → logic/shell}/history.js +110 -60
- package/src/core/logic/shell/pluginManager.js +363 -0
- package/src/core/logic/shell/shortcuts.js +130 -0
- package/src/core/logic/shell/ui.js +904 -0
- package/src/core/schema/context.js +66 -0
- package/src/core/schema/frameContext.js +160 -0
- package/src/core/schema/options.js +628 -0
- package/src/core/section/constructor.js +194 -500
- package/src/core/section/documentType.js +297 -222
- package/src/events.js +808 -543
- package/src/helper/clipboard.js +27 -16
- package/src/helper/converter.js +100 -78
- package/src/helper/dom/domCheck.js +56 -30
- package/src/helper/dom/domQuery.js +159 -89
- package/src/helper/dom/domUtils.js +114 -49
- package/src/helper/dom/index.js +5 -1
- package/src/helper/env.js +26 -26
- package/src/helper/index.js +1 -1
- package/src/helper/keyCodeMap.js +25 -28
- package/src/helper/numbers.js +4 -8
- package/src/helper/unicode.js +4 -8
- package/src/hooks/base.js +307 -0
- package/src/hooks/params.js +130 -0
- package/src/interfaces/contracts.js +227 -0
- package/src/interfaces/index.js +7 -0
- package/src/interfaces/plugins.js +239 -0
- package/src/langs/ckb.js +4 -4
- package/src/langs/cs.js +4 -4
- package/src/langs/da.js +4 -4
- package/src/langs/de.js +4 -4
- package/src/langs/en.js +4 -4
- package/src/langs/es.js +4 -4
- package/src/langs/fa.js +4 -4
- package/src/langs/fr.js +4 -4
- package/src/langs/he.js +4 -4
- package/src/langs/hu.js +4 -4
- package/src/langs/it.js +4 -4
- package/src/langs/ja.js +4 -4
- package/src/langs/km.js +4 -4
- package/src/langs/ko.js +4 -4
- package/src/langs/lv.js +4 -4
- package/src/langs/nl.js +4 -4
- package/src/langs/pl.js +4 -4
- package/src/langs/pt_br.js +13 -13
- package/src/langs/ro.js +4 -4
- package/src/langs/ru.js +4 -4
- package/src/langs/se.js +4 -4
- package/src/langs/tr.js +4 -4
- package/src/langs/uk.js +4 -4
- package/src/langs/ur.js +4 -4
- package/src/langs/zh_cn.js +4 -4
- package/src/modules/{Browser.js → contract/Browser.js} +119 -128
- package/src/modules/{ColorPicker.js → contract/ColorPicker.js} +132 -142
- package/src/modules/contract/Controller.js +589 -0
- package/src/modules/{Figure.js → contract/Figure.js} +591 -411
- package/src/modules/{HueSlider.js → contract/HueSlider.js} +125 -86
- package/src/modules/contract/Modal.js +357 -0
- package/src/modules/contract/index.js +9 -0
- package/src/modules/manager/ApiManager.js +197 -0
- package/src/modules/{FileManager.js → manager/FileManager.js} +128 -160
- package/src/modules/manager/index.js +5 -0
- package/src/modules/{ModalAnchorEditor.js → ui/ModalAnchorEditor.js} +108 -138
- package/src/modules/{SelectMenu.js → ui/SelectMenu.js} +119 -120
- package/src/modules/{_DragHandle.js → ui/_DragHandle.js} +1 -1
- package/src/modules/ui/index.js +6 -0
- package/src/plugins/browser/audioGallery.js +23 -26
- package/src/plugins/browser/fileBrowser.js +25 -28
- package/src/plugins/browser/fileGallery.js +20 -23
- package/src/plugins/browser/imageGallery.js +24 -23
- package/src/plugins/browser/videoGallery.js +27 -29
- package/src/plugins/command/blockquote.js +11 -17
- package/src/plugins/command/exportPDF.js +26 -26
- package/src/plugins/command/fileUpload.js +138 -133
- package/src/plugins/command/list_bulleted.js +48 -44
- package/src/plugins/command/list_numbered.js +48 -44
- package/src/plugins/dropdown/align.js +64 -50
- package/src/plugins/dropdown/backgroundColor.js +34 -35
- package/src/plugins/dropdown/{formatBlock.js → blockStyle.js} +43 -37
- package/src/plugins/dropdown/font.js +50 -36
- package/src/plugins/dropdown/fontColor.js +34 -35
- package/src/plugins/dropdown/hr.js +55 -50
- package/src/plugins/dropdown/layout.js +20 -15
- package/src/plugins/dropdown/lineHeight.js +46 -30
- package/src/plugins/dropdown/list.js +32 -33
- package/src/plugins/dropdown/paragraphStyle.js +40 -34
- package/src/plugins/dropdown/table/index.js +915 -0
- package/src/plugins/dropdown/table/render/table.html.js +308 -0
- package/src/plugins/dropdown/table/render/table.menu.js +121 -0
- package/src/plugins/dropdown/table/services/table.cell.js +465 -0
- package/src/plugins/dropdown/table/services/table.clipboard.js +414 -0
- package/src/plugins/dropdown/table/services/table.grid.js +504 -0
- package/src/plugins/dropdown/table/services/table.resize.js +463 -0
- package/src/plugins/dropdown/table/services/table.selection.js +466 -0
- package/src/plugins/dropdown/table/services/table.style.js +844 -0
- package/src/plugins/dropdown/table/shared/table.constants.js +109 -0
- package/src/plugins/dropdown/table/shared/table.utils.js +219 -0
- package/src/plugins/dropdown/template.js +20 -15
- package/src/plugins/dropdown/textStyle.js +28 -22
- package/src/plugins/field/mention.js +54 -49
- package/src/plugins/index.js +5 -5
- package/src/plugins/input/fontSize.js +100 -97
- package/src/plugins/input/pageNavigator.js +13 -10
- package/src/plugins/modal/audio.js +208 -219
- package/src/plugins/modal/drawing.js +99 -104
- package/src/plugins/modal/embed.js +323 -312
- package/src/plugins/modal/image/index.js +942 -0
- package/src/plugins/modal/image/render/image.html.js +150 -0
- package/src/plugins/modal/image/services/image.size.js +198 -0
- package/src/plugins/modal/image/services/image.upload.js +216 -0
- package/src/plugins/modal/image/shared/image.constants.js +20 -0
- package/src/plugins/modal/link.js +74 -54
- package/src/plugins/modal/math.js +126 -119
- package/src/plugins/modal/video/index.js +858 -0
- package/src/plugins/modal/video/render/video.html.js +131 -0
- package/src/plugins/modal/video/services/video.size.js +281 -0
- package/src/plugins/modal/video/services/video.upload.js +92 -0
- package/src/plugins/popup/anchor.js +57 -49
- package/src/suneditor.js +73 -61
- package/src/themes/cobalt.css +155 -0
- package/src/themes/dark.css +143 -120
- package/src/typedef.js +214 -63
- package/types/assets/icons/defaultIcons.d.ts +8 -0
- package/types/assets/suneditor-contents.css.d.ts +1 -0
- package/types/assets/suneditor.css.d.ts +1 -0
- package/types/core/config/contextProvider.d.ts +148 -0
- package/types/core/config/eventManager.d.ts +68 -0
- package/types/core/config/instanceCheck.d.ts +33 -0
- package/types/core/config/optionProvider.d.ts +147 -0
- package/types/core/editor.d.ts +27 -586
- package/types/core/event/actions/index.d.ts +50 -0
- package/types/core/event/effects/common.registry.d.ts +56 -0
- package/types/core/event/effects/keydown.registry.d.ts +80 -0
- package/types/core/event/effects/ruleHelpers.d.ts +36 -0
- package/types/core/event/eventOrchestrator.d.ts +191 -0
- package/types/core/event/executor.d.ts +13 -0
- package/types/core/event/handlers/handler_toolbar.d.ts +38 -0
- package/types/core/event/handlers/handler_ww_clipboard.d.ts +36 -0
- package/types/core/event/handlers/handler_ww_dragDrop.d.ts +26 -0
- package/types/core/event/handlers/handler_ww_input.d.ts +38 -0
- package/types/core/event/handlers/handler_ww_key.d.ts +40 -0
- package/types/core/event/handlers/handler_ww_mouse.d.ts +47 -0
- package/types/core/event/ports.d.ts +256 -0
- package/types/core/event/reducers/keydown.reducer.d.ts +84 -0
- package/types/core/event/rules/keydown.rule.arrow.d.ts +19 -0
- package/types/core/event/rules/keydown.rule.backspace.d.ts +18 -0
- package/types/core/event/rules/keydown.rule.delete.d.ts +18 -0
- package/types/core/event/rules/keydown.rule.enter.d.ts +18 -0
- package/types/core/event/rules/keydown.rule.tab.d.ts +18 -0
- package/types/core/event/support/defaultLineManager.d.ts +22 -0
- package/types/core/event/support/selectionState.d.ts +29 -0
- package/types/core/kernel/coreKernel.d.ts +219 -0
- package/types/core/kernel/kernelInjector.d.ts +16 -0
- package/types/core/kernel/store.d.ts +170 -0
- package/types/core/logic/dom/char.d.ts +46 -0
- package/types/core/logic/dom/format.d.ts +234 -0
- package/types/core/logic/dom/html.d.ts +290 -0
- package/types/core/logic/dom/inline.d.ts +93 -0
- package/types/core/logic/dom/listFormat.d.ts +101 -0
- package/types/core/logic/dom/nodeTransform.d.ts +110 -0
- package/types/core/logic/dom/offset.d.ts +335 -0
- package/types/core/logic/dom/selection.d.ts +165 -0
- package/types/core/logic/panel/menu.d.ts +93 -0
- package/types/core/logic/panel/toolbar.d.ts +128 -0
- package/types/core/logic/panel/viewer.d.ts +89 -0
- package/types/core/logic/shell/_commandExecutor.d.ts +18 -0
- package/types/core/logic/shell/commandDispatcher.d.ts +65 -0
- package/types/core/logic/shell/component.d.ts +182 -0
- package/types/core/logic/shell/focusManager.d.ts +31 -0
- package/types/core/{base → logic/shell}/history.d.ts +13 -12
- package/types/core/logic/shell/pluginManager.d.ts +115 -0
- package/types/core/logic/shell/shortcuts.d.ts +131 -0
- package/types/core/logic/shell/ui.d.ts +261 -0
- package/types/core/schema/context.d.ts +104 -0
- package/types/core/schema/frameContext.d.ts +320 -0
- package/types/core/schema/options.d.ts +1241 -0
- package/types/core/section/constructor.d.ts +117 -652
- package/types/core/section/documentType.d.ts +43 -61
- package/types/events.d.ts +796 -65
- package/types/helper/clipboard.d.ts +5 -4
- package/types/helper/converter.d.ts +55 -43
- package/types/helper/dom/domCheck.d.ts +27 -19
- package/types/helper/dom/domQuery.d.ts +76 -57
- package/types/helper/dom/domUtils.d.ts +62 -39
- package/types/helper/dom/index.d.ts +87 -1
- package/types/helper/env.d.ts +16 -13
- package/types/helper/index.d.ts +8 -2
- package/types/helper/keyCodeMap.d.ts +24 -23
- package/types/helper/numbers.d.ts +4 -6
- package/types/helper/unicode.d.ts +4 -3
- package/types/hooks/base.d.ts +239 -0
- package/types/hooks/params.d.ts +65 -0
- package/types/index.d.ts +20 -117
- package/types/interfaces/contracts.d.ts +183 -0
- package/types/interfaces/index.d.ts +3 -0
- package/types/interfaces/plugins.d.ts +168 -0
- package/types/langs/_Lang.d.ts +2 -2
- package/types/langs/index.d.ts +2 -2
- package/types/modules/contract/Browser.d.ts +262 -0
- package/types/modules/contract/ColorPicker.d.ts +99 -0
- package/types/modules/contract/Controller.d.ts +204 -0
- package/types/modules/contract/Figure.d.ts +529 -0
- package/types/modules/{HueSlider.d.ts → contract/HueSlider.d.ts} +39 -28
- package/types/modules/contract/Modal.d.ts +62 -0
- package/types/modules/contract/index.d.ts +7 -0
- package/types/modules/manager/ApiManager.d.ts +106 -0
- package/types/modules/manager/FileManager.d.ts +124 -0
- package/types/modules/manager/index.d.ts +3 -0
- package/types/modules/ui/ModalAnchorEditor.d.ts +152 -0
- package/types/modules/ui/SelectMenu.d.ts +107 -0
- package/types/modules/{_DragHandle.d.ts → ui/_DragHandle.d.ts} +1 -0
- package/types/modules/ui/index.d.ts +4 -0
- package/types/plugins/browser/audioGallery.d.ts +33 -41
- package/types/plugins/browser/fileBrowser.d.ts +42 -50
- package/types/plugins/browser/fileGallery.d.ts +33 -41
- package/types/plugins/browser/imageGallery.d.ts +30 -37
- package/types/plugins/browser/videoGallery.d.ts +33 -41
- package/types/plugins/command/blockquote.d.ts +4 -21
- package/types/plugins/command/exportPDF.d.ts +23 -33
- package/types/plugins/command/fileUpload.d.ts +80 -100
- package/types/plugins/command/list_bulleted.d.ts +9 -35
- package/types/plugins/command/list_numbered.d.ts +9 -35
- package/types/plugins/dropdown/align.d.ts +23 -46
- package/types/plugins/dropdown/backgroundColor.d.ts +35 -53
- package/types/plugins/dropdown/blockStyle.d.ts +45 -0
- package/types/plugins/dropdown/font.d.ts +18 -41
- package/types/plugins/dropdown/fontColor.d.ts +35 -53
- package/types/plugins/dropdown/hr.d.ts +26 -52
- package/types/plugins/dropdown/layout.d.ts +19 -25
- package/types/plugins/dropdown/lineHeight.d.ts +21 -39
- package/types/plugins/dropdown/list.d.ts +6 -34
- package/types/plugins/dropdown/paragraphStyle.d.ts +34 -45
- package/types/plugins/dropdown/table/index.d.ts +158 -0
- package/types/plugins/dropdown/table/render/table.html.d.ts +71 -0
- package/types/plugins/dropdown/table/render/table.menu.d.ts +59 -0
- package/types/plugins/dropdown/table/services/table.cell.d.ts +76 -0
- package/types/plugins/dropdown/table/services/table.clipboard.d.ts +26 -0
- package/types/plugins/dropdown/table/services/table.grid.d.ts +77 -0
- package/types/plugins/dropdown/table/services/table.resize.d.ts +72 -0
- package/types/plugins/dropdown/table/services/table.selection.d.ts +59 -0
- package/types/plugins/dropdown/table/services/table.style.d.ts +162 -0
- package/types/plugins/dropdown/table/shared/table.constants.d.ts +134 -0
- package/types/plugins/dropdown/table/shared/table.utils.d.ts +91 -0
- package/types/plugins/dropdown/template.d.ts +19 -25
- package/types/plugins/dropdown/textStyle.d.ts +23 -30
- package/types/plugins/field/mention.d.ts +66 -72
- package/types/plugins/index.d.ts +41 -40
- package/types/plugins/input/fontSize.d.ts +57 -96
- package/types/plugins/input/pageNavigator.d.ts +5 -8
- package/types/plugins/modal/audio.d.ts +60 -153
- package/types/plugins/modal/drawing.d.ts +16 -118
- package/types/plugins/modal/embed.d.ts +46 -166
- package/types/plugins/modal/image/index.d.ts +281 -0
- package/types/plugins/modal/image/render/image.html.d.ts +45 -0
- package/types/plugins/modal/image/services/image.size.d.ts +55 -0
- package/types/plugins/modal/image/services/image.upload.d.ts +24 -0
- package/types/plugins/modal/image/shared/image.constants.d.ts +17 -0
- package/types/plugins/modal/link.d.ts +46 -66
- package/types/plugins/modal/math.d.ts +17 -86
- package/types/plugins/modal/{video.d.ts → video/index.d.ts} +89 -221
- package/types/plugins/modal/video/render/video.html.d.ts +37 -0
- package/types/plugins/modal/video/services/video.size.d.ts +74 -0
- package/types/plugins/modal/video/services/video.upload.d.ts +19 -0
- package/types/plugins/popup/anchor.d.ts +8 -38
- package/types/suneditor.d.ts +55 -24
- package/types/typedef.d.ts +344 -228
- package/CONTRIBUTING.md +0 -186
- package/src/core/base/eventHandlers/handler_ww_key_input.js +0 -1200
- package/src/core/base/eventHandlers/handler_ww_mouse.js +0 -194
- package/src/core/base/eventManager.js +0 -1523
- package/src/core/class/component.js +0 -856
- package/src/core/class/format.js +0 -3433
- package/src/core/class/menu.js +0 -346
- package/src/core/class/selection.js +0 -610
- package/src/core/class/shortcuts.js +0 -98
- package/src/core/class/toolbar.js +0 -431
- package/src/core/class/ui.js +0 -424
- package/src/core/class/viewer.js +0 -750
- package/src/core/section/actives.js +0 -266
- package/src/core/section/context.js +0 -102
- package/src/editorInjector/_classes.js +0 -36
- package/src/editorInjector/_core.js +0 -87
- package/src/editorInjector/index.js +0 -73
- package/src/modules/ApiManager.js +0 -191
- package/src/modules/Controller.js +0 -474
- package/src/modules/Modal.js +0 -346
- package/src/modules/index.js +0 -14
- package/src/plugins/dropdown/table.js +0 -4034
- package/src/plugins/modal/image.js +0 -1376
- package/src/plugins/modal/video.js +0 -1226
- package/types/core/base/eventHandlers/handler_toolbar.d.ts +0 -41
- package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +0 -40
- package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +0 -35
- package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +0 -45
- package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +0 -39
- package/types/core/base/eventManager.d.ts +0 -401
- package/types/core/class/char.d.ts +0 -61
- package/types/core/class/component.d.ts +0 -213
- package/types/core/class/format.d.ts +0 -623
- package/types/core/class/html.d.ts +0 -430
- package/types/core/class/menu.d.ts +0 -126
- package/types/core/class/nodeTransform.d.ts +0 -93
- package/types/core/class/offset.d.ts +0 -522
- package/types/core/class/selection.d.ts +0 -188
- package/types/core/class/shortcuts.d.ts +0 -142
- package/types/core/class/toolbar.d.ts +0 -189
- package/types/core/class/ui.d.ts +0 -164
- package/types/core/class/viewer.d.ts +0 -140
- package/types/core/section/actives.d.ts +0 -46
- package/types/core/section/context.d.ts +0 -45
- package/types/editorInjector/_classes.d.ts +0 -41
- package/types/editorInjector/_core.d.ts +0 -87
- package/types/editorInjector/index.d.ts +0 -69
- package/types/modules/ApiManager.d.ts +0 -125
- package/types/modules/Browser.d.ts +0 -326
- package/types/modules/ColorPicker.d.ts +0 -135
- package/types/modules/Controller.d.ts +0 -251
- package/types/modules/Figure.d.ts +0 -517
- package/types/modules/FileManager.d.ts +0 -202
- package/types/modules/Modal.d.ts +0 -111
- package/types/modules/ModalAnchorEditor.d.ts +0 -236
- package/types/modules/SelectMenu.d.ts +0 -194
- package/types/modules/index.d.ts +0 -26
- package/types/plugins/dropdown/formatBlock.d.ts +0 -55
- package/types/plugins/dropdown/table.d.ts +0 -627
- package/types/plugins/modal/image.d.ts +0 -451
- /package/{LICENSE → LICENSE.txt} +0 -0
|
@@ -1,176 +1,205 @@
|
|
|
1
|
-
|
|
2
|
-
* @fileoverview Char class
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import CoreInjector from '../../editorInjector/_core';
|
|
6
|
-
import { dom, converter, numbers, unicode, clipboard } from '../../helper';
|
|
1
|
+
import { dom, converter, numbers, unicode, clipboard, env } from '../../../helper';
|
|
7
2
|
|
|
3
|
+
const { _d } = env;
|
|
8
4
|
const REQUIRED_DATA_ATTRS = 'data-se-[^\\s]+';
|
|
9
5
|
const V2_MIG_DATA_ATTRS = '|data-index|data-file-size|data-file-name|data-exp|data-font-size';
|
|
10
6
|
|
|
11
7
|
/**
|
|
12
|
-
* @typedef {Omit<HTML & Partial<__se__EditorInjector>, 'html'>} HTMLThis
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @constructor
|
|
17
|
-
* @this {HTMLThis}
|
|
18
8
|
* @description All HTML related classes involved in the editing area
|
|
19
|
-
* @param {__se__EditorCore} editor - The root editor instance
|
|
20
9
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
10
|
+
class HTML {
|
|
11
|
+
#$;
|
|
12
|
+
#store;
|
|
13
|
+
|
|
14
|
+
#frameContext;
|
|
15
|
+
#frameOptions;
|
|
16
|
+
#options;
|
|
17
|
+
#instanceCheck;
|
|
18
|
+
|
|
19
|
+
#fontSizeUnitRegExp;
|
|
20
|
+
#isAllowedClassName;
|
|
21
|
+
#allowHTMLComment;
|
|
22
|
+
#disallowedStyleNodesRegExp;
|
|
23
|
+
#htmlCheckWhitelistRegExp;
|
|
24
|
+
#htmlCheckBlacklistRegExp;
|
|
25
|
+
#elementWhitelistRegExp;
|
|
26
|
+
#elementBlacklistRegExp;
|
|
27
|
+
#attributeWhitelistRegExp;
|
|
28
|
+
#attributeBlacklistRegExp;
|
|
29
|
+
#cleanStyleTagKeyRegExp;
|
|
30
|
+
#cleanStyleRegExpMap;
|
|
31
|
+
#textStyleTags;
|
|
32
|
+
#disallowedTagsRegExp;
|
|
33
|
+
#disallowedTagNameRegExp;
|
|
34
|
+
#allowedTagNameRegExp;
|
|
35
|
+
|
|
37
36
|
/** @type {Object<string, RegExp>} */
|
|
38
|
-
|
|
37
|
+
#attributeWhitelist;
|
|
39
38
|
/** @type {Object<string, RegExp>} */
|
|
40
|
-
|
|
41
|
-
this._attributeWhitelistRegExp = null;
|
|
42
|
-
this._attributeBlacklistRegExp = null;
|
|
43
|
-
this._cleanStyleTagKeyRegExp = null;
|
|
44
|
-
this._cleanStyleRegExpMap = null;
|
|
45
|
-
this._textStyleTags = options.get('_textStyleTags');
|
|
39
|
+
#attributeBlacklist;
|
|
46
40
|
/** @type {Object<string, *>} */
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
41
|
+
#autoStyleify;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @constructor
|
|
45
|
+
* @param {SunEditor.Kernel} kernel
|
|
46
|
+
*/
|
|
47
|
+
constructor(kernel) {
|
|
48
|
+
this.#$ = kernel.$;
|
|
49
|
+
this.#store = kernel.store;
|
|
50
|
+
|
|
51
|
+
const options = (this.#options = this.#$.options);
|
|
52
|
+
this.#frameOptions = this.#$.frameOptions;
|
|
53
|
+
this.#frameContext = this.#$.frameContext;
|
|
54
|
+
this.#instanceCheck = this.#$.instanceCheck;
|
|
55
|
+
|
|
56
|
+
// members
|
|
57
|
+
this.#isAllowedClassName = function (v) {
|
|
58
|
+
return this.test(v) ? v : '';
|
|
59
|
+
}.bind(options.get('allowedClassName'));
|
|
60
|
+
|
|
61
|
+
this.#textStyleTags = options.get('_textStyleTags');
|
|
62
|
+
|
|
63
|
+
// clean styles
|
|
64
|
+
const tagStyles = options.get('tagStyles');
|
|
65
|
+
const splitTagStyles = {};
|
|
66
|
+
for (const k in tagStyles) {
|
|
67
|
+
const s = k.split('|');
|
|
68
|
+
for (let i = 0, len = s.length, n; i < len; i++) {
|
|
69
|
+
n = s[i];
|
|
70
|
+
if (!splitTagStyles[n]) splitTagStyles[n] = '';
|
|
71
|
+
else splitTagStyles[n] += '|';
|
|
72
|
+
splitTagStyles[n] += tagStyles[k];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const k in splitTagStyles) {
|
|
76
|
+
splitTagStyles[k] = new RegExp(`\\s*[^-a-zA-Z](${splitTagStyles[k]})\\s*:[^;]+(?!;)*`, 'gi');
|
|
62
77
|
}
|
|
63
|
-
}
|
|
64
|
-
for (const k in splitTagStyles) {
|
|
65
|
-
splitTagStyles[k] = new RegExp(`\\s*[^-a-zA-Z](${splitTagStyles[k]})\\s*:[^;]+(?!;)*`, 'gi');
|
|
66
|
-
}
|
|
67
78
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
for (const key in stylesObj) {
|
|
78
|
-
stylesMap.set(new RegExp(`^(${key})$`), stylesObj[key]);
|
|
79
|
-
}
|
|
80
|
-
this._cleanStyleTagKeyRegExp = new RegExp(`^(${Object.keys(stylesObj).join('|')})$`, 'i');
|
|
81
|
-
this._cleanStyleRegExpMap = stylesMap;
|
|
82
|
-
|
|
83
|
-
// font size unit
|
|
84
|
-
this.fontSizeUnitRegExp = new RegExp('\\d+(' + options.get('fontSizeUnits').join('|') + ')$', 'i');
|
|
85
|
-
|
|
86
|
-
// extra tags
|
|
87
|
-
const allowedExtraTags = options.get('_allowedExtraTag');
|
|
88
|
-
const disallowedExtraTags = options.get('_disallowedExtraTag');
|
|
89
|
-
this.__disallowedTagsRegExp = new RegExp(`<(${disallowedExtraTags})[^>]*>([\\s\\S]*?)<\\/\\1>|<(${disallowedExtraTags})[^>]*\\/?>`, 'gi');
|
|
90
|
-
this.__disallowedTagNameRegExp = new RegExp(`^(${disallowedExtraTags})$`, 'i');
|
|
91
|
-
this.__allowedTagNameRegExp = new RegExp(`^(${allowedExtraTags})$`, 'i');
|
|
92
|
-
|
|
93
|
-
// set disallow text nodes
|
|
94
|
-
const disallowStyleNodes = Object.keys(options.get('_defaultStyleTagMap'));
|
|
95
|
-
const allowStyleNodes = !options.get('elementWhitelist')
|
|
96
|
-
? []
|
|
97
|
-
: options
|
|
98
|
-
.get('elementWhitelist')
|
|
99
|
-
.split('|')
|
|
100
|
-
.filter((v) => /b|i|ins|s|strike/i.test(v));
|
|
101
|
-
for (let i = 0; i < allowStyleNodes.length; i++) {
|
|
102
|
-
disallowStyleNodes.splice(disallowStyleNodes.indexOf(allowStyleNodes[i].toLowerCase()), 1);
|
|
103
|
-
}
|
|
104
|
-
this._disallowedStyleNodesRegExp = disallowStyleNodes.length === 0 ? null : new RegExp('(<\\/?)(' + disallowStyleNodes.join('|') + ')\\b\\s*([^>^<]+)?\\s*(?=>)', 'gi');
|
|
105
|
-
|
|
106
|
-
// whitelist
|
|
107
|
-
// tags
|
|
108
|
-
const defaultAttr = options.get('__defaultAttributeWhitelist');
|
|
109
|
-
this._allowHTMLComment = options.get('_editorElementWhitelist').includes('//') || options.get('_editorElementWhitelist') === '*';
|
|
110
|
-
// html check
|
|
111
|
-
this._htmlCheckWhitelistRegExp = new RegExp('^(' + GetRegList(options.get('_editorElementWhitelist').replace('|//', ''), '') + ')$', 'i');
|
|
112
|
-
this._htmlCheckBlacklistRegExp = new RegExp('^(' + (options.get('elementBlacklist') || '^') + ')$', 'i');
|
|
113
|
-
// elements
|
|
114
|
-
this._elementWhitelistRegExp = converter.createElementWhitelist(GetRegList(options.get('_editorElementWhitelist').replace('|//', '|<!--|-->'), ''));
|
|
115
|
-
this._elementBlacklistRegExp = converter.createElementBlacklist(options.get('elementBlacklist').replace('|//', '|<!--|-->'));
|
|
116
|
-
// attributes
|
|
117
|
-
const regEndStr = '\\s*=\\s*(")[^"]*\\1';
|
|
118
|
-
const _wAttr = options.get('attributeWhitelist');
|
|
79
|
+
const stylesMap = new Map();
|
|
80
|
+
const stylesObj = {
|
|
81
|
+
...splitTagStyles,
|
|
82
|
+
line: options.get('_lineStylesRegExp'),
|
|
83
|
+
};
|
|
84
|
+
this.#textStyleTags.forEach((v) => {
|
|
85
|
+
stylesObj[v] = options.get('_textStylesRegExp');
|
|
86
|
+
});
|
|
119
87
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
88
|
+
for (const key in stylesObj) {
|
|
89
|
+
stylesMap.set(new RegExp(`^(${key})$`), stylesObj[key]);
|
|
90
|
+
}
|
|
91
|
+
this.#cleanStyleTagKeyRegExp = new RegExp(`^(${Object.keys(stylesObj).join('|')})$`, 'i');
|
|
92
|
+
this.#cleanStyleRegExpMap = stylesMap;
|
|
93
|
+
|
|
94
|
+
// font size unit
|
|
95
|
+
this.#fontSizeUnitRegExp = new RegExp('\\d+(' + options.get('fontSizeUnits').join('|') + ')$', 'i');
|
|
96
|
+
|
|
97
|
+
// extra tags
|
|
98
|
+
const allowedExtraTags = options.get('_allowedExtraTag');
|
|
99
|
+
const disallowedExtraTags = options.get('_disallowedExtraTag');
|
|
100
|
+
this.#disallowedTagsRegExp = new RegExp(`<(${disallowedExtraTags})[^>]*>([\\s\\S]*?)<\\/\\1>|<(${disallowedExtraTags})[^>]*\\/?>`, 'gi');
|
|
101
|
+
this.#disallowedTagNameRegExp = new RegExp(`^(${disallowedExtraTags})$`, 'i');
|
|
102
|
+
this.#allowedTagNameRegExp = new RegExp(`^(${allowedExtraTags})$`, 'i');
|
|
103
|
+
|
|
104
|
+
// set disallow text nodes
|
|
105
|
+
const disallowStyleNodes = Object.keys(options.get('_defaultStyleTagMap'));
|
|
106
|
+
const allowStyleNodes = !options.get('elementWhitelist')
|
|
107
|
+
? []
|
|
108
|
+
: options
|
|
109
|
+
.get('elementWhitelist')
|
|
110
|
+
.split('|')
|
|
111
|
+
.filter((v) => /b|i|ins|s|strike/i.test(v));
|
|
112
|
+
for (let i = 0; i < allowStyleNodes.length; i++) {
|
|
113
|
+
disallowStyleNodes.splice(disallowStyleNodes.indexOf(allowStyleNodes[i].toLowerCase()), 1);
|
|
114
|
+
}
|
|
115
|
+
this.#disallowedStyleNodesRegExp = disallowStyleNodes.length === 0 ? null : new RegExp('(<\\/?)(' + disallowStyleNodes.join('|') + ')\\b\\s*([^>^<]+)?\\s*(?=>)', 'gi');
|
|
116
|
+
|
|
117
|
+
// whitelist
|
|
118
|
+
// tags
|
|
119
|
+
const defaultAttr = options.get('__defaultAttributeWhitelist');
|
|
120
|
+
this.#allowHTMLComment = options.get('_editorElementWhitelist').includes('//') || options.get('_editorElementWhitelist') === '*';
|
|
121
|
+
// html check
|
|
122
|
+
this.#htmlCheckWhitelistRegExp = new RegExp('^(' + GetRegList(options.get('_editorElementWhitelist').replace('|//', ''), '') + ')$', 'i');
|
|
123
|
+
this.#htmlCheckBlacklistRegExp = new RegExp('^(' + (options.get('elementBlacklist') || '^') + ')$', 'i');
|
|
124
|
+
// elements
|
|
125
|
+
this.#elementWhitelistRegExp = converter.createElementWhitelist(GetRegList(options.get('_editorElementWhitelist').replace('|//', '|<!--|-->'), ''));
|
|
126
|
+
this.#elementBlacklistRegExp = converter.createElementBlacklist(options.get('elementBlacklist').replace('|//', '|<!--|-->'));
|
|
127
|
+
// attributes
|
|
128
|
+
const regEndStr = '\\s*=\\s*(")[^"]*\\1';
|
|
129
|
+
const _wAttr = options.get('attributeWhitelist');
|
|
130
|
+
|
|
131
|
+
/** @type {Object<string, RegExp>} */
|
|
132
|
+
let tagsAttr = {};
|
|
133
|
+
let allAttr = '';
|
|
134
|
+
if (_wAttr) {
|
|
135
|
+
for (const k in _wAttr) {
|
|
136
|
+
if (/^on[a-z]+$/i.test(_wAttr[k])) continue;
|
|
137
|
+
if (k === '*') {
|
|
138
|
+
allAttr = GetRegList(_wAttr[k], defaultAttr);
|
|
139
|
+
} else {
|
|
140
|
+
tagsAttr[k] = new RegExp('\\s(?:' + GetRegList(_wAttr[k], defaultAttr) + ')' + regEndStr, 'ig');
|
|
141
|
+
}
|
|
130
142
|
}
|
|
131
143
|
}
|
|
132
|
-
}
|
|
133
144
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
this.#attributeWhitelistRegExp = new RegExp('\\s(?:' + (allAttr || defaultAttr) + '|' + REQUIRED_DATA_ATTRS + (options.get('v2Migration') ? V2_MIG_DATA_ATTRS : '') + ')' + regEndStr, 'ig');
|
|
146
|
+
this.#attributeWhitelist = tagsAttr;
|
|
147
|
+
|
|
148
|
+
// blacklist
|
|
149
|
+
const _bAttr = options.get('attributeBlacklist');
|
|
150
|
+
tagsAttr = {};
|
|
151
|
+
allAttr = '';
|
|
152
|
+
if (_bAttr) {
|
|
153
|
+
for (const k in _bAttr) {
|
|
154
|
+
if (k === '*') {
|
|
155
|
+
allAttr = GetRegList(_bAttr[k], '');
|
|
156
|
+
} else {
|
|
157
|
+
tagsAttr[k] = new RegExp('\\s(?:' + GetRegList(_bAttr[k], '') + ')' + regEndStr, 'ig');
|
|
158
|
+
}
|
|
147
159
|
}
|
|
148
160
|
}
|
|
149
|
-
}
|
|
150
161
|
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
this.#attributeBlacklistRegExp = new RegExp('\\s(?:' + (allAttr || '^') + ')' + regEndStr, 'ig');
|
|
163
|
+
this.#attributeBlacklist = tagsAttr;
|
|
153
164
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
165
|
+
// autoStyleify
|
|
166
|
+
this.__resetAutoStyleify(options.get('autoStyleify'));
|
|
167
|
+
}
|
|
157
168
|
|
|
158
|
-
HTML.prototype = {
|
|
159
169
|
/**
|
|
160
|
-
* @this {HTMLThis}
|
|
161
170
|
* @description Filters an HTML string based on allowed and disallowed tags, with optional custom validation.
|
|
162
171
|
* - Removes blacklisted tags and keeps only whitelisted tags.
|
|
163
172
|
* - Allows custom validation functions to replace, modify, or remove elements.
|
|
164
173
|
* @param {string} html - The HTML string to be filtered.
|
|
165
174
|
* @param {Object} params - Filtering parameters.
|
|
166
|
-
* @param {string} [params.tagWhitelist] - Allowed tags, specified as a string with tags separated by '|'
|
|
167
|
-
* @param {string} [params.tagBlacklist] - Disallowed tags, specified as a string with tags separated by '|'
|
|
175
|
+
* @param {string} [params.tagWhitelist] - Allowed tags, specified as a string with tags separated by `'|'`. (e.g. `"div|p|span"`).
|
|
176
|
+
* @param {string} [params.tagBlacklist] - Disallowed tags, specified as a string with tags separated by `'|'`. (e.g. `"script|iframe"`).
|
|
168
177
|
* @param {(node: Node) => Node | string | null} [params.validate] - Function to validate and modify individual nodes.
|
|
169
178
|
* - Return `null` to remove the node.
|
|
170
179
|
* - Return a `Node` to replace the current node.
|
|
171
|
-
* - Return a `string` to replace the node's outerHTML
|
|
180
|
+
* - Return a `string` to replace the node's `outerHTML`.
|
|
172
181
|
* @param {boolean} [params.validateAll] - Whether to apply validation to all nodes.
|
|
173
182
|
* @returns {string} - The filtered HTML string.
|
|
183
|
+
* @example
|
|
184
|
+
* // Remove script and iframe tags using blacklist
|
|
185
|
+
* const filtered = editor.html.filter('<div>Content<script>alert("xss")</script></div>', {
|
|
186
|
+
* tagBlacklist: 'script|iframe'
|
|
187
|
+
* });
|
|
188
|
+
*
|
|
189
|
+
* // Keep only specific tags using whitelist
|
|
190
|
+
* const filtered = editor.html.filter('<div><span>Text</span><img src="x"></div>', {
|
|
191
|
+
* tagWhitelist: 'div|span'
|
|
192
|
+
* });
|
|
193
|
+
*
|
|
194
|
+
* // Custom validation to modify nodes
|
|
195
|
+
* const filtered = editor.html.filter('<div class="test"><a href="#">Link</a></div>', {
|
|
196
|
+
* validate: (node) => {
|
|
197
|
+
* if (node.tagName === 'A') {
|
|
198
|
+
* node.setAttribute('target', '_blank');
|
|
199
|
+
* return node;
|
|
200
|
+
* }
|
|
201
|
+
* }
|
|
202
|
+
* });
|
|
174
203
|
*/
|
|
175
204
|
filter(html, { tagWhitelist, tagBlacklist, validate, validateAll }) {
|
|
176
205
|
if (tagWhitelist) {
|
|
@@ -182,28 +211,11 @@ HTML.prototype = {
|
|
|
182
211
|
if (validate) {
|
|
183
212
|
const parseDocument = new DOMParser().parseFromString(html, 'text/html');
|
|
184
213
|
parseDocument.body.querySelectorAll('*').forEach((node) => {
|
|
185
|
-
if (!node.closest('.se-component') && !node.closest('.se-flex-component')) {
|
|
186
|
-
const result = validate(node);
|
|
187
|
-
if (result === null) {
|
|
188
|
-
node.remove();
|
|
189
|
-
} else if (result instanceof Node) {
|
|
190
|
-
node.replaceWith(result);
|
|
191
|
-
} else if (typeof result === 'string') {
|
|
192
|
-
node.outerHTML = result;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
html = parseDocument.body.innerHTML;
|
|
197
|
-
} else if (validateAll) {
|
|
198
|
-
const parseDocument = new DOMParser().parseFromString(html, 'text/html');
|
|
199
|
-
const compClass = ['.se-component', '.se-flex-component'];
|
|
200
|
-
const closestAny = (element) => compClass.some((selector) => element.closest(selector));
|
|
201
|
-
parseDocument.body.querySelectorAll('*').forEach((node) => {
|
|
202
|
-
if (!closestAny(node)) {
|
|
214
|
+
if (validateAll || (!node.closest('.se-component') && !node.closest('.se-flex-component'))) {
|
|
203
215
|
const result = validate(node);
|
|
204
216
|
if (result === null) {
|
|
205
217
|
node.remove();
|
|
206
|
-
} else if (result
|
|
218
|
+
} else if (this.#instanceCheck.isNode(result)) {
|
|
207
219
|
node.replaceWith(result);
|
|
208
220
|
} else if (typeof result === 'string') {
|
|
209
221
|
node.outerHTML = result;
|
|
@@ -214,48 +226,58 @@ HTML.prototype = {
|
|
|
214
226
|
}
|
|
215
227
|
|
|
216
228
|
return html;
|
|
217
|
-
}
|
|
229
|
+
}
|
|
218
230
|
|
|
219
231
|
/**
|
|
220
|
-
* @this {HTMLThis}
|
|
221
232
|
* @description Cleans and compresses HTML code to suit the editor format.
|
|
222
233
|
* @param {string} html HTML string to clean and compress
|
|
223
234
|
* @param {Object} [options] Cleaning options
|
|
224
|
-
* @param {boolean} [options.forceFormat=false] If true
|
|
225
|
-
* @param {string|RegExp
|
|
226
|
-
* Create RegExp object using helper.converter.createElementWhitelist method.
|
|
227
|
-
* @param {string|RegExp
|
|
228
|
-
* Create RegExp object using helper.converter.createElementBlacklist method.
|
|
229
|
-
* @param {boolean} [options._freeCodeViewMode=false] If true
|
|
235
|
+
* @param {boolean} [options.forceFormat=false] If `true`, wraps text nodes without a format node in the format tag.
|
|
236
|
+
* @param {?(string|RegExp)} [options.whitelist] Regular expression of allowed tags.
|
|
237
|
+
* Create RegExp object using `helper.converter.createElementWhitelist` method.
|
|
238
|
+
* @param {?(string|RegExp)} [options.blacklist] Regular expression of disallowed tags.
|
|
239
|
+
* Create RegExp object using `helper.converter.createElementBlacklist` method.
|
|
240
|
+
* @param {boolean} [options._freeCodeViewMode=false] If `true`, the free code view mode is enabled.
|
|
230
241
|
* @returns {string} Cleaned and compressed HTML string
|
|
242
|
+
* @example
|
|
243
|
+
* // Basic cleaning
|
|
244
|
+
* const cleaned = editor.html.clean('<div> <p>Hello</p> </div>');
|
|
245
|
+
*
|
|
246
|
+
* // Clean with format wrapping
|
|
247
|
+
* const cleaned = editor.html.clean('Plain text content', { forceFormat: true });
|
|
248
|
+
*
|
|
249
|
+
* // Clean with blacklist to remove specific tags
|
|
250
|
+
* const cleaned = editor.html.clean('<div><script>alert(1)</script>Content</div>', {
|
|
251
|
+
* blacklist: 'script|style'
|
|
252
|
+
* });
|
|
231
253
|
*/
|
|
232
254
|
clean(html, { forceFormat, whitelist, blacklist, _freeCodeViewMode } = {}) {
|
|
233
|
-
const { tagFilter, formatFilter, classFilter,
|
|
255
|
+
const { tagFilter, formatFilter, classFilter, textStyleTagFilter, attrFilter, styleFilter } = this.#options.get('strictMode');
|
|
234
256
|
let cleanData = '';
|
|
235
257
|
|
|
236
258
|
html = this.compress(html);
|
|
237
259
|
|
|
238
260
|
if (tagFilter) {
|
|
239
|
-
html = html.replace(this
|
|
240
|
-
html = this
|
|
261
|
+
html = html.replace(this.#disallowedTagsRegExp, '');
|
|
262
|
+
html = this.#deleteDisallowedTags(html, this.#elementWhitelistRegExp, this.#elementBlacklistRegExp).replace(/<br\/?>$/i, '');
|
|
241
263
|
}
|
|
242
264
|
|
|
243
|
-
if (this
|
|
265
|
+
if (this.#autoStyleify) {
|
|
244
266
|
const domParser = new DOMParser().parseFromString(html, 'text/html');
|
|
245
|
-
dom.query.getListChildNodes(domParser.body, converter.spanToStyleNode.bind(null, this
|
|
267
|
+
dom.query.getListChildNodes(domParser.body, converter.spanToStyleNode.bind(null, this.#autoStyleify), null);
|
|
246
268
|
html = domParser.body.innerHTML;
|
|
247
269
|
}
|
|
248
270
|
|
|
249
271
|
if (attrFilter || styleFilter) {
|
|
250
|
-
html = html.replace(/(<[a-zA-Z0-9-]+)[^>]*(?=>)/g, CleanElements.bind(this, attrFilter, styleFilter));
|
|
272
|
+
html = html.replace(/(<[a-zA-Z0-9-]+)[^>]*(?=>)/g, this.#CleanElements.bind(this, attrFilter, styleFilter));
|
|
251
273
|
}
|
|
252
274
|
|
|
253
275
|
// get dom tree
|
|
254
|
-
const domParser =
|
|
276
|
+
const domParser = _d.createRange().createContextualFragment(html);
|
|
255
277
|
|
|
256
278
|
if (tagFilter) {
|
|
257
279
|
try {
|
|
258
|
-
this
|
|
280
|
+
this.#consistencyCheckOfHTML(domParser, this.#htmlCheckWhitelistRegExp, this.#htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode);
|
|
259
281
|
} catch (error) {
|
|
260
282
|
console.warn('[SUNEDITOR.html.clean.fail]', error.message);
|
|
261
283
|
}
|
|
@@ -269,38 +291,34 @@ HTML.prototype = {
|
|
|
269
291
|
|
|
270
292
|
const attrs = JSON.parse(iframePlaceholders[i].getAttribute('data-se-iframe-holder-attrs'));
|
|
271
293
|
for (const [key, value] of Object.entries(attrs)) {
|
|
294
|
+
// Block event handler attributes and validate src protocol
|
|
295
|
+
if (/^on/i.test(key)) continue;
|
|
296
|
+
if (key === 'src' && !_SAFE_URL_PROTOCOL.test(String(value).replace(/[\s\r\n\t]+/g, ''))) continue;
|
|
272
297
|
iframe.setAttribute(key, value);
|
|
273
298
|
}
|
|
274
299
|
|
|
275
300
|
iframePlaceholders[i].replaceWith(iframe);
|
|
276
301
|
}
|
|
277
302
|
|
|
278
|
-
|
|
279
|
-
this.editor._MELInfo.forEach((method, query) => {
|
|
280
|
-
const infoLst = domParser.querySelectorAll(query);
|
|
281
|
-
for (let i = 0, len = infoLst.length; i < len; i++) {
|
|
282
|
-
method(infoLst[i]);
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
}
|
|
303
|
+
this.#$.pluginManager.applyRetainFormat(domParser);
|
|
286
304
|
|
|
287
305
|
if (formatFilter) {
|
|
288
306
|
let domTree = domParser.childNodes;
|
|
289
|
-
|
|
290
|
-
if (forceFormat) domTree = this
|
|
307
|
+
forceFormat ||= this.#isFormatData(domTree);
|
|
308
|
+
if (forceFormat) domTree = this.#editFormat(domParser).childNodes;
|
|
291
309
|
|
|
292
310
|
for (let i = 0, len = domTree.length, t; i < len; i++) {
|
|
293
311
|
t = domTree[i];
|
|
294
|
-
if (this.
|
|
312
|
+
if (this.#allowedTagNameRegExp.test(t.nodeName)) {
|
|
295
313
|
cleanData += /** @type {HTMLElement} */ (t).outerHTML;
|
|
296
314
|
continue;
|
|
297
315
|
}
|
|
298
|
-
cleanData += this
|
|
316
|
+
cleanData += this.#makeLine(t, forceFormat);
|
|
299
317
|
}
|
|
300
318
|
}
|
|
301
319
|
|
|
302
320
|
// set clean data
|
|
303
|
-
|
|
321
|
+
cleanData ||= html;
|
|
304
322
|
|
|
305
323
|
// whitelist, blacklist
|
|
306
324
|
if (tagFilter) {
|
|
@@ -308,46 +326,58 @@ HTML.prototype = {
|
|
|
308
326
|
if (blacklist) cleanData = cleanData.replace(typeof blacklist === 'string' ? converter.createElementBlacklist(blacklist) : blacklist, '');
|
|
309
327
|
}
|
|
310
328
|
|
|
311
|
-
if (
|
|
312
|
-
cleanData = this
|
|
329
|
+
if (textStyleTagFilter) {
|
|
330
|
+
cleanData = this.#styleNodeConvertor(cleanData);
|
|
313
331
|
}
|
|
314
332
|
|
|
315
333
|
return cleanData;
|
|
316
|
-
}
|
|
334
|
+
}
|
|
317
335
|
|
|
318
336
|
/**
|
|
319
|
-
* @this {HTMLThis}
|
|
320
337
|
* @description Inserts an (HTML element / HTML string / plain string) at the selection range.
|
|
321
|
-
* - If
|
|
338
|
+
* - If `frameOptions.get('charCounter_max')` is exceeded when `html` is added, `null` is returned without addition.
|
|
322
339
|
* @param {Node|string} html HTML Element or HTML string or plain string
|
|
323
340
|
* @param {Object} [options] Options
|
|
324
|
-
* @param {boolean} [options.selectInserted=false] If true
|
|
325
|
-
* @param {boolean} [options.skipCharCount=false] If true
|
|
326
|
-
* @param {boolean} [options.skipCleaning=false] If true
|
|
327
|
-
* @returns {HTMLElement|null} The inserted element or null if insertion failed
|
|
341
|
+
* @param {boolean} [options.selectInserted=false] If `true`, selects the range of the inserted node.
|
|
342
|
+
* @param {boolean} [options.skipCharCount=false] If `true`, inserts even if `frameOptions.get('charCounter_max')` is exceeded.
|
|
343
|
+
* @param {boolean} [options.skipCleaning=false] If `true`, inserts the HTML string without refining it with `html.clean`.
|
|
344
|
+
* @returns {HTMLElement|null} The inserted element or `null` if insertion failed
|
|
345
|
+
* @example
|
|
346
|
+
* // Insert HTML string at cursor
|
|
347
|
+
* editor.html.insert('<strong>Bold text</strong>');
|
|
348
|
+
*
|
|
349
|
+
* // Insert and select the inserted content
|
|
350
|
+
* editor.html.insert('<p>New paragraph</p>', { selectInserted: true });
|
|
351
|
+
*
|
|
352
|
+
* // Insert raw HTML without cleaning
|
|
353
|
+
* editor.html.insert('<div class="custom">Content</div>', { skipCleaning: true });
|
|
328
354
|
*/
|
|
329
355
|
insert(html, { selectInserted, skipCharCount, skipCleaning } = {}) {
|
|
330
|
-
if (!this
|
|
356
|
+
if (!this.#frameContext.get('wysiwyg').contains(this.#$.selection.get().focusNode)) this.#$.focusManager.focus();
|
|
331
357
|
|
|
358
|
+
this.remove();
|
|
359
|
+
this.#$.focusManager.focus();
|
|
360
|
+
|
|
361
|
+
let focusNode = null;
|
|
332
362
|
if (typeof html === 'string') {
|
|
333
363
|
if (!skipCleaning) html = this.clean(html, { forceFormat: false, whitelist: null, blacklist: null });
|
|
334
364
|
try {
|
|
335
|
-
if (dom.check.isListCell(this
|
|
336
|
-
const domParser =
|
|
365
|
+
if (dom.check.isListCell(this.#$.format.getLine(this.#$.selection.getNode(), null))) {
|
|
366
|
+
const domParser = _d.createRange().createContextualFragment(html);
|
|
337
367
|
const domTree = domParser.childNodes;
|
|
338
|
-
if (this
|
|
368
|
+
if (this.#isFormatData(domTree)) html = this.#convertListCell(domTree);
|
|
339
369
|
}
|
|
340
370
|
|
|
341
|
-
const domParser =
|
|
371
|
+
const domParser = _d.createRange().createContextualFragment(html);
|
|
342
372
|
const domTree = domParser.childNodes;
|
|
343
373
|
|
|
344
374
|
if (!skipCharCount) {
|
|
345
|
-
const type = this
|
|
375
|
+
const type = this.#frameOptions.get('charCounter_type') === 'byte-html' ? 'outerHTML' : 'textContent';
|
|
346
376
|
let checkHTML = '';
|
|
347
377
|
for (let i = 0, len = domTree.length; i < len; i++) {
|
|
348
378
|
checkHTML += domTree[i][type];
|
|
349
379
|
}
|
|
350
|
-
if (!this
|
|
380
|
+
if (!this.#$.char.check(checkHTML)) return;
|
|
351
381
|
}
|
|
352
382
|
|
|
353
383
|
let c, a, t, prev, firstCon;
|
|
@@ -359,74 +389,109 @@ HTML.prototype = {
|
|
|
359
389
|
}
|
|
360
390
|
t = this.insertNode(c, { afterNode: a, skipCharCount: true });
|
|
361
391
|
a = t.container || t;
|
|
362
|
-
|
|
392
|
+
firstCon ||= t;
|
|
363
393
|
prev = c;
|
|
364
394
|
}
|
|
365
395
|
|
|
366
396
|
if (prev?.nodeType === 3 && a?.nodeType === 1) a = prev;
|
|
367
397
|
const offset = a.nodeType === 3 ? t.endOffset || a.textContent.length : a.childNodes.length;
|
|
398
|
+
focusNode = a;
|
|
368
399
|
|
|
369
400
|
if (selectInserted) {
|
|
370
|
-
this
|
|
371
|
-
} else if (!this
|
|
372
|
-
this
|
|
401
|
+
this.#$.selection.setRange(firstCon.container || firstCon, firstCon.startOffset || 0, a, offset);
|
|
402
|
+
} else if (!this.#$.component.is(a)) {
|
|
403
|
+
this.#$.selection.setRange(a, offset, a, offset);
|
|
373
404
|
}
|
|
374
405
|
} catch (error) {
|
|
375
|
-
if (this
|
|
406
|
+
if (this.#frameContext.get('isReadOnly') || this.#frameContext.get('isDisabled')) return;
|
|
376
407
|
throw Error(`[SUNEDITOR.html.insert.error] ${error.message}`);
|
|
377
408
|
}
|
|
378
409
|
} else {
|
|
379
|
-
if (this
|
|
380
|
-
this
|
|
410
|
+
if (this.#$.component.is(html)) {
|
|
411
|
+
this.#$.component.insert(html, { skipCharCount, insertBehavior: 'none' });
|
|
381
412
|
} else {
|
|
382
413
|
let afterNode = null;
|
|
383
|
-
if (this
|
|
384
|
-
afterNode = this
|
|
414
|
+
if (this.#$.format.isLine(html) || dom.check.isMedia(html)) {
|
|
415
|
+
afterNode = this.#$.format.getLine(this.#$.selection.getNode(), null);
|
|
385
416
|
}
|
|
386
417
|
this.insertNode(html, { afterNode, skipCharCount });
|
|
387
418
|
}
|
|
388
419
|
}
|
|
389
420
|
|
|
390
|
-
|
|
391
|
-
this.
|
|
392
|
-
|
|
393
|
-
|
|
421
|
+
// focus
|
|
422
|
+
this.#store.set('_lastSelectionNode', null);
|
|
423
|
+
|
|
424
|
+
if (focusNode) {
|
|
425
|
+
const children = dom.query.getListChildNodes(focusNode, null, null);
|
|
426
|
+
if (children.length > 0) {
|
|
427
|
+
focusNode = children.at(-1);
|
|
428
|
+
const offset = focusNode?.nodeType === 3 ? focusNode.textContent.length : 1;
|
|
429
|
+
this.#$.selection.setRange(focusNode, offset, focusNode, offset);
|
|
430
|
+
} else {
|
|
431
|
+
this.#$.focusManager.focus();
|
|
432
|
+
}
|
|
433
|
+
} else {
|
|
434
|
+
this.#$.focusManager.focus();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
this.#$.history.push(false);
|
|
438
|
+
}
|
|
394
439
|
|
|
395
440
|
/**
|
|
396
|
-
* @this {HTMLThis}
|
|
397
441
|
* @description Delete selected node and insert argument value node and return.
|
|
398
|
-
* - If the
|
|
399
|
-
* - Inserting a text node merges with both text nodes on both sides and returns a new
|
|
442
|
+
* - If the `afterNode` exists, it is inserted after the `afterNode`
|
|
443
|
+
* - Inserting a text node merges with both text nodes on both sides and returns a new `{ container, startOffset, endOffset }`.
|
|
400
444
|
* @param {Node} oNode Node to be inserted
|
|
401
445
|
* @param {Object} [options] Options
|
|
402
446
|
* @param {Node} [options.afterNode=null] If the node exists, it is inserted after the node
|
|
403
|
-
* @param {boolean} [options.skipCharCount=null] If true
|
|
447
|
+
* @param {boolean} [options.skipCharCount=null] If `true`, it will be inserted even if `frameOptions.get('charCounter_max')` is exceeded.
|
|
404
448
|
* @returns {Object|Node|null}
|
|
449
|
+
* @example
|
|
450
|
+
* // Insert node at current selection
|
|
451
|
+
* const strongNode = document.createElement('strong');
|
|
452
|
+
* strongNode.textContent = 'Bold';
|
|
453
|
+
* editor.html.insertNode(strongNode);
|
|
454
|
+
*
|
|
455
|
+
* // Insert node after a specific element
|
|
456
|
+
* const paragraph = editor.html.getNode();
|
|
457
|
+
* const newSpan = document.createElement('span');
|
|
458
|
+
* editor.html.insertNode(newSpan, { afterNode: paragraph });
|
|
459
|
+
*
|
|
460
|
+
* // Insert bypassing character count limit
|
|
461
|
+
* editor.html.insertNode(largeContentNode, { skipCharCount: true });
|
|
405
462
|
*/
|
|
406
463
|
insertNode(oNode, { afterNode, skipCharCount } = {}) {
|
|
407
464
|
let result = null;
|
|
408
|
-
if (this
|
|
465
|
+
if (this.#frameContext.get('isReadOnly') || (!skipCharCount && !this.#$.char.check(oNode))) {
|
|
409
466
|
return result;
|
|
410
467
|
}
|
|
411
468
|
|
|
412
469
|
let fNode = null;
|
|
413
|
-
let range =
|
|
414
|
-
|
|
470
|
+
let range = null;
|
|
471
|
+
|
|
472
|
+
if (afterNode) {
|
|
473
|
+
const afterNewLine = this.#$.format.isLine(afterNode) || this.#$.format.isBlock(afterNode) || this.#$.component.is(afterNode) ? this.#$.format.addLine(afterNode, null) : afterNode;
|
|
474
|
+
range = this.#$.selection.setRange(afterNewLine, 1, afterNewLine, 1);
|
|
475
|
+
} else {
|
|
476
|
+
range = this.#$.selection.getRange();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
let line = dom.check.isListCell(range.commonAncestorContainer) ? range.commonAncestorContainer : this.#$.format.getLine(this.#$.selection.getNode(), null);
|
|
415
480
|
let insertListCell = dom.check.isListCell(line) && (dom.check.isListCell(oNode) || dom.check.isList(oNode));
|
|
416
481
|
|
|
417
482
|
let parentNode,
|
|
418
483
|
originAfter,
|
|
419
484
|
tempAfterNode,
|
|
420
485
|
tempParentNode = null;
|
|
421
|
-
const freeFormat = this
|
|
422
|
-
const isFormats =
|
|
486
|
+
const freeFormat = this.#$.format.isBrLine(line);
|
|
487
|
+
const isFormats = this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode) || this.#$.component.isBasic(oNode);
|
|
423
488
|
|
|
424
489
|
if (insertListCell) {
|
|
425
490
|
tempAfterNode = afterNode || dom.check.isList(oNode) ? line.lastChild : line.nextElementSibling;
|
|
426
491
|
tempParentNode = dom.check.isList(oNode) ? line : (tempAfterNode || line).parentNode;
|
|
427
492
|
}
|
|
428
493
|
|
|
429
|
-
if (!afterNode && (isFormats || this
|
|
494
|
+
if (!afterNode && (isFormats || this.#$.component.isBasic(oNode) || dom.check.isMedia(oNode))) {
|
|
430
495
|
const isEdge = dom.check.isEdgePoint(range.endContainer, range.endOffset, 'end');
|
|
431
496
|
const r = this.remove();
|
|
432
497
|
const container = r.container;
|
|
@@ -452,11 +517,13 @@ HTML.prototype = {
|
|
|
452
517
|
tempAfterNode = null;
|
|
453
518
|
} else if (container.nodeType === 3 || dom.check.isBreak(container) || insertListCell) {
|
|
454
519
|
const depthFormat = dom.query.getParentElement(container, (current) => {
|
|
455
|
-
return this
|
|
520
|
+
return this.#$.format.isBlock(current) || dom.check.isListCell(current);
|
|
456
521
|
});
|
|
457
|
-
afterNode = this
|
|
522
|
+
afterNode = this.#$.nodeTransform.split(container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
|
|
458
523
|
if (!afterNode) {
|
|
459
|
-
|
|
524
|
+
if (!dom.check.isListCell(line)) {
|
|
525
|
+
tempAfterNode = afterNode = line;
|
|
526
|
+
}
|
|
460
527
|
} else if (insertListCell) {
|
|
461
528
|
if (line.contains(container)) {
|
|
462
529
|
const subList = dom.check.isList(line.lastElementChild);
|
|
@@ -483,11 +550,11 @@ HTML.prototype = {
|
|
|
483
550
|
}
|
|
484
551
|
}
|
|
485
552
|
|
|
486
|
-
range = !afterNode && !isFormats ? this
|
|
553
|
+
range = !afterNode && !isFormats ? this.#$.selection.getRangeAndAddLine(this.#$.selection.getRange(), null) : this.#$.selection.getRange();
|
|
487
554
|
const commonCon = range.commonAncestorContainer;
|
|
488
555
|
const startOff = range.startOffset;
|
|
489
556
|
const endOff = range.endOffset;
|
|
490
|
-
const formatRange = range.startContainer === commonCon && this
|
|
557
|
+
const formatRange = range.startContainer === commonCon && this.#$.format.isLine(commonCon);
|
|
491
558
|
const startCon = formatRange ? commonCon.childNodes[startOff] || commonCon.childNodes[0] || range.startContainer : range.startContainer;
|
|
492
559
|
const endCon = formatRange ? commonCon.childNodes[endOff] || commonCon.childNodes[commonCon.childNodes.length - 1] || range.endContainer : range.endContainer;
|
|
493
560
|
|
|
@@ -543,10 +610,10 @@ HTML.prototype = {
|
|
|
543
610
|
const prevContainer = removedTag.prevContainer;
|
|
544
611
|
|
|
545
612
|
if (container?.childNodes.length === 0 && isFormats) {
|
|
546
|
-
if (this
|
|
613
|
+
if (this.#$.format.isLine(container)) {
|
|
547
614
|
container.innerHTML = '<br>';
|
|
548
|
-
} else if (this
|
|
549
|
-
container.innerHTML = '<' + this
|
|
615
|
+
} else if (this.#$.format.isBlock(container)) {
|
|
616
|
+
container.innerHTML = '<' + this.#options.get('defaultLine') + '><br></' + this.#options.get('defaultLine') + '>';
|
|
550
617
|
}
|
|
551
618
|
}
|
|
552
619
|
|
|
@@ -566,15 +633,15 @@ HTML.prototype = {
|
|
|
566
633
|
} else {
|
|
567
634
|
afterNode = null;
|
|
568
635
|
}
|
|
569
|
-
} else if (dom.check.isWysiwygFrame(container) && !this
|
|
570
|
-
parentNode = container.appendChild(dom.utils.createElement(this
|
|
636
|
+
} else if (dom.check.isWysiwygFrame(container) && !this.#$.format.isLine(oNode)) {
|
|
637
|
+
parentNode = container.appendChild(dom.utils.createElement(this.#options.get('defaultLine')));
|
|
571
638
|
afterNode = null;
|
|
572
639
|
} else {
|
|
573
640
|
afterNode = isFormats ? endCon : container === prevContainer ? container.nextSibling : container;
|
|
574
641
|
parentNode = !afterNode || !afterNode.parentNode ? commonCon : afterNode.parentNode;
|
|
575
642
|
}
|
|
576
643
|
|
|
577
|
-
while (afterNode && !this
|
|
644
|
+
while (afterNode && !this.#$.format.isLine(afterNode) && afterNode.parentNode !== commonCon) {
|
|
578
645
|
afterNode = afterNode.parentNode;
|
|
579
646
|
}
|
|
580
647
|
}
|
|
@@ -589,24 +656,21 @@ HTML.prototype = {
|
|
|
589
656
|
|
|
590
657
|
try {
|
|
591
658
|
// set node
|
|
592
|
-
const wysiwyg = this
|
|
659
|
+
const wysiwyg = this.#frameContext.get('wysiwyg');
|
|
593
660
|
if (!insertListCell) {
|
|
594
661
|
if (dom.check.isWysiwygFrame(afterNode) || parentNode === wysiwyg.parentNode) {
|
|
595
662
|
parentNode = wysiwyg;
|
|
596
663
|
afterNode = null;
|
|
597
664
|
}
|
|
598
665
|
|
|
599
|
-
if (this
|
|
666
|
+
if (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode) || (!dom.check.isListCell(parentNode) && this.#$.component.isBasic(oNode))) {
|
|
600
667
|
const oldParent = parentNode;
|
|
601
|
-
if (dom.check.
|
|
602
|
-
parentNode = afterNode;
|
|
603
|
-
afterNode = null;
|
|
604
|
-
} else if (dom.check.isListCell(afterNode)) {
|
|
668
|
+
if (dom.check.isListCell(afterNode)) {
|
|
605
669
|
parentNode = afterNode.previousElementSibling || afterNode;
|
|
606
670
|
} else if (!originAfter && !afterNode) {
|
|
607
671
|
const r = this.remove();
|
|
608
|
-
const container = r.container.nodeType === 3 ? (dom.check.isListCell(this
|
|
609
|
-
const rangeCon = dom.check.isWysiwygFrame(container) || this
|
|
672
|
+
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;
|
|
673
|
+
const rangeCon = dom.check.isWysiwygFrame(container) || this.#$.format.isBlock(container);
|
|
610
674
|
parentNode = rangeCon ? container : container.parentNode;
|
|
611
675
|
afterNode = rangeCon ? null : container.nextSibling;
|
|
612
676
|
}
|
|
@@ -614,13 +678,13 @@ HTML.prototype = {
|
|
|
614
678
|
if (oldParent.childNodes.length === 0 && parentNode !== oldParent) dom.utils.removeItem(oldParent);
|
|
615
679
|
}
|
|
616
680
|
|
|
617
|
-
if (isFormats && !freeFormat && !this
|
|
618
|
-
afterNode = parentNode.nextElementSibling;
|
|
681
|
+
if (isFormats && !freeFormat && !this.#$.format.isBlock(parentNode) && !dom.check.isListCell(parentNode) && !dom.check.isWysiwygFrame(parentNode)) {
|
|
682
|
+
afterNode = /** @type {HTMLElement} */ (parentNode).nextElementSibling;
|
|
619
683
|
parentNode = parentNode.parentNode;
|
|
620
684
|
}
|
|
621
685
|
|
|
622
686
|
if (dom.check.isWysiwygFrame(parentNode) && (oNode.nodeType === 3 || dom.check.isBreak(oNode))) {
|
|
623
|
-
const formatNode = dom.utils.createElement(this
|
|
687
|
+
const formatNode = dom.utils.createElement(this.#options.get('defaultLine'), null, oNode);
|
|
624
688
|
fNode = oNode;
|
|
625
689
|
oNode = formatNode;
|
|
626
690
|
}
|
|
@@ -652,7 +716,7 @@ HTML.prototype = {
|
|
|
652
716
|
insertListCell = true;
|
|
653
717
|
}
|
|
654
718
|
|
|
655
|
-
this
|
|
719
|
+
this.#checkDuplicateNode(oNode, parentNode);
|
|
656
720
|
parentNode.insertBefore(oNode, afterNode);
|
|
657
721
|
|
|
658
722
|
if (insertListCell) {
|
|
@@ -682,7 +746,7 @@ HTML.prototype = {
|
|
|
682
746
|
} finally {
|
|
683
747
|
if (fNode) oNode = fNode;
|
|
684
748
|
|
|
685
|
-
const dupleNodes = parentNode.querySelectorAll('[data-duple]');
|
|
749
|
+
const dupleNodes = /** @type {HTMLElement} */ (parentNode).querySelectorAll('[data-duple]');
|
|
686
750
|
if (dupleNodes.length > 0) {
|
|
687
751
|
for (let i = 0, len = dupleNodes.length, d, c, ch, parent; i < len; i++) {
|
|
688
752
|
d = dupleNodes[i];
|
|
@@ -699,23 +763,23 @@ HTML.prototype = {
|
|
|
699
763
|
}
|
|
700
764
|
}
|
|
701
765
|
|
|
702
|
-
if ((this
|
|
703
|
-
const cItem = this
|
|
766
|
+
if ((this.#$.format.isLine(oNode) || this.#$.component.isBasic(oNode)) && startCon === endCon) {
|
|
767
|
+
const cItem = this.#$.format.getLine(commonCon, null);
|
|
704
768
|
if (cItem?.nodeType === 1 && dom.check.isEmptyLine(cItem)) {
|
|
705
769
|
dom.utils.removeItem(cItem);
|
|
706
770
|
}
|
|
707
771
|
}
|
|
708
772
|
|
|
709
|
-
if (freeFormat && (this
|
|
710
|
-
oNode = this
|
|
773
|
+
if (freeFormat && !dom.check.isList(oNode) && (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode))) {
|
|
774
|
+
oNode = this.#setIntoFreeFormat(oNode);
|
|
711
775
|
}
|
|
712
776
|
|
|
713
|
-
if (!this
|
|
777
|
+
if (!this.#$.component.isBasic(oNode)) {
|
|
714
778
|
let offset = 1;
|
|
715
779
|
if (oNode.nodeType === 3) {
|
|
716
780
|
offset = oNode.textContent.length;
|
|
717
|
-
this
|
|
718
|
-
} else if (!dom.check.isBreak(oNode) && !dom.check.isListCell(oNode) && this
|
|
781
|
+
this.#$.selection.setRange(oNode, offset, oNode, offset);
|
|
782
|
+
} else if (!dom.check.isBreak(oNode) && !dom.check.isListCell(oNode) && this.#$.format.isLine(parentNode)) {
|
|
719
783
|
let zeroWidth = null;
|
|
720
784
|
if (!oNode.previousSibling || dom.check.isBreak(oNode.previousSibling)) {
|
|
721
785
|
zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
@@ -727,12 +791,12 @@ HTML.prototype = {
|
|
|
727
791
|
oNode.parentNode.insertBefore(zeroWidth, oNode.nextSibling);
|
|
728
792
|
}
|
|
729
793
|
|
|
730
|
-
if (this.
|
|
794
|
+
if (this.#$.inline._isIgnoreNodeChange(oNode)) {
|
|
731
795
|
oNode = oNode.nextSibling;
|
|
732
796
|
offset = 0;
|
|
733
797
|
}
|
|
734
798
|
|
|
735
|
-
this
|
|
799
|
+
this.#$.selection.setRange(oNode, offset, oNode, offset);
|
|
736
800
|
}
|
|
737
801
|
}
|
|
738
802
|
|
|
@@ -740,33 +804,32 @@ HTML.prototype = {
|
|
|
740
804
|
}
|
|
741
805
|
|
|
742
806
|
return result;
|
|
743
|
-
}
|
|
807
|
+
}
|
|
744
808
|
|
|
745
809
|
/**
|
|
746
|
-
* @this {HTMLThis}
|
|
747
810
|
* @description Delete the selected range.
|
|
748
|
-
* @returns {{container: Node, offset: number, commonCon?: Node
|
|
749
|
-
* - container
|
|
750
|
-
* - offset
|
|
751
|
-
* - commonCon
|
|
752
|
-
* - prevContainer
|
|
811
|
+
* @returns {{container: Node, offset: number, commonCon?: ?Node, prevContainer?: ?Node}}
|
|
812
|
+
* - `container`: the last element after deletion
|
|
813
|
+
* - `offset`: offset
|
|
814
|
+
* - `commonCon`: `commonAncestorContainer`
|
|
815
|
+
* - `prevContainer`: `previousElementSibling` of the deleted area
|
|
753
816
|
*/
|
|
754
817
|
remove() {
|
|
755
|
-
this
|
|
818
|
+
this.#$.selection.resetRangeToTextNode();
|
|
756
819
|
|
|
757
|
-
const range = this
|
|
820
|
+
const range = this.#$.selection.getRange();
|
|
758
821
|
const isStartEdge = range.startOffset === 0;
|
|
759
822
|
const isEndEdge = dom.check.isEdgePoint(range.endContainer, range.endOffset, 'end');
|
|
760
823
|
let prevContainer = null;
|
|
761
824
|
let startPrevEl = null;
|
|
762
825
|
let endNextEl = null;
|
|
763
826
|
if (isStartEdge) {
|
|
764
|
-
startPrevEl = this
|
|
827
|
+
startPrevEl = this.#$.format.getLine(range.startContainer);
|
|
765
828
|
prevContainer = startPrevEl ? startPrevEl.previousElementSibling : null;
|
|
766
829
|
startPrevEl = startPrevEl ? prevContainer : startPrevEl;
|
|
767
830
|
}
|
|
768
831
|
if (isEndEdge) {
|
|
769
|
-
endNextEl = this
|
|
832
|
+
endNextEl = this.#$.format.getLine(range.endContainer);
|
|
770
833
|
endNextEl = endNextEl ? endNextEl.nextElementSibling : endNextEl;
|
|
771
834
|
}
|
|
772
835
|
|
|
@@ -777,9 +840,20 @@ HTML.prototype = {
|
|
|
777
840
|
let startOff = range.startOffset;
|
|
778
841
|
let endOff = range.endOffset;
|
|
779
842
|
const commonCon = /** @type {HTMLElement} */ (range.commonAncestorContainer.nodeType === 3 && range.commonAncestorContainer.parentNode === startCon.parentNode ? startCon.parentNode : range.commonAncestorContainer);
|
|
843
|
+
|
|
844
|
+
if (dom.check.isWysiwygFrame(startCon) && dom.check.isWysiwygFrame(endCon)) {
|
|
845
|
+
this.set('');
|
|
846
|
+
const newInitBR = this.#$.selection.getNode();
|
|
847
|
+
return {
|
|
848
|
+
container: newInitBR,
|
|
849
|
+
offset: 0,
|
|
850
|
+
commonCon,
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
780
854
|
if (commonCon === startCon && commonCon === endCon) {
|
|
781
|
-
if (this
|
|
782
|
-
const compInfo = this
|
|
855
|
+
if (this.#$.component.isBasic(commonCon)) {
|
|
856
|
+
const compInfo = this.#$.component.get(commonCon);
|
|
783
857
|
const compContainer = compInfo.container;
|
|
784
858
|
const parent = compContainer.parentElement;
|
|
785
859
|
|
|
@@ -790,39 +864,39 @@ HTML.prototype = {
|
|
|
790
864
|
|
|
791
865
|
dom.utils.removeItem(compContainer);
|
|
792
866
|
|
|
793
|
-
if (this
|
|
867
|
+
if (this.#$.format.isLine(parent)) {
|
|
794
868
|
if (parent.childNodes.length === 0) {
|
|
795
869
|
dom.utils.removeItem(parent);
|
|
796
870
|
return {
|
|
797
871
|
container: parentNext,
|
|
798
872
|
offset: parentNextOffset,
|
|
799
|
-
commonCon
|
|
873
|
+
commonCon,
|
|
800
874
|
};
|
|
801
875
|
} else {
|
|
802
876
|
return {
|
|
803
877
|
container: next,
|
|
804
878
|
offset: nextOffset,
|
|
805
|
-
commonCon
|
|
879
|
+
commonCon,
|
|
806
880
|
};
|
|
807
881
|
}
|
|
808
882
|
} else {
|
|
809
883
|
return {
|
|
810
884
|
container: parentNext,
|
|
811
885
|
offset: parentNextOffset,
|
|
812
|
-
commonCon
|
|
886
|
+
commonCon,
|
|
813
887
|
};
|
|
814
888
|
}
|
|
815
889
|
} else {
|
|
816
890
|
if ((commonCon.nodeType === 1 && startOff === 0 && endOff === 1) || (commonCon.nodeType === 3 && startOff === 0 && endOff === commonCon.textContent.length)) {
|
|
817
|
-
const nextEl = dom.query.getNextDeepestNode(commonCon, this
|
|
818
|
-
const prevEl = dom.query.getPreviousDeepestNode(commonCon, this
|
|
819
|
-
const line = this
|
|
891
|
+
const nextEl = dom.query.getNextDeepestNode(commonCon, this.#frameContext.get('wysiwyg'));
|
|
892
|
+
const prevEl = dom.query.getPreviousDeepestNode(commonCon, this.#frameContext.get('wysiwyg'));
|
|
893
|
+
const line = this.#$.format.getLine(commonCon);
|
|
820
894
|
dom.utils.removeItem(commonCon);
|
|
821
895
|
|
|
822
896
|
let rEl = nextEl || prevEl;
|
|
823
897
|
let rOffset = nextEl ? 0 : rEl?.nodeType === 3 ? rEl.textContent.length : 1;
|
|
824
898
|
|
|
825
|
-
const npEl = this
|
|
899
|
+
const npEl = this.#$.format.getLine(rEl) || this.#$.component.get(rEl);
|
|
826
900
|
if (line !== npEl) {
|
|
827
901
|
rEl = /** @type {Node} */ (npEl);
|
|
828
902
|
rOffset = rOffset === 0 ? 0 : 1;
|
|
@@ -835,7 +909,7 @@ HTML.prototype = {
|
|
|
835
909
|
return {
|
|
836
910
|
container: rEl,
|
|
837
911
|
offset: rOffset,
|
|
838
|
-
commonCon
|
|
912
|
+
commonCon,
|
|
839
913
|
};
|
|
840
914
|
}
|
|
841
915
|
|
|
@@ -849,7 +923,7 @@ HTML.prototype = {
|
|
|
849
923
|
return {
|
|
850
924
|
container: commonCon,
|
|
851
925
|
offset: 0,
|
|
852
|
-
commonCon
|
|
926
|
+
commonCon,
|
|
853
927
|
};
|
|
854
928
|
|
|
855
929
|
if (startCon === endCon && range.collapsed) {
|
|
@@ -858,7 +932,7 @@ HTML.prototype = {
|
|
|
858
932
|
container: startCon,
|
|
859
933
|
offset: startOff,
|
|
860
934
|
prevContainer: startCon && startCon.parentNode ? startCon : null,
|
|
861
|
-
commonCon
|
|
935
|
+
commonCon,
|
|
862
936
|
};
|
|
863
937
|
}
|
|
864
938
|
}
|
|
@@ -866,7 +940,7 @@ HTML.prototype = {
|
|
|
866
940
|
let beforeNode = null;
|
|
867
941
|
let afterNode = null;
|
|
868
942
|
|
|
869
|
-
const childNodes = dom.query.getListChildNodes(commonCon, null);
|
|
943
|
+
const childNodes = dom.query.getListChildNodes(commonCon, null, null);
|
|
870
944
|
let startIndex = dom.utils.getArrayIndex(childNodes, startCon);
|
|
871
945
|
let endIndex = dom.utils.getArrayIndex(childNodes, endCon);
|
|
872
946
|
|
|
@@ -887,17 +961,17 @@ HTML.prototype = {
|
|
|
887
961
|
}
|
|
888
962
|
} else {
|
|
889
963
|
if (childNodes.length === 0) {
|
|
890
|
-
if (this
|
|
964
|
+
if (this.#$.format.isLine(commonCon) || this.#$.format.isBlock(commonCon) || dom.check.isWysiwygFrame(commonCon) || dom.check.isBreak(commonCon) || dom.check.isMedia(commonCon)) {
|
|
891
965
|
return {
|
|
892
966
|
container: commonCon,
|
|
893
967
|
offset: 0,
|
|
894
|
-
commonCon
|
|
968
|
+
commonCon,
|
|
895
969
|
};
|
|
896
970
|
} else if (dom.check.isText(commonCon)) {
|
|
897
971
|
return {
|
|
898
972
|
container: commonCon,
|
|
899
973
|
offset: endOff,
|
|
900
|
-
commonCon
|
|
974
|
+
commonCon,
|
|
901
975
|
};
|
|
902
976
|
}
|
|
903
977
|
childNodes.push(commonCon);
|
|
@@ -908,7 +982,7 @@ HTML.prototype = {
|
|
|
908
982
|
return {
|
|
909
983
|
container: dom.check.isMedia(commonCon) ? commonCon : startCon,
|
|
910
984
|
offset: 0,
|
|
911
|
-
commonCon
|
|
985
|
+
commonCon,
|
|
912
986
|
};
|
|
913
987
|
}
|
|
914
988
|
}
|
|
@@ -918,17 +992,19 @@ HTML.prototype = {
|
|
|
918
992
|
|
|
919
993
|
const _isText = dom.check.isText;
|
|
920
994
|
const _isElement = dom.check.isElement;
|
|
995
|
+
const _isSingleItem = startIndex === endIndex;
|
|
996
|
+
let nextFocusNodes = null;
|
|
921
997
|
for (let i = startIndex; i <= endIndex; i++) {
|
|
922
998
|
const item = /** @type {Text} */ (childNodes[i]);
|
|
923
999
|
|
|
924
1000
|
if (_isText(item) && (item.data === undefined || item.length === 0)) {
|
|
925
|
-
this
|
|
1001
|
+
nextFocusNodes = this.#nodeRemoveListItem(item, _isSingleItem);
|
|
926
1002
|
continue;
|
|
927
1003
|
}
|
|
928
1004
|
|
|
929
1005
|
if (item === startCon) {
|
|
930
1006
|
if (_isElement(startCon)) {
|
|
931
|
-
if (this
|
|
1007
|
+
if (this.#$.component.is(startCon)) continue;
|
|
932
1008
|
else beforeNode = dom.utils.createTextNode(startCon.textContent);
|
|
933
1009
|
} else {
|
|
934
1010
|
const sc = /** @type {Text} */ (startCon);
|
|
@@ -944,7 +1020,7 @@ HTML.prototype = {
|
|
|
944
1020
|
if (beforeNode.length > 0) {
|
|
945
1021
|
/** @type {Text} */ (startCon).data = beforeNode.data;
|
|
946
1022
|
} else {
|
|
947
|
-
this
|
|
1023
|
+
nextFocusNodes = this.#nodeRemoveListItem(startCon, _isSingleItem);
|
|
948
1024
|
}
|
|
949
1025
|
|
|
950
1026
|
if (item === endCon) break;
|
|
@@ -955,20 +1031,20 @@ HTML.prototype = {
|
|
|
955
1031
|
if (_isText(endCon)) {
|
|
956
1032
|
afterNode = dom.utils.createTextNode(endCon.substringData(endOff, endCon.length - endOff));
|
|
957
1033
|
} else {
|
|
958
|
-
if (this
|
|
1034
|
+
if (this.#$.component.is(endCon)) continue;
|
|
959
1035
|
else afterNode = dom.utils.createTextNode(endCon.textContent);
|
|
960
1036
|
}
|
|
961
1037
|
|
|
962
1038
|
if (afterNode.length > 0) {
|
|
963
1039
|
/** @type {Text} */ (endCon).data = afterNode.data;
|
|
964
1040
|
} else {
|
|
965
|
-
this
|
|
1041
|
+
nextFocusNodes = this.#nodeRemoveListItem(endCon, _isSingleItem);
|
|
966
1042
|
}
|
|
967
1043
|
|
|
968
1044
|
continue;
|
|
969
1045
|
}
|
|
970
1046
|
|
|
971
|
-
this
|
|
1047
|
+
nextFocusNodes = this.#nodeRemoveListItem(item, _isSingleItem);
|
|
972
1048
|
}
|
|
973
1049
|
|
|
974
1050
|
const endUl = dom.query.getParentElement(endCon, 'ul');
|
|
@@ -992,7 +1068,7 @@ HTML.prototype = {
|
|
|
992
1068
|
}
|
|
993
1069
|
}
|
|
994
1070
|
|
|
995
|
-
if (!this
|
|
1071
|
+
if (!this.#$.format.getLine(container) && !(startCon && startCon.parentNode)) {
|
|
996
1072
|
if (endNextEl) {
|
|
997
1073
|
container = endNextEl;
|
|
998
1074
|
offset = 0;
|
|
@@ -1003,58 +1079,65 @@ HTML.prototype = {
|
|
|
1003
1079
|
}
|
|
1004
1080
|
|
|
1005
1081
|
if (!dom.check.isWysiwygFrame(container) && container.childNodes.length === 0) {
|
|
1006
|
-
const rc = this
|
|
1007
|
-
if (rc) container = rc.sc || rc.ec || this
|
|
1082
|
+
const rc = this.#$.nodeTransform.removeAllParents(container, null, null);
|
|
1083
|
+
if (rc) container = rc.sc || rc.ec || this.#frameContext.get('wysiwyg');
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
if (!container || (container.nodeType === 1 && !this.#$.format.isLine(container) && !dom.check.isBreak(container))) {
|
|
1087
|
+
container = nextFocusNodes?.sc || nextFocusNodes?.ec;
|
|
1088
|
+
offset = container?.nodeType === 3 ? container.textContent.length : 1;
|
|
1008
1089
|
}
|
|
1009
1090
|
|
|
1010
1091
|
// set range
|
|
1011
|
-
this
|
|
1092
|
+
this.#$.selection.setRange(container, offset, container, offset);
|
|
1012
1093
|
|
|
1013
1094
|
return {
|
|
1014
1095
|
container,
|
|
1015
1096
|
offset,
|
|
1016
1097
|
prevContainer,
|
|
1017
|
-
commonCon
|
|
1098
|
+
commonCon: commonCon?.parentElement ? commonCon : null,
|
|
1018
1099
|
};
|
|
1019
|
-
}
|
|
1100
|
+
}
|
|
1020
1101
|
|
|
1021
1102
|
/**
|
|
1022
|
-
* @this {HTMLThis}
|
|
1023
1103
|
* @description Gets the current content
|
|
1024
1104
|
* @param {Object} [options] Options
|
|
1025
|
-
* @param {boolean} [options.withFrame=false] Gets the current content with containing parent div.sun-editor-editable (
|
|
1026
|
-
* Ignored for targetOptions.get('iframe_fullPage') is true
|
|
1027
|
-
* @param {boolean} [options.includeFullPage=false] Return only the content of the body without headers when the
|
|
1105
|
+
* @param {boolean} [options.withFrame=false] Gets the current content with containing parent `div.sun-editor-editable` (`<div class="sun-editor-editable">{content}</div>`).
|
|
1106
|
+
* Ignored for `targetOptions.get('iframe_fullPage')` is `true`.
|
|
1107
|
+
* @param {boolean} [options.includeFullPage=false] Return only the content of the body without headers when the `iframe_fullPage` option is `true`
|
|
1028
1108
|
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1029
1109
|
* @returns {string|Object<*, string>}
|
|
1030
1110
|
*/
|
|
1031
1111
|
get({ withFrame, includeFullPage, rootKey } = {}) {
|
|
1032
|
-
if (!rootKey) rootKey = [this.
|
|
1112
|
+
if (!rootKey) rootKey = [this.#store.get('rootKey')];
|
|
1033
1113
|
else if (!Array.isArray(rootKey)) rootKey = [rootKey];
|
|
1034
1114
|
|
|
1035
|
-
const prevrootKey = this.
|
|
1115
|
+
const prevrootKey = this.#store.get('rootKey');
|
|
1036
1116
|
const resultValue = {};
|
|
1037
1117
|
for (let i = 0, len = rootKey.length, r; i < len; i++) {
|
|
1038
|
-
this.
|
|
1118
|
+
this.#$.facade.changeFrameContext(rootKey[i]);
|
|
1039
1119
|
|
|
1040
|
-
const
|
|
1041
|
-
const renderHTML = dom.utils.createElement('DIV', null, this._convertToCode(fc.get('wysiwyg'), true));
|
|
1120
|
+
const renderHTML = dom.utils.createElement('DIV', null, this._convertToCode(this.#frameContext.get('wysiwyg'), true));
|
|
1042
1121
|
|
|
1043
1122
|
const isTableCell = dom.check.isTableCell;
|
|
1044
1123
|
const isEmptyLine = dom.check.isEmptyLine;
|
|
1045
1124
|
const editableEls = [];
|
|
1046
1125
|
const emptyCells = [];
|
|
1047
|
-
dom.query.getListChildren(
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1126
|
+
dom.query.getListChildren(
|
|
1127
|
+
renderHTML,
|
|
1128
|
+
(current) => {
|
|
1129
|
+
if (current.hasAttribute('contenteditable')) {
|
|
1130
|
+
editableEls.push(current);
|
|
1131
|
+
}
|
|
1051
1132
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1133
|
+
const parent = current.parentElement;
|
|
1134
|
+
if (isTableCell(parent) && parent.children.length <= 1 && isEmptyLine(current)) {
|
|
1135
|
+
emptyCells.push(parent);
|
|
1136
|
+
}
|
|
1137
|
+
return false;
|
|
1138
|
+
},
|
|
1139
|
+
null,
|
|
1140
|
+
);
|
|
1058
1141
|
|
|
1059
1142
|
for (let j = 0, jlen = editableEls.length; j < jlen; j++) {
|
|
1060
1143
|
editableEls[j].removeAttribute('contenteditable');
|
|
@@ -1064,97 +1147,94 @@ HTML.prototype = {
|
|
|
1064
1147
|
}
|
|
1065
1148
|
|
|
1066
1149
|
const content = renderHTML.innerHTML;
|
|
1067
|
-
if (this
|
|
1150
|
+
if (this.#frameOptions.get('iframe_fullPage')) {
|
|
1068
1151
|
if (includeFullPage) {
|
|
1069
|
-
const attrs = dom.utils.getAttributesToString(
|
|
1070
|
-
r = `<!DOCTYPE html><html>${
|
|
1152
|
+
const attrs = dom.utils.getAttributesToString(this.#frameContext.get('_wd').body, ['contenteditable']);
|
|
1153
|
+
r = `<!DOCTYPE html><html>${this.#frameContext.get('_wd').head.outerHTML}<body ${attrs}>${content}</body></html>`;
|
|
1071
1154
|
} else {
|
|
1072
1155
|
r = content;
|
|
1073
1156
|
}
|
|
1074
1157
|
} else {
|
|
1075
|
-
r = withFrame ? `<div class="${this
|
|
1158
|
+
r = withFrame ? `<div class="${this.#options.get('_editableClass') + '' + (this.#options.get('_rtl') ? ' se-rtl' : '')}">${content}</div>` : renderHTML.innerHTML;
|
|
1076
1159
|
}
|
|
1077
1160
|
|
|
1078
1161
|
resultValue[rootKey[i]] = r;
|
|
1079
1162
|
}
|
|
1080
1163
|
|
|
1081
|
-
this.
|
|
1164
|
+
this.#$.facade.changeFrameContext(prevrootKey);
|
|
1082
1165
|
return rootKey.length > 1 ? resultValue : resultValue[rootKey[0]];
|
|
1083
|
-
}
|
|
1166
|
+
}
|
|
1084
1167
|
|
|
1085
1168
|
/**
|
|
1086
|
-
* @this {HTMLThis}
|
|
1087
1169
|
* @description Sets the HTML string to the editor content
|
|
1088
1170
|
* @param {string} html HTML string
|
|
1089
1171
|
* @param {Object} [options] Options
|
|
1090
1172
|
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1091
1173
|
*/
|
|
1092
1174
|
set(html, { rootKey } = {}) {
|
|
1093
|
-
this.
|
|
1175
|
+
this.#$.ui.offCurrentController();
|
|
1176
|
+
this.#$.selection.removeRange();
|
|
1094
1177
|
const convertValue = html === null || html === undefined ? '' : this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
|
|
1095
1178
|
|
|
1096
|
-
if (!rootKey) rootKey = [this.
|
|
1179
|
+
if (!rootKey) rootKey = [this.#store.get('rootKey')];
|
|
1097
1180
|
else if (!Array.isArray(rootKey)) rootKey = [rootKey];
|
|
1098
1181
|
|
|
1099
1182
|
for (let i = 0; i < rootKey.length; i++) {
|
|
1100
|
-
this.
|
|
1183
|
+
this.#$.facade.changeFrameContext(rootKey[i]);
|
|
1101
1184
|
|
|
1102
|
-
if (!this
|
|
1103
|
-
this
|
|
1104
|
-
this.
|
|
1105
|
-
this
|
|
1185
|
+
if (!this.#frameContext.get('isCodeView')) {
|
|
1186
|
+
this.#frameContext.get('wysiwyg').innerHTML = convertValue;
|
|
1187
|
+
this.#$.pluginManager.resetFileInfo();
|
|
1188
|
+
this.#$.history.push(false, rootKey[i]);
|
|
1106
1189
|
} else {
|
|
1107
1190
|
const value = this._convertToCode(convertValue, false);
|
|
1108
|
-
this
|
|
1191
|
+
this.#$.viewer._setCodeView(value);
|
|
1109
1192
|
}
|
|
1110
1193
|
}
|
|
1111
|
-
}
|
|
1194
|
+
}
|
|
1112
1195
|
|
|
1113
1196
|
/**
|
|
1114
|
-
* @this {HTMLThis}
|
|
1115
1197
|
* @description Add content to the end of content.
|
|
1116
1198
|
* @param {string} html Content to Input
|
|
1117
1199
|
* @param {Object} [options] Options
|
|
1118
1200
|
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1119
1201
|
*/
|
|
1120
1202
|
add(html, { rootKey } = {}) {
|
|
1121
|
-
|
|
1203
|
+
this.#$.ui.offCurrentController();
|
|
1204
|
+
|
|
1205
|
+
if (!rootKey) rootKey = [this.#store.get('rootKey')];
|
|
1122
1206
|
else if (!Array.isArray(rootKey)) rootKey = [rootKey];
|
|
1123
1207
|
|
|
1124
1208
|
for (let i = 0; i < rootKey.length; i++) {
|
|
1125
|
-
this.
|
|
1209
|
+
this.#$.facade.changeFrameContext(rootKey[i]);
|
|
1126
1210
|
const convertValue = this.clean(html, { forceFormat: true, whitelist: null, blacklist: null });
|
|
1127
1211
|
|
|
1128
|
-
if (!this
|
|
1212
|
+
if (!this.#frameContext.get('isCodeView')) {
|
|
1129
1213
|
const temp = dom.utils.createElement('DIV', null, convertValue);
|
|
1130
|
-
const children = temp.children;
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
if (!children[j]) continue;
|
|
1134
|
-
this.editor.frameContext.get('wysiwyg').appendChild(children[j]);
|
|
1214
|
+
const children = Array.from(temp.children);
|
|
1215
|
+
for (let j = 0, jLen = children.length; j < jLen; j++) {
|
|
1216
|
+
this.#frameContext.get('wysiwyg').appendChild(children[j]);
|
|
1135
1217
|
}
|
|
1136
|
-
this
|
|
1137
|
-
this
|
|
1218
|
+
this.#$.history.push(false, rootKey[i]);
|
|
1219
|
+
this.#$.selection.scrollTo(children.at(-1));
|
|
1138
1220
|
} else {
|
|
1139
|
-
this
|
|
1221
|
+
this.#$.viewer._setCodeView(this.#$.viewer._getCodeView() + '\n' + this._convertToCode(convertValue, false));
|
|
1140
1222
|
}
|
|
1141
1223
|
}
|
|
1142
|
-
}
|
|
1224
|
+
}
|
|
1143
1225
|
|
|
1144
1226
|
/**
|
|
1145
|
-
* @this {HTMLThis}
|
|
1146
1227
|
* @description Gets the current content to JSON data
|
|
1147
1228
|
* @param {Object} [options] Options
|
|
1148
|
-
* @param {boolean} [options.withFrame=false] Gets the current content with containing parent div.sun-editor-editable (
|
|
1229
|
+
* @param {boolean} [options.withFrame=false] Gets the current content with containing parent `div.sun-editor-editable` (`<div class="sun-editor-editable">{content}</div>`).
|
|
1149
1230
|
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1150
1231
|
* @returns {Object<string, *>} JSON data
|
|
1151
1232
|
*/
|
|
1152
1233
|
getJson({ withFrame, rootKey } = {}) {
|
|
1153
1234
|
return converter.htmlToJson(this.get({ withFrame, rootKey }));
|
|
1154
|
-
}
|
|
1235
|
+
}
|
|
1155
1236
|
|
|
1156
1237
|
/**
|
|
1157
|
-
* @this {HTMLThis}
|
|
1158
1238
|
* @description Sets the JSON data to the editor content
|
|
1159
1239
|
* @param {Object<string, *>} jsdonData HTML string
|
|
1160
1240
|
* @param {Object} [options] Options
|
|
@@ -1162,80 +1242,83 @@ HTML.prototype = {
|
|
|
1162
1242
|
*/
|
|
1163
1243
|
setJson(jsdonData, { rootKey } = {}) {
|
|
1164
1244
|
this.set(converter.jsonToHtml(jsdonData), { rootKey });
|
|
1165
|
-
}
|
|
1245
|
+
}
|
|
1166
1246
|
|
|
1167
1247
|
/**
|
|
1168
|
-
* @
|
|
1169
|
-
* @
|
|
1170
|
-
* @param {Element|Text|string} content Content to be copied to the clipboard
|
|
1248
|
+
* @description Call `clipboard.write` to copy the contents and display a success/failure toast message.
|
|
1249
|
+
* @param {Node|Element|Text|string} content Content to be copied to the clipboard
|
|
1171
1250
|
* @returns {Promise<boolean>} Success or failure
|
|
1172
1251
|
*/
|
|
1173
1252
|
async copy(content) {
|
|
1174
1253
|
try {
|
|
1175
|
-
|
|
1176
|
-
|
|
1254
|
+
if (typeof content !== 'string' && !dom.check.isElement(content) && !dom.check.isText(content)) return false;
|
|
1255
|
+
|
|
1256
|
+
if ((await clipboard.write(content)) === false) {
|
|
1257
|
+
this.#$.ui.showToast(this.#$.lang.message_copy_fail, this.#options.get('toastMessageTime').copy, 'error');
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
this.#$.ui.showToast(this.#$.lang.message_copy_success, this.#options.get('toastMessageTime').copy);
|
|
1177
1261
|
return true;
|
|
1178
1262
|
} catch (err) {
|
|
1179
1263
|
console.error('[SUNEDITOR.html.copy.fail] :', err);
|
|
1180
|
-
this
|
|
1264
|
+
this.#$.ui.showToast(this.#$.lang.message_copy_fail, this.#options.get('toastMessageTime').copy, 'error');
|
|
1181
1265
|
return false;
|
|
1182
1266
|
}
|
|
1183
|
-
}
|
|
1267
|
+
}
|
|
1184
1268
|
|
|
1185
1269
|
/**
|
|
1186
|
-
* @
|
|
1187
|
-
* @description Sets the content of the iframe's head tag and body tag when using the "iframe" or "iframe_fullPage" option.
|
|
1270
|
+
* @description Sets the content of the iframe's head tag and body tag when using the `iframe` or `iframe_fullPage` option.
|
|
1188
1271
|
* @param {{head: string, body: string}} ctx { head: HTML string, body: HTML string}
|
|
1189
1272
|
* @param {Object} [options] Options
|
|
1190
1273
|
* @param {number|Array<number>} [options.rootKey=null] Root index
|
|
1191
1274
|
*/
|
|
1192
1275
|
setFullPage(ctx, { rootKey } = {}) {
|
|
1193
|
-
if (!this
|
|
1276
|
+
if (!this.#frameOptions.get('iframe')) return false;
|
|
1194
1277
|
|
|
1195
|
-
if (!rootKey) rootKey = [this.
|
|
1278
|
+
if (!rootKey) rootKey = [this.#store.get('rootKey')];
|
|
1196
1279
|
else if (!Array.isArray(rootKey)) rootKey = [rootKey];
|
|
1197
1280
|
|
|
1198
1281
|
for (let i = 0; i < rootKey.length; i++) {
|
|
1199
|
-
this.
|
|
1200
|
-
if (ctx.head) this
|
|
1201
|
-
if (ctx.body) this
|
|
1202
|
-
this.
|
|
1282
|
+
this.#$.facade.changeFrameContext(rootKey[i]);
|
|
1283
|
+
if (ctx.head) this.#frameContext.get('_wd').head.innerHTML = ctx.head.replace(this.#disallowedTagsRegExp, '');
|
|
1284
|
+
if (ctx.body) this.#frameContext.get('_wd').body.innerHTML = this.clean(ctx.body, { forceFormat: true, whitelist: null, blacklist: null });
|
|
1285
|
+
this.#$.pluginManager.resetFileInfo();
|
|
1203
1286
|
}
|
|
1204
|
-
}
|
|
1287
|
+
}
|
|
1205
1288
|
|
|
1206
1289
|
/**
|
|
1207
|
-
* @this {HTMLThis}
|
|
1208
1290
|
* @description HTML code compression
|
|
1209
1291
|
* @param {string} html HTML string
|
|
1210
1292
|
* @returns {string} HTML string
|
|
1211
1293
|
*/
|
|
1212
1294
|
compress(html) {
|
|
1213
1295
|
return html.replace(/>\s+</g, '> <').replace(/\n/g, '').trim();
|
|
1214
|
-
}
|
|
1296
|
+
}
|
|
1215
1297
|
|
|
1216
1298
|
/**
|
|
1217
|
-
* @
|
|
1218
|
-
* @this {HTMLThis}
|
|
1299
|
+
* @internal
|
|
1219
1300
|
* @description construct wysiwyg area element to html string
|
|
1220
|
-
* @param {Node|string} html WYSIWYG element (this
|
|
1221
|
-
* @param {boolean} comp If true
|
|
1301
|
+
* @param {Node|string} html WYSIWYG element (this.#frameContext.get('wysiwyg')) or HTML string.
|
|
1302
|
+
* @param {boolean} comp If `true`, does not line break and indentation of tags.
|
|
1222
1303
|
* @returns {string}
|
|
1223
1304
|
*/
|
|
1224
1305
|
_convertToCode(html, comp) {
|
|
1225
1306
|
let returnHTML = '';
|
|
1226
1307
|
const wRegExp = RegExp;
|
|
1227
1308
|
const brReg = new wRegExp('^(BLOCKQUOTE|PRE|TABLE|THEAD|TBODY|TR|TH|TD|OL|UL|IMG|IFRAME|VIDEO|AUDIO|FIGURE|FIGCAPTION|HR|BR|CANVAS|SELECT)$', 'i');
|
|
1228
|
-
const wDoc = typeof html === 'string' ?
|
|
1309
|
+
const wDoc = typeof html === 'string' ? _d.createRange().createContextualFragment(html) : html;
|
|
1229
1310
|
const isFormat = (current) => {
|
|
1230
|
-
return this
|
|
1311
|
+
return this.#$.format.isLine(current) || this.#$.component.is(current);
|
|
1231
1312
|
};
|
|
1232
1313
|
const brChar = comp ? '' : '\n';
|
|
1233
1314
|
|
|
1234
|
-
const codeSize = comp ? 0 : this.
|
|
1315
|
+
const codeSize = comp ? 0 : this.#store.get('codeIndentSize') * 1;
|
|
1235
1316
|
const indentSize = codeSize > 0 ? new Array(codeSize + 1).join(' ') : '';
|
|
1236
1317
|
|
|
1237
1318
|
(function recursionFunc(element, indent) {
|
|
1238
|
-
const children = element
|
|
1319
|
+
const children = element?.childNodes;
|
|
1320
|
+
if (!children) return;
|
|
1321
|
+
|
|
1239
1322
|
const elementRegTest = brReg.test(element.nodeName);
|
|
1240
1323
|
const elementIndent = elementRegTest ? indent : '';
|
|
1241
1324
|
|
|
@@ -1271,46 +1354,46 @@ HTML.prototype = {
|
|
|
1271
1354
|
})(wDoc, '');
|
|
1272
1355
|
|
|
1273
1356
|
return returnHTML.trim() + brChar;
|
|
1274
|
-
}
|
|
1357
|
+
}
|
|
1275
1358
|
|
|
1276
1359
|
/**
|
|
1277
|
-
* @private
|
|
1278
|
-
* @this {HTMLThis}
|
|
1279
1360
|
* @description Checks whether the given list item node should be removed and handles necessary clean-up.
|
|
1280
1361
|
* @param {Node} item The list item node to be checked.
|
|
1362
|
+
* @param {boolean} isSingleItem Single item
|
|
1363
|
+
* @returns {{sc:Node, ec:Node}|null} An object containing the start and end containers if any transformations were made, otherwise `null`.
|
|
1281
1364
|
*/
|
|
1282
|
-
|
|
1283
|
-
const line = this
|
|
1365
|
+
#nodeRemoveListItem(item, isSingleItem) {
|
|
1366
|
+
const line = this.#$.format.getLine(item, null);
|
|
1284
1367
|
dom.utils.removeItem(item);
|
|
1285
1368
|
|
|
1286
|
-
if (!dom.check.isListCell(line)) return;
|
|
1369
|
+
if (!dom.check.isListCell(line) || isSingleItem) return;
|
|
1287
1370
|
|
|
1288
|
-
this
|
|
1371
|
+
const result = this.#$.nodeTransform.removeAllParents(line, null, null);
|
|
1289
1372
|
|
|
1290
1373
|
if (dom.check.isList(line?.firstChild)) {
|
|
1291
1374
|
line.insertBefore(dom.utils.createTextNode(unicode.zeroWidthSpace), line.firstChild);
|
|
1292
1375
|
}
|
|
1293
|
-
|
|
1376
|
+
|
|
1377
|
+
return result ? { sc: result.sc, ec: result.ec } : null;
|
|
1378
|
+
}
|
|
1294
1379
|
|
|
1295
1380
|
/**
|
|
1296
|
-
* @
|
|
1297
|
-
* @this {HTMLThis}
|
|
1298
|
-
* @description Recursive function when used to place a node in "BrLine" in "html.insertNode"
|
|
1381
|
+
* @description Recursive function when used to place a node in `BrLine` in `html.insertNode`
|
|
1299
1382
|
* @param {Node} oNode Node to be inserted
|
|
1300
|
-
* @returns {Node}
|
|
1383
|
+
* @returns {Node} `oNode`
|
|
1301
1384
|
*/
|
|
1302
|
-
|
|
1385
|
+
#setIntoFreeFormat(oNode) {
|
|
1303
1386
|
const parentNode = oNode.parentNode;
|
|
1304
1387
|
let oNodeChildren, lastONode;
|
|
1305
1388
|
|
|
1306
|
-
while (this
|
|
1389
|
+
while (this.#$.format.isLine(oNode) || this.#$.format.isBlock(oNode)) {
|
|
1307
1390
|
oNodeChildren = oNode.childNodes;
|
|
1308
1391
|
lastONode = null;
|
|
1309
1392
|
|
|
1310
1393
|
while (oNodeChildren[0]) {
|
|
1311
1394
|
lastONode = oNodeChildren[0];
|
|
1312
|
-
if (this
|
|
1313
|
-
this
|
|
1395
|
+
if (this.#$.format.isLine(lastONode) || this.#$.format.isBlock(lastONode)) {
|
|
1396
|
+
this.#setIntoFreeFormat(lastONode);
|
|
1314
1397
|
if (!oNode.parentNode) break;
|
|
1315
1398
|
oNodeChildren = oNode.childNodes;
|
|
1316
1399
|
continue;
|
|
@@ -1325,26 +1408,28 @@ HTML.prototype = {
|
|
|
1325
1408
|
}
|
|
1326
1409
|
|
|
1327
1410
|
return oNode;
|
|
1328
|
-
}
|
|
1411
|
+
}
|
|
1329
1412
|
|
|
1330
1413
|
/**
|
|
1331
|
-
* @
|
|
1332
|
-
* @this {HTMLThis}
|
|
1333
|
-
* @description Returns HTML string according to tag type and configurati isExcludeFormat.
|
|
1414
|
+
* @description Returns HTML string according to tag type and configuration `isExcludeFormat`.
|
|
1334
1415
|
* @param {Node} node Node
|
|
1335
|
-
* @param {boolean} forceFormat If true
|
|
1416
|
+
* @param {boolean} forceFormat If `true`, text nodes that do not have a format node is wrapped with the format tag.
|
|
1336
1417
|
*/
|
|
1337
|
-
|
|
1338
|
-
const defaultLine = this
|
|
1418
|
+
#makeLine(node, forceFormat) {
|
|
1419
|
+
const defaultLine = this.#options.get('defaultLine');
|
|
1339
1420
|
// element
|
|
1340
1421
|
if (node.nodeType === 1) {
|
|
1341
|
-
if (this.
|
|
1422
|
+
if (this.#disallowedTagNameRegExp.test(node.nodeName)) return '';
|
|
1342
1423
|
if (dom.check.isExcludeFormat(node)) return node.outerHTML;
|
|
1343
1424
|
|
|
1344
1425
|
const ch =
|
|
1345
|
-
dom.query.getListChildNodes(
|
|
1346
|
-
|
|
1347
|
-
|
|
1426
|
+
dom.query.getListChildNodes(
|
|
1427
|
+
node,
|
|
1428
|
+
(current) => {
|
|
1429
|
+
return dom.check.isSpanWithoutAttr(current) && !dom.query.getParentElement(current, dom.check.isExcludeFormat);
|
|
1430
|
+
},
|
|
1431
|
+
null,
|
|
1432
|
+
) || [];
|
|
1348
1433
|
for (let i = ch.length - 1, c; i >= 0; i--) {
|
|
1349
1434
|
c = /** @type {HTMLElement} */ (ch[i]);
|
|
1350
1435
|
c.outerHTML = c.innerHTML;
|
|
@@ -1352,9 +1437,9 @@ HTML.prototype = {
|
|
|
1352
1437
|
|
|
1353
1438
|
if (
|
|
1354
1439
|
!forceFormat ||
|
|
1355
|
-
this
|
|
1356
|
-
this
|
|
1357
|
-
this
|
|
1440
|
+
this.#$.format.isLine(node) ||
|
|
1441
|
+
this.#$.format.isBlock(node) ||
|
|
1442
|
+
this.#$.component.is(node) ||
|
|
1358
1443
|
dom.check.isMedia(node) ||
|
|
1359
1444
|
dom.check.isFigure(node) ||
|
|
1360
1445
|
(dom.check.isAnchor(node) && dom.check.isMedia(node.firstElementChild))
|
|
@@ -1378,18 +1463,16 @@ HTML.prototype = {
|
|
|
1378
1463
|
return html;
|
|
1379
1464
|
}
|
|
1380
1465
|
// comments
|
|
1381
|
-
if (node.nodeType === 8 && this
|
|
1466
|
+
if (node.nodeType === 8 && this.#allowHTMLComment) {
|
|
1382
1467
|
return '<!--' + node.textContent.trim() + '-->';
|
|
1383
1468
|
}
|
|
1384
1469
|
|
|
1385
1470
|
return '';
|
|
1386
|
-
}
|
|
1471
|
+
}
|
|
1387
1472
|
|
|
1388
1473
|
/**
|
|
1389
|
-
* @private
|
|
1390
|
-
* @this {HTMLThis}
|
|
1391
1474
|
* @description Fix tags that do not fit the editor format.
|
|
1392
|
-
* @param {DocumentFragment} documentFragment Document fragment
|
|
1475
|
+
* @param {DocumentFragment} documentFragment Document fragment `DOCUMENT_FRAGMENT_NODE` (nodeType === 11)
|
|
1393
1476
|
* @param {RegExp} htmlCheckWhitelistRegExp Editor tags whitelist
|
|
1394
1477
|
* @param {RegExp} htmlCheckBlacklistRegExp Editor tags blacklist
|
|
1395
1478
|
* @param {boolean} tagFilter Tag filter option
|
|
@@ -1397,84 +1480,89 @@ HTML.prototype = {
|
|
|
1397
1480
|
* @param {boolean} classFilter Class name filter option
|
|
1398
1481
|
* @param {boolean} _freeCodeViewMode Enforces strict HTML validation based on the editor`s policy
|
|
1399
1482
|
*/
|
|
1400
|
-
|
|
1483
|
+
#consistencyCheckOfHTML(documentFragment, htmlCheckWhitelistRegExp, htmlCheckBlacklistRegExp, tagFilter, formatFilter, classFilter, _freeCodeViewMode) {
|
|
1401
1484
|
const removeTags = [],
|
|
1402
1485
|
emptyTags = [],
|
|
1403
1486
|
wrongList = [],
|
|
1404
1487
|
withoutFormatCells = [];
|
|
1405
1488
|
|
|
1406
1489
|
// wrong position
|
|
1407
|
-
const wrongTags = dom.query.getListChildNodes(
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
// tag filter
|
|
1414
|
-
if (tagFilter) {
|
|
1415
|
-
// white list
|
|
1416
|
-
if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && dom.check.isExcludeFormat(current))) {
|
|
1417
|
-
removeTags.push(current);
|
|
1490
|
+
const wrongTags = dom.query.getListChildNodes(
|
|
1491
|
+
documentFragment,
|
|
1492
|
+
(current) => {
|
|
1493
|
+
if (current.nodeType !== 1) {
|
|
1494
|
+
if (formatFilter && dom.check.isList(current.parentElement)) removeTags.push(current);
|
|
1495
|
+
if (current.nodeType === 3 && !current.textContent.trim()) removeTags.push(current);
|
|
1418
1496
|
return false;
|
|
1419
1497
|
}
|
|
1420
|
-
}
|
|
1421
1498
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
!dom.check.isListCell(current) &&
|
|
1430
|
-
!dom.check.isAnchor(current) &&
|
|
1431
|
-
(this.format.isLine(current) || this.format.isBlock(current) || this.format.isTextStyleNode(current)) &&
|
|
1432
|
-
current.childNodes.length === 0 &&
|
|
1433
|
-
nrtag
|
|
1434
|
-
) {
|
|
1435
|
-
emptyTags.push(current);
|
|
1436
|
-
return false;
|
|
1499
|
+
// tag filter
|
|
1500
|
+
if (tagFilter) {
|
|
1501
|
+
// white list
|
|
1502
|
+
if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && dom.check.isExcludeFormat(current))) {
|
|
1503
|
+
removeTags.push(current);
|
|
1504
|
+
return false;
|
|
1505
|
+
}
|
|
1437
1506
|
}
|
|
1438
1507
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1508
|
+
const nrtag = !dom.query.getParentElement(current, dom.check.isExcludeFormat);
|
|
1509
|
+
|
|
1510
|
+
// formatFilter
|
|
1511
|
+
if (formatFilter) {
|
|
1512
|
+
// empty tags
|
|
1513
|
+
if (
|
|
1514
|
+
!dom.check.isTableElements(current) &&
|
|
1515
|
+
!dom.check.isListCell(current) &&
|
|
1516
|
+
!dom.check.isAnchor(current) &&
|
|
1517
|
+
(this.#$.format.isLine(current) || this.#$.format.isBlock(current) || this.#$.format.isTextStyleNode(current)) &&
|
|
1518
|
+
current.childNodes.length === 0 &&
|
|
1519
|
+
nrtag
|
|
1520
|
+
) {
|
|
1521
|
+
emptyTags.push(current);
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1444
1524
|
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
if (!this.format.isLine(fel) && !this.format.isBlock(fel) && !this.component.is(fel)) {
|
|
1449
|
-
withoutFormatCells.push(current);
|
|
1525
|
+
// wrong list
|
|
1526
|
+
if (dom.check.isList(current.parentNode) && !dom.check.isList(current) && !dom.check.isListCell(current)) {
|
|
1527
|
+
wrongList.push(current);
|
|
1450
1528
|
return false;
|
|
1451
1529
|
}
|
|
1530
|
+
|
|
1531
|
+
// table cells
|
|
1532
|
+
if (dom.check.isTableCell(current)) {
|
|
1533
|
+
const fel = current.firstElementChild;
|
|
1534
|
+
if (!this.#$.format.isLine(fel) && !this.#$.format.isBlock(fel) && !this.#$.component.is(fel)) {
|
|
1535
|
+
withoutFormatCells.push(current);
|
|
1536
|
+
return false;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1452
1539
|
}
|
|
1453
|
-
}
|
|
1454
1540
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1541
|
+
// class filter
|
|
1542
|
+
if (classFilter) {
|
|
1543
|
+
if (nrtag && current.className) {
|
|
1544
|
+
const className = Array.from(current.classList).map(this.#isAllowedClassName).join(' ').trim();
|
|
1545
|
+
if (className) current.className = className;
|
|
1546
|
+
else current.removeAttribute('class');
|
|
1547
|
+
}
|
|
1461
1548
|
}
|
|
1462
|
-
}
|
|
1463
1549
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1550
|
+
// format filter
|
|
1551
|
+
if (!formatFilter) {
|
|
1552
|
+
return false;
|
|
1553
|
+
}
|
|
1468
1554
|
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1555
|
+
const result =
|
|
1556
|
+
!_freeCodeViewMode &&
|
|
1557
|
+
current.parentNode !== documentFragment &&
|
|
1558
|
+
nrtag &&
|
|
1559
|
+
((dom.check.isListCell(current) && !dom.check.isList(current.parentNode)) ||
|
|
1560
|
+
((this.#$.format.isLine(current) || this.#$.component.is(current)) && !this.#$.format.isBlock(current.parentNode) && !dom.query.getParentElement(current, this.#$.component.is.bind(this.#$.component))));
|
|
1475
1561
|
|
|
1476
|
-
|
|
1477
|
-
|
|
1562
|
+
return result;
|
|
1563
|
+
},
|
|
1564
|
+
null,
|
|
1565
|
+
);
|
|
1478
1566
|
|
|
1479
1567
|
for (let i = 0, len = removeTags.length; i < len; i++) {
|
|
1480
1568
|
dom.utils.removeItem(removeTags[i]);
|
|
@@ -1488,8 +1576,8 @@ HTML.prototype = {
|
|
|
1488
1576
|
|
|
1489
1577
|
if (dom.query.getParentElement(t, dom.check.isListCell)) {
|
|
1490
1578
|
const cellChildren = t.childNodes;
|
|
1491
|
-
for (let j = cellChildren.length - 1;
|
|
1492
|
-
p.insertBefore(
|
|
1579
|
+
for (let j = cellChildren.length - 1; j >= 0; j--) {
|
|
1580
|
+
p.insertBefore(cellChildren[j], t);
|
|
1493
1581
|
}
|
|
1494
1582
|
checkTags.push(t);
|
|
1495
1583
|
} else {
|
|
@@ -1516,7 +1604,7 @@ HTML.prototype = {
|
|
|
1516
1604
|
|
|
1517
1605
|
tp = dom.utils.createElement('LI');
|
|
1518
1606
|
|
|
1519
|
-
if (this
|
|
1607
|
+
if (this.#$.format.isLine(t)) {
|
|
1520
1608
|
children = t.childNodes;
|
|
1521
1609
|
while (children[0]) {
|
|
1522
1610
|
tp.appendChild(children[0]);
|
|
@@ -1536,48 +1624,44 @@ HTML.prototype = {
|
|
|
1536
1624
|
f.innerHTML = t.textContent.trim().length === 0 && t.children.length === 0 ? '<br>' : t.innerHTML;
|
|
1537
1625
|
t.innerHTML = f.outerHTML;
|
|
1538
1626
|
}
|
|
1539
|
-
}
|
|
1627
|
+
}
|
|
1540
1628
|
|
|
1541
1629
|
/**
|
|
1542
|
-
* @
|
|
1543
|
-
* @this {HTMLThis}
|
|
1544
|
-
* @description Removes attribute values such as style and converts tags that do not conform to the "html5" standard.
|
|
1630
|
+
* @description Removes attribute values such as style and converts tags that do not conform to the `html5` standard.
|
|
1545
1631
|
* @param {string} html HTML string
|
|
1546
1632
|
* @returns {string} HTML string
|
|
1547
1633
|
*/
|
|
1548
|
-
|
|
1549
|
-
if (!this
|
|
1634
|
+
#styleNodeConvertor(html) {
|
|
1635
|
+
if (!this.#disallowedStyleNodesRegExp) return html;
|
|
1550
1636
|
|
|
1551
|
-
const ec = this
|
|
1552
|
-
return html.replace(this
|
|
1637
|
+
const ec = this.#options.get('_defaultStyleTagMap');
|
|
1638
|
+
return html.replace(this.#disallowedStyleNodesRegExp, (m, t, n, p) => {
|
|
1553
1639
|
return t + (typeof ec[n] === 'string' ? ec[n] : n) + (p ? ' ' + p : '');
|
|
1554
1640
|
});
|
|
1555
|
-
}
|
|
1641
|
+
}
|
|
1556
1642
|
|
|
1557
1643
|
/**
|
|
1558
|
-
* @
|
|
1559
|
-
* @this {HTMLThis}
|
|
1560
|
-
* @description Determines if formatting is required and returns a domTree
|
|
1644
|
+
* @description Determines if formatting is required and returns a `domTree`
|
|
1561
1645
|
* @param {DocumentFragment} domFrag documentFragment
|
|
1562
1646
|
* @returns {DocumentFragment}
|
|
1563
1647
|
*/
|
|
1564
|
-
|
|
1648
|
+
#editFormat(domFrag) {
|
|
1565
1649
|
let value = '',
|
|
1566
1650
|
f;
|
|
1567
1651
|
const tempTree = domFrag.childNodes;
|
|
1568
1652
|
|
|
1569
1653
|
for (let i = 0, len = tempTree.length, n; i < len; i++) {
|
|
1570
1654
|
n = /** @type {HTMLElement} */ (tempTree[i]);
|
|
1571
|
-
if (this.
|
|
1655
|
+
if (this.#allowedTagNameRegExp.test(n.nodeName)) {
|
|
1572
1656
|
value += n.outerHTML;
|
|
1573
1657
|
continue;
|
|
1574
1658
|
}
|
|
1575
1659
|
|
|
1576
1660
|
if (n.nodeType === 8) {
|
|
1577
1661
|
value += '<!-- ' + n.textContent + ' -->';
|
|
1578
|
-
} else if (!/meta/i.test(n.nodeName) && !this
|
|
1579
|
-
|
|
1580
|
-
if (this
|
|
1662
|
+
} else if (!/meta/i.test(n.nodeName) && !this.#$.format.isLine(n) && !this.#$.format.isBlock(n) && !this.#$.component.is(n) && !dom.check.isExcludeFormat(n)) {
|
|
1663
|
+
f ||= dom.utils.createElement(this.#options.get('defaultLine'));
|
|
1664
|
+
if (this.#$.format.isTextStyleNode(n)) {
|
|
1581
1665
|
/** @type {HTMLElement} */
|
|
1582
1666
|
(n).removeAttribute('style');
|
|
1583
1667
|
}
|
|
@@ -1595,19 +1679,17 @@ HTML.prototype = {
|
|
|
1595
1679
|
|
|
1596
1680
|
if (f) value += f.outerHTML;
|
|
1597
1681
|
|
|
1598
|
-
return
|
|
1599
|
-
}
|
|
1682
|
+
return _d.createRange().createContextualFragment(value);
|
|
1683
|
+
}
|
|
1600
1684
|
|
|
1601
1685
|
/**
|
|
1602
|
-
* @private
|
|
1603
|
-
* @this {HTMLThis}
|
|
1604
1686
|
* @description Converts a list of DOM nodes into an HTML list structure.
|
|
1605
|
-
* - If the node is already a list, its innerHTML is used. If it is a block element,
|
|
1687
|
+
* - If the node is already a list, its `innerHTML` is used. If it is a block element,
|
|
1606
1688
|
* - the function is called recursively.
|
|
1607
|
-
* @param {
|
|
1689
|
+
* @param {SunEditor.NodeCollection} domTree List of DOM nodes to be converted.
|
|
1608
1690
|
* @returns {string} The generated HTML list.
|
|
1609
1691
|
*/
|
|
1610
|
-
|
|
1692
|
+
#convertListCell(domTree) {
|
|
1611
1693
|
let html = '';
|
|
1612
1694
|
|
|
1613
1695
|
for (let i = 0, len = domTree.length, node; i < len; i++) {
|
|
@@ -1617,10 +1699,10 @@ HTML.prototype = {
|
|
|
1617
1699
|
html += node.innerHTML;
|
|
1618
1700
|
} else if (dom.check.isListCell(node)) {
|
|
1619
1701
|
html += node.outerHTML;
|
|
1620
|
-
} else if (this
|
|
1702
|
+
} else if (this.#$.format.isLine(node)) {
|
|
1621
1703
|
html += '<li>' + (node.innerHTML.trim() || '<br>') + '</li>';
|
|
1622
|
-
} else if (this
|
|
1623
|
-
html += this
|
|
1704
|
+
} else if (this.#$.format.isBlock(node) && !dom.check.isTableElements(node)) {
|
|
1705
|
+
html += this.#convertListCell(node.children);
|
|
1624
1706
|
} else {
|
|
1625
1707
|
html += '<li>' + /** @type {HTMLElement} */ (node).outerHTML + '</li>';
|
|
1626
1708
|
}
|
|
@@ -1630,55 +1712,51 @@ HTML.prototype = {
|
|
|
1630
1712
|
}
|
|
1631
1713
|
|
|
1632
1714
|
return html;
|
|
1633
|
-
}
|
|
1715
|
+
}
|
|
1634
1716
|
|
|
1635
1717
|
/**
|
|
1636
|
-
* @private
|
|
1637
|
-
* @this {HTMLThis}
|
|
1638
1718
|
* @description Checks whether the provided DOM nodes require formatting.
|
|
1639
1719
|
* @param {NodeList} domTree List of DOM nodes to check.
|
|
1640
|
-
* @returns {boolean}
|
|
1720
|
+
* @returns {boolean} `true` if formatting is required, otherwise `false`.
|
|
1641
1721
|
*/
|
|
1642
|
-
|
|
1722
|
+
#isFormatData(domTree) {
|
|
1643
1723
|
let requireFormat = false;
|
|
1644
1724
|
|
|
1645
1725
|
for (let i = 0, len = domTree.length, t; i < len; i++) {
|
|
1646
1726
|
t = domTree[i];
|
|
1647
|
-
if (t.nodeType === 1 && !this
|
|
1727
|
+
if (t.nodeType === 1 && !this.#$.format.isTextStyleNode(t) && !dom.check.isBreak(t) && !this.#disallowedTagNameRegExp.test(t.nodeName)) {
|
|
1648
1728
|
requireFormat = true;
|
|
1649
1729
|
break;
|
|
1650
1730
|
}
|
|
1651
1731
|
}
|
|
1652
1732
|
|
|
1653
1733
|
return requireFormat;
|
|
1654
|
-
}
|
|
1734
|
+
}
|
|
1655
1735
|
|
|
1656
1736
|
/**
|
|
1657
|
-
* @private
|
|
1658
|
-
* @this {HTMLThis}
|
|
1659
1737
|
* @description Cleans the inline style attributes of an HTML element.
|
|
1660
1738
|
* - Extracts allowed styles and removes disallowed ones based on editor settings.
|
|
1661
1739
|
* @param {string} m The full matched string from a regular expression.
|
|
1662
|
-
* @param {Array
|
|
1740
|
+
* @param {?Array} v The list of allowed attributes.
|
|
1663
1741
|
* @param {string} name The tag name of the element being cleaned.
|
|
1664
1742
|
* @returns {Array} The updated list of allowed attributes including cleaned styles.
|
|
1665
1743
|
*/
|
|
1666
|
-
|
|
1667
|
-
let sv = (m.match(
|
|
1668
|
-
if (this.
|
|
1669
|
-
const size = (m.match(
|
|
1670
|
-
const face = (m.match(
|
|
1671
|
-
const color = (m.match(
|
|
1744
|
+
#cleanStyle(m, v, name) {
|
|
1745
|
+
let sv = (m.match(_RE_STYLE_ATTR) || [])[0];
|
|
1746
|
+
if (this.#textStyleTags.includes(name) && !sv && (m.match(_RE_TAG_ATTRS) || [])[1]) {
|
|
1747
|
+
const size = (m.match(_RE_SIZE_ATTR) || [])[1];
|
|
1748
|
+
const face = (m.match(_RE_FACE_ATTR) || [])[1];
|
|
1749
|
+
const color = (m.match(_RE_COLOR_ATTR) || [])[1];
|
|
1672
1750
|
if (size || face || color) {
|
|
1673
1751
|
sv = 'style="' + (size ? 'font-size:' + numbers.get(Number(size) / 3.333, 1) + 'rem;' : '') + (face ? 'font-family:' + face + ';' : '') + (color ? 'color:' + color + ';' : '') + '"';
|
|
1674
1752
|
}
|
|
1675
1753
|
}
|
|
1676
1754
|
|
|
1677
1755
|
if (sv) {
|
|
1678
|
-
|
|
1756
|
+
v ||= [];
|
|
1679
1757
|
|
|
1680
1758
|
let mv;
|
|
1681
|
-
for (const [key, value] of this
|
|
1759
|
+
for (const [key, value] of this.#cleanStyleRegExpMap) {
|
|
1682
1760
|
if (key.test(name)) {
|
|
1683
1761
|
mv = value;
|
|
1684
1762
|
break;
|
|
@@ -1686,31 +1764,31 @@ HTML.prototype = {
|
|
|
1686
1764
|
}
|
|
1687
1765
|
if (!mv) return v;
|
|
1688
1766
|
|
|
1689
|
-
const style = sv.replace(
|
|
1767
|
+
const style = sv.replace(_RE_AMP_QUOT, '').match(mv);
|
|
1690
1768
|
if (!style) return v;
|
|
1691
1769
|
|
|
1692
1770
|
const allowedStyle = [];
|
|
1693
1771
|
for (let i = 0, len = style.length, r; i < len; i++) {
|
|
1694
|
-
r = style[i].match(
|
|
1695
|
-
if (r &&
|
|
1772
|
+
r = style[i].match(_RE_CSS_PROP);
|
|
1773
|
+
if (r && !_RE_CSS_GLOBAL.test(r[3])) {
|
|
1696
1774
|
const k = converter.kebabToCamelCase(r[1].trim());
|
|
1697
|
-
const cs = this
|
|
1775
|
+
const cs = this.#frameContext.get('wwComputedStyle')[k]?.replace(_RE_DQUOTE, '');
|
|
1698
1776
|
const c = r[3].trim();
|
|
1699
1777
|
switch (k) {
|
|
1700
1778
|
case 'fontFamily':
|
|
1701
|
-
if (!this
|
|
1779
|
+
if (!this.#$.plugins.font || !this.#$.plugins.font.fontArray.includes(c)) continue;
|
|
1702
1780
|
break;
|
|
1703
1781
|
case 'fontSize':
|
|
1704
|
-
if (!this
|
|
1705
|
-
if (!this
|
|
1706
|
-
r[0] = r[0].replace((r[0].match(
|
|
1782
|
+
if (!this.#$.plugins.fontSize) continue;
|
|
1783
|
+
if (!this.#fontSizeUnitRegExp.test(r[0])) {
|
|
1784
|
+
r[0] = r[0].replace((r[0].match(_RE_CSS_VALUE) || [])[1], converter.toFontUnit.bind(null, this.#options.get('fontSizeUnits')[0]));
|
|
1707
1785
|
}
|
|
1708
1786
|
break;
|
|
1709
1787
|
case 'color':
|
|
1710
|
-
if (!this
|
|
1788
|
+
if (!this.#$.plugins.fontColor || _RE_TRANSPARENT_COLOR.test(c)) continue;
|
|
1711
1789
|
break;
|
|
1712
1790
|
case 'backgroundColor':
|
|
1713
|
-
if (!this
|
|
1791
|
+
if (!this.#$.plugins.backgroundColor || _RE_TRANSPARENT_COLOR.test(c)) continue;
|
|
1714
1792
|
break;
|
|
1715
1793
|
}
|
|
1716
1794
|
|
|
@@ -1723,67 +1801,60 @@ HTML.prototype = {
|
|
|
1723
1801
|
}
|
|
1724
1802
|
|
|
1725
1803
|
return v;
|
|
1726
|
-
}
|
|
1804
|
+
}
|
|
1727
1805
|
|
|
1728
1806
|
/**
|
|
1729
|
-
* @private
|
|
1730
|
-
* @this {HTMLThis}
|
|
1731
1807
|
* @description Delete disallowed tags
|
|
1732
1808
|
* @param {string} html HTML string
|
|
1733
1809
|
* @returns {string}
|
|
1734
1810
|
*/
|
|
1735
|
-
|
|
1811
|
+
#deleteDisallowedTags(html, whitelistRegExp, blacklistRegExp) {
|
|
1736
1812
|
if (whitelistRegExp.test('<font>')) {
|
|
1737
1813
|
html = html.replace(/(<\/?)font(\s?)/gi, '$1span$2');
|
|
1738
1814
|
}
|
|
1739
1815
|
|
|
1740
1816
|
return html.replace(whitelistRegExp, '').replace(blacklistRegExp, '');
|
|
1741
|
-
}
|
|
1817
|
+
}
|
|
1742
1818
|
|
|
1743
1819
|
/**
|
|
1744
|
-
* @private
|
|
1745
|
-
* @this {HTMLThis}
|
|
1746
1820
|
* @description Recursively checks for duplicate text style nodes within a given parent node.
|
|
1747
1821
|
* @param {Node} oNode The node to check for duplicate styles.
|
|
1748
1822
|
* @param {Node} parentNode The parent node where the duplicate check occurs.
|
|
1749
1823
|
*/
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
(function recursionFunc(current) {
|
|
1754
|
-
inst._dupleCheck(current, parentNode);
|
|
1824
|
+
#checkDuplicateNode(oNode, parentNode) {
|
|
1825
|
+
const recursionFunc = (current) => {
|
|
1826
|
+
this.#dupleCheck(current, parentNode);
|
|
1755
1827
|
const childNodes = current.childNodes;
|
|
1756
1828
|
for (let i = 0, len = childNodes.length; i < len; i++) {
|
|
1757
1829
|
recursionFunc(childNodes[i]);
|
|
1758
1830
|
}
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1831
|
+
};
|
|
1832
|
+
recursionFunc(oNode);
|
|
1833
|
+
}
|
|
1761
1834
|
|
|
1762
1835
|
/**
|
|
1763
|
-
* @private
|
|
1764
|
-
* @this {HTMLThis}
|
|
1765
1836
|
* @description Recursively checks for duplicate text style nodes within a given parent node.
|
|
1766
1837
|
* - If duplicate styles are found, redundant attributes are removed.
|
|
1767
1838
|
* @param {Node} oNode The node to check for duplicate styles.
|
|
1768
1839
|
* @param {Node} parentNode The parent node where the duplicate check occurs.
|
|
1769
1840
|
* @returns {Node} The cleaned node with redundant styles removed.
|
|
1770
1841
|
*/
|
|
1771
|
-
|
|
1772
|
-
if (!this
|
|
1842
|
+
#dupleCheck(oNode, parentNode) {
|
|
1843
|
+
if (!this.#$.format.isTextStyleNode(oNode)) return;
|
|
1773
1844
|
|
|
1774
|
-
const oStyles = (oNode.style.cssText.match(
|
|
1845
|
+
const oStyles = (oNode.style.cssText.match(_RE_CSS_PARTS) || []).map(function (v) {
|
|
1775
1846
|
return v.trim();
|
|
1776
1847
|
});
|
|
1777
1848
|
const nodeName = oNode.nodeName;
|
|
1778
|
-
if (
|
|
1849
|
+
if (_RE_SPAN.test(nodeName) && oStyles.length === 0) return oNode;
|
|
1779
1850
|
|
|
1780
|
-
const inst = this
|
|
1851
|
+
const inst = this.#$.format;
|
|
1781
1852
|
let duple = false;
|
|
1782
1853
|
(function recursionFunc(ancestor) {
|
|
1783
1854
|
if (dom.check.isWysiwygFrame(ancestor) || !inst.isTextStyleNode(ancestor)) return;
|
|
1784
1855
|
if (ancestor.nodeName === nodeName) {
|
|
1785
1856
|
duple = true;
|
|
1786
|
-
const styles = ancestor.style.cssText.match(
|
|
1857
|
+
const styles = ancestor.style.cssText.match(_RE_CSS_PARTS) || [];
|
|
1787
1858
|
for (let i = 0, len = styles.length, j; i < len; i++) {
|
|
1788
1859
|
if ((j = oStyles.indexOf(styles[i].trim())) > -1) {
|
|
1789
1860
|
oStyles.splice(j, 1);
|
|
@@ -1802,24 +1873,84 @@ HTML.prototype = {
|
|
|
1802
1873
|
oNode.setAttribute('style', '');
|
|
1803
1874
|
oNode.removeAttribute('style');
|
|
1804
1875
|
}
|
|
1805
|
-
if (
|
|
1876
|
+
if (oNode.attributes.length === 0) {
|
|
1806
1877
|
oNode.setAttribute('data-duple', 'true');
|
|
1807
1878
|
}
|
|
1808
1879
|
}
|
|
1809
1880
|
|
|
1810
1881
|
return oNode;
|
|
1811
|
-
}
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
/**
|
|
1885
|
+
* @description Tag and tag attribute check RegExp function.
|
|
1886
|
+
* @param {string} m RegExp value
|
|
1887
|
+
* @param {string} t RegExp value
|
|
1888
|
+
* @returns {string}
|
|
1889
|
+
*/
|
|
1890
|
+
#CleanElements(attrFilter, styleFilter, m, t) {
|
|
1891
|
+
if (_RE_XML_NS_TAG.test(m)) return m;
|
|
1892
|
+
|
|
1893
|
+
let v = null;
|
|
1894
|
+
const tagName = t.match(_RE_TAG_NAME)[0].toLowerCase();
|
|
1895
|
+
|
|
1896
|
+
if (attrFilter) {
|
|
1897
|
+
// blacklist
|
|
1898
|
+
const bAttr = this.#attributeBlacklist[tagName];
|
|
1899
|
+
m = m.replace(_RE_ON_HANDLER, '');
|
|
1900
|
+
if (bAttr) m = m.replace(bAttr, '');
|
|
1901
|
+
else m = m.replace(this.#attributeBlacklistRegExp, '');
|
|
1902
|
+
|
|
1903
|
+
// whitelist
|
|
1904
|
+
const wAttr = this.#attributeWhitelist[tagName];
|
|
1905
|
+
if (wAttr) v = m.match(wAttr);
|
|
1906
|
+
else v = m.match(this.#attributeWhitelistRegExp);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
if (!styleFilter) return m;
|
|
1910
|
+
|
|
1911
|
+
// attribute
|
|
1912
|
+
if (tagName === 'a') {
|
|
1913
|
+
const sv = m.match(/(?:(?:id|name)\s*=\s*(?:"|')[^"']*(?:"|'))/g);
|
|
1914
|
+
if (sv) {
|
|
1915
|
+
v ||= [];
|
|
1916
|
+
v.push(sv[0]);
|
|
1917
|
+
}
|
|
1918
|
+
} else if (!v || !_RE_STYLE_EQ.test(v.toString())) {
|
|
1919
|
+
if (this.#textStyleTags.includes(tagName)) {
|
|
1920
|
+
v = this.#cleanStyle(m, v, tagName);
|
|
1921
|
+
} else if (this.#$.format.isLine(tagName)) {
|
|
1922
|
+
v = this.#cleanStyle(m, v, 'line');
|
|
1923
|
+
} else if (this.#cleanStyleTagKeyRegExp.test(tagName)) {
|
|
1924
|
+
v = this.#cleanStyle(m, v, tagName);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
// figure
|
|
1929
|
+
if (dom.check.isMedia(tagName) || dom.check.isFigure(tagName)) {
|
|
1930
|
+
const sv = m.match(_RE_STYLE_ATTR);
|
|
1931
|
+
v ||= [];
|
|
1932
|
+
if (sv) v.push(sv[0]);
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
if (v) {
|
|
1936
|
+
for (let i = 0, len = v.length, a; i < len; i++) {
|
|
1937
|
+
a = _isSafeAttribute(v[i].trim()) ? v[i] : '';
|
|
1938
|
+
t += (_RE_LEADING_SPACE.test(a) ? '' : ' ') + a;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
return t;
|
|
1943
|
+
}
|
|
1812
1944
|
|
|
1813
1945
|
/**
|
|
1814
|
-
* @
|
|
1815
|
-
* @this {HTMLThis}
|
|
1946
|
+
* @internal
|
|
1816
1947
|
* @description Reset autoStyleify options.
|
|
1817
1948
|
* @param {Array.<string>} autoStyleify Styles applied automatically on text input.
|
|
1818
|
-
* - ex ["bold", "underline", "italic", "strike"]
|
|
1949
|
+
* - ex `["bold", "underline", "italic", "strike"]`
|
|
1819
1950
|
*/
|
|
1820
1951
|
__resetAutoStyleify(autoStyleify) {
|
|
1821
1952
|
if (autoStyleify.length > 0) {
|
|
1822
|
-
const convertTextTags = this
|
|
1953
|
+
const convertTextTags = this.#options.get('convertTextTags');
|
|
1823
1954
|
const styleToTag = {};
|
|
1824
1955
|
autoStyleify.forEach((style) => {
|
|
1825
1956
|
switch (style) {
|
|
@@ -1837,80 +1968,68 @@ HTML.prototype = {
|
|
|
1837
1968
|
break;
|
|
1838
1969
|
}
|
|
1839
1970
|
});
|
|
1840
|
-
this
|
|
1971
|
+
this.#autoStyleify = styleToTag;
|
|
1841
1972
|
} else {
|
|
1842
|
-
this
|
|
1973
|
+
this.#autoStyleify = null;
|
|
1843
1974
|
}
|
|
1844
|
-
},
|
|
1845
|
-
|
|
1846
|
-
constructor: HTML
|
|
1847
|
-
};
|
|
1848
|
-
|
|
1849
|
-
/**
|
|
1850
|
-
* @private
|
|
1851
|
-
* @this {HTMLThis}
|
|
1852
|
-
* @description Tag and tag attribute check RegExp function.
|
|
1853
|
-
* @param {string} m RegExp value
|
|
1854
|
-
* @param {string} t RegExp value
|
|
1855
|
-
* @returns {string}
|
|
1856
|
-
*/
|
|
1857
|
-
function CleanElements(attrFilter, styleFilter, m, t) {
|
|
1858
|
-
if (/^<[a-z0-9]+:[a-z0-9]+/i.test(m)) return m;
|
|
1859
|
-
|
|
1860
|
-
let v = null;
|
|
1861
|
-
const tagName = t.match(/(?!<)[a-zA-Z0-9-]+/)[0].toLowerCase();
|
|
1862
|
-
|
|
1863
|
-
if (attrFilter) {
|
|
1864
|
-
// blacklist
|
|
1865
|
-
const bAttr = this._attributeBlacklist[tagName];
|
|
1866
|
-
m = m.replace(/\s(?:on[a-z]+)\s*=\s*(")[^"]*\1/gi, '');
|
|
1867
|
-
if (bAttr) m = m.replace(bAttr, '');
|
|
1868
|
-
else m = m.replace(this._attributeBlacklistRegExp, '');
|
|
1869
|
-
|
|
1870
|
-
// whitelist
|
|
1871
|
-
const wAttr = this._attributeWhitelist[tagName];
|
|
1872
|
-
if (wAttr) v = m.match(wAttr);
|
|
1873
|
-
else v = m.match(this._attributeWhitelistRegExp);
|
|
1874
1975
|
}
|
|
1875
1976
|
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
}
|
|
1885
|
-
} else if (!v || !/style=/i.test(v.toString())) {
|
|
1886
|
-
if (this._textStyleTags.includes(tagName)) {
|
|
1887
|
-
v = this._cleanStyle(m, v, tagName);
|
|
1888
|
-
} else if (this.format.isLine(tagName)) {
|
|
1889
|
-
v = this._cleanStyle(m, v, 'line');
|
|
1890
|
-
} else if (this._cleanStyleTagKeyRegExp.test(tagName)) {
|
|
1891
|
-
v = this._cleanStyle(m, v, tagName);
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
|
-
// figure
|
|
1896
|
-
if (dom.check.isMedia(tagName) || dom.check.isFigure(tagName)) {
|
|
1897
|
-
const sv = m.match(/style\s*=\s*(?:"|')[^"']*(?:"|')/);
|
|
1898
|
-
if (!v) v = [];
|
|
1899
|
-
if (sv) v.push(sv[0]);
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
if (v) {
|
|
1903
|
-
for (let i = 0, len = v.length, a; i < len; i++) {
|
|
1904
|
-
a = /^(?:href|src)\s*=\s*('|"|\s)*javascript\s*:/i.test(v[i].trim()) ? '' : v[i];
|
|
1905
|
-
t += (/^\s/.test(a) ? '' : ' ') + a;
|
|
1977
|
+
/**
|
|
1978
|
+
* @internal
|
|
1979
|
+
* @description Destroy the HTML instance and release memory
|
|
1980
|
+
*/
|
|
1981
|
+
_destroy() {
|
|
1982
|
+
// Clear Map
|
|
1983
|
+
if (this.#cleanStyleRegExpMap) {
|
|
1984
|
+
this.#cleanStyleRegExpMap.clear();
|
|
1906
1985
|
}
|
|
1907
1986
|
}
|
|
1987
|
+
}
|
|
1908
1988
|
|
|
1909
|
-
|
|
1989
|
+
/** Module-level regex constants */
|
|
1990
|
+
// #CleanElements
|
|
1991
|
+
const _RE_XML_NS_TAG = /^<[a-z0-9]+:[a-z0-9]+/i;
|
|
1992
|
+
const _RE_TAG_NAME = /(?!<)[a-zA-Z0-9-]+/;
|
|
1993
|
+
const _RE_ON_HANDLER = /\s(?:on[a-z]+)\s*=\s*(")[^"]*\1/gi;
|
|
1994
|
+
const _RE_STYLE_EQ = /style=/i;
|
|
1995
|
+
const _RE_STYLE_ATTR = /style\s*=\s*(?:"|')[^"']*(?:"|')/;
|
|
1996
|
+
const _RE_LEADING_SPACE = /^\s/;
|
|
1997
|
+
|
|
1998
|
+
// #cleanStyle
|
|
1999
|
+
const _RE_TAG_ATTRS = /<[^\s]+\s(.+)/;
|
|
2000
|
+
const _RE_SIZE_ATTR = /\ssize="([^"]+)"/i;
|
|
2001
|
+
const _RE_FACE_ATTR = /\sface="([^"]+)"/i;
|
|
2002
|
+
const _RE_COLOR_ATTR = /\scolor="([^"]+)"/i;
|
|
2003
|
+
const _RE_AMP_QUOT = /"/g;
|
|
2004
|
+
const _RE_CSS_PROP = /([a-zA-Z0-9-]+)(:)([^"']+)/;
|
|
2005
|
+
const _RE_CSS_GLOBAL = /inherit|initial|revert|unset/i;
|
|
2006
|
+
const _RE_DQUOTE = /"/g;
|
|
2007
|
+
const _RE_CSS_VALUE = /:\s*([^;]+)/;
|
|
2008
|
+
const _RE_TRANSPARENT_COLOR = /rgba\(([0-9]+\s*,\s*){3}0\)|windowtext/i;
|
|
2009
|
+
|
|
2010
|
+
// #dupleCheck
|
|
2011
|
+
const _RE_CSS_PARTS = /[^;]+;/g;
|
|
2012
|
+
const _RE_SPAN = /^span$/i;
|
|
2013
|
+
|
|
2014
|
+
// _isSafeAttribute
|
|
2015
|
+
const _SAFE_URL_PROTOCOL = /^(?:https?|ftps?|mailto|tel|blob|sms|geo|webcal|callto):|^[#/]|^data:image\//i;
|
|
2016
|
+
const _URL_ATTR_PATTERN = /^(?:href|src)\s*=\s*(?:'|"|\s)*/i;
|
|
2017
|
+
const _RE_ATTR_VALUE = /=\s*(?:"|'|)\s*([^"'\s>]*)/;
|
|
2018
|
+
const _RE_WHITESPACE = /[\s\r\n\t]+/g;
|
|
2019
|
+
const _RE_HTML_ENTITY = /&(#x?[0-9a-f]+|[a-z]+);/gi;
|
|
2020
|
+
const _RE_COLON = /:/i;
|
|
2021
|
+
|
|
2022
|
+
function _isSafeAttribute(attr) {
|
|
2023
|
+
if (!_URL_ATTR_PATTERN.test(attr)) return true;
|
|
2024
|
+
|
|
2025
|
+
const urlMatch = attr.match(_RE_ATTR_VALUE);
|
|
2026
|
+
if (!urlMatch) return true;
|
|
2027
|
+
|
|
2028
|
+
const url = urlMatch[1].replace(_RE_WHITESPACE, '').replace(_RE_HTML_ENTITY, '');
|
|
2029
|
+
return _SAFE_URL_PROTOCOL.test(url) || !_RE_COLON.test(url);
|
|
1910
2030
|
}
|
|
1911
2031
|
|
|
1912
2032
|
/**
|
|
1913
|
-
* @private
|
|
1914
2033
|
* @description Get related list
|
|
1915
2034
|
* @param {string} str Regular expression string
|
|
1916
2035
|
* @param {string} str2 Regular expression string
|