@shelamkoff/rector 1.0.0
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/LICENSE +21 -0
- package/NOTICE +17 -0
- package/README.md +23 -0
- package/THIRD_PARTY_LICENSES.txt +298 -0
- package/dist/I18n.d.ts +1 -0
- package/dist/core/Block.d.ts +110 -0
- package/dist/core/Block.js +390 -0
- package/dist/core/BlockAnimator.d.ts +39 -0
- package/dist/core/BlockAnimator.js +106 -0
- package/dist/core/BlockManager.d.ts +163 -0
- package/dist/core/BlockManager.js +640 -0
- package/dist/core/BlockOperations.d.ts +67 -0
- package/dist/core/BlockOperations.js +238 -0
- package/dist/core/ChangeNotifier.d.ts +15 -0
- package/dist/core/ChangeNotifier.js +55 -0
- package/dist/core/CommandDispatcher.d.ts +52 -0
- package/dist/core/CommandDispatcher.js +208 -0
- package/dist/core/CrossBlockSelection.d.ts +30 -0
- package/dist/core/CrossBlockSelection.js +74 -0
- package/dist/core/Diagnostics.d.ts +23 -0
- package/dist/core/Diagnostics.js +62 -0
- package/dist/core/DocumentSchema.d.ts +29 -0
- package/dist/core/DocumentSchema.js +147 -0
- package/dist/core/DocumentSnapshotStore.d.ts +34 -0
- package/dist/core/DocumentSnapshotStore.js +153 -0
- package/dist/core/DragManager.d.ts +17 -0
- package/dist/core/DragManager.js +223 -0
- package/dist/core/EditorFacade.d.ts +154 -0
- package/dist/core/EditorFacade.js +361 -0
- package/dist/core/EditorHolderOwnership.d.ts +12 -0
- package/dist/core/EditorHolderOwnership.js +28 -0
- package/dist/core/I18n.d.ts +147 -0
- package/dist/core/I18n.js +296 -0
- package/dist/core/InlinePatternMatcher.d.ts +26 -0
- package/dist/core/InlinePatternMatcher.js +267 -0
- package/dist/core/InlinePluginRegistry.d.ts +55 -0
- package/dist/core/InlinePluginRegistry.js +128 -0
- package/dist/core/KeyboardManager.d.ts +17 -0
- package/dist/core/KeyboardManager.js +231 -0
- package/dist/core/LifecycleScope.d.ts +19 -0
- package/dist/core/LifecycleScope.js +41 -0
- package/dist/core/MouseSelectionManager.d.ts +30 -0
- package/dist/core/MouseSelectionManager.js +307 -0
- package/dist/core/PluginOwnership.d.ts +9 -0
- package/dist/core/PluginOwnership.js +40 -0
- package/dist/core/PopupManager.d.ts +50 -0
- package/dist/core/PopupManager.js +183 -0
- package/dist/core/PreservedBlockPlugin.d.ts +11 -0
- package/dist/core/PreservedBlockPlugin.js +42 -0
- package/dist/core/PublicEditorApi.d.ts +82 -0
- package/dist/core/PublicEditorApi.js +158 -0
- package/dist/core/SelectionManager.d.ts +50 -0
- package/dist/core/SelectionManager.js +205 -0
- package/dist/core/ShortcutRegistry.d.ts +29 -0
- package/dist/core/ShortcutRegistry.js +106 -0
- package/dist/core/SlashCommands.d.ts +34 -0
- package/dist/core/SlashCommands.js +483 -0
- package/dist/core/StyleInjector.d.ts +1 -0
- package/dist/core/StyleInjector.js +1 -0
- package/dist/core/Tooltip.d.ts +17 -0
- package/dist/core/Tooltip.js +60 -0
- package/dist/core/TriggerManager.d.ts +17 -0
- package/dist/core/TriggerManager.js +159 -0
- package/dist/core/TypeSelector.d.ts +31 -0
- package/dist/core/TypeSelector.js +464 -0
- package/dist/core/UndoManager.d.ts +72 -0
- package/dist/core/UndoManager.js +383 -0
- package/dist/core/block-settings-menu/BlockActions.d.ts +81 -0
- package/dist/core/block-settings-menu/BlockActions.js +233 -0
- package/dist/core/block-settings-menu/BlockSettingsMenu.d.ts +46 -0
- package/dist/core/block-settings-menu/BlockSettingsMenu.js +205 -0
- package/dist/core/block-settings-menu/MenuBuilder.d.ts +43 -0
- package/dist/core/block-settings-menu/MenuBuilder.js +203 -0
- package/dist/core/block-settings-menu/MenuPositioner.d.ts +22 -0
- package/dist/core/block-settings-menu/MenuPositioner.js +79 -0
- package/dist/core/clipboard/Clipboard.d.ts +47 -0
- package/dist/core/clipboard/Clipboard.js +653 -0
- package/dist/core/clipboard/CrossBlockEditor.d.ts +43 -0
- package/dist/core/clipboard/CrossBlockEditor.js +198 -0
- package/dist/core/clipboard/PasteRouter.d.ts +27 -0
- package/dist/core/clipboard/PasteRouter.js +86 -0
- package/dist/core/clipboard/pasteInsert.d.ts +35 -0
- package/dist/core/clipboard/pasteInsert.js +215 -0
- package/dist/core/clipboard/pasteUtils.d.ts +25 -0
- package/dist/core/clipboard/pasteUtils.js +98 -0
- package/dist/core/config.d.ts +77 -0
- package/dist/core/config.js +100 -0
- package/dist/core/constants.d.ts +23 -0
- package/dist/core/constants.js +30 -0
- package/dist/core/crossBlockConvert.d.ts +38 -0
- package/dist/core/crossBlockConvert.js +253 -0
- package/dist/core/dom.d.ts +35 -0
- package/dist/core/dom.js +65 -0
- package/dist/core/editorEvents.d.ts +89 -0
- package/dist/core/editorEvents.js +60 -0
- package/dist/core/hydrateInlinePlugins.d.ts +9 -0
- package/dist/core/hydrateInlinePlugins.js +36 -0
- package/dist/core/icons.d.ts +10 -0
- package/dist/core/icons.js +21 -0
- package/dist/core/index.d.ts +59 -0
- package/dist/core/index.js +703 -0
- package/dist/core/inline-toolbar/ActionsPanel.d.ts +61 -0
- package/dist/core/inline-toolbar/ActionsPanel.js +132 -0
- package/dist/core/inline-toolbar/InlinePositioner.d.ts +20 -0
- package/dist/core/inline-toolbar/InlinePositioner.js +63 -0
- package/dist/core/inline-toolbar/InlineToolbar.d.ts +45 -0
- package/dist/core/inline-toolbar/InlineToolbar.js +456 -0
- package/dist/core/inline-toolbar/PluginControlsSlot.d.ts +52 -0
- package/dist/core/inline-toolbar/PluginControlsSlot.js +118 -0
- package/dist/core/inline-toolbar/SelectionTracker.d.ts +63 -0
- package/dist/core/inline-toolbar/SelectionTracker.js +173 -0
- package/dist/core/inlinePluginInsert.d.ts +13 -0
- package/dist/core/inlinePluginInsert.js +56 -0
- package/dist/core/locale/en.d.ts +35 -0
- package/dist/core/locale/en.js +37 -0
- package/dist/core/locale/ru.d.ts +35 -0
- package/dist/core/locale/ru.js +37 -0
- package/dist/core/menuKeyboardNav.d.ts +15 -0
- package/dist/core/menuKeyboardNav.js +52 -0
- package/dist/core/sanitize.d.ts +2 -0
- package/dist/core/sanitize.js +6 -0
- package/dist/core/splitConvert.d.ts +46 -0
- package/dist/core/splitConvert.js +190 -0
- package/dist/core/textOffset.d.ts +46 -0
- package/dist/core/textOffset.js +180 -0
- package/dist/core/themes/dark.css +30 -0
- package/dist/core/themes/light.css +33 -0
- package/dist/core/themes/variables.css +1170 -0
- package/dist/core/toolbar/OffcanvasRoot.d.ts +37 -0
- package/dist/core/toolbar/OffcanvasRoot.js +127 -0
- package/dist/core/toolbar/Toolbar.d.ts +70 -0
- package/dist/core/toolbar/Toolbar.js +432 -0
- package/dist/core/toolbar/ToolbarPositioner.d.ts +60 -0
- package/dist/core/toolbar/ToolbarPositioner.js +188 -0
- package/dist/core/toolbar/ToolboxBuilder.d.ts +44 -0
- package/dist/core/toolbar/ToolboxBuilder.js +260 -0
- package/dist/core/types.d.ts +747 -0
- package/dist/core/uid.d.ts +6 -0
- package/dist/core/uid.js +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/inline-plugins/README.md +35 -0
- package/dist/inline-plugins/README.ru.md +35 -0
- package/dist/inline-plugins/color/README.md +61 -0
- package/dist/inline-plugins/color/README.ru.md +61 -0
- package/dist/inline-plugins/color.d.ts +8 -0
- package/dist/inline-plugins/color.js +184 -0
- package/dist/inline-plugins/locale/en.d.ts +7 -0
- package/dist/inline-plugins/locale/en.js +6 -0
- package/dist/inline-plugins/locale/ru.d.ts +7 -0
- package/dist/inline-plugins/locale/ru.js +6 -0
- package/dist/inline-plugins/mention/README.md +97 -0
- package/dist/inline-plugins/mention/README.ru.md +97 -0
- package/dist/inline-plugins/mention/index.d.ts +110 -0
- package/dist/inline-plugins/mention/index.js +1897 -0
- package/dist/inline-plugins/mention/styles.css +183 -0
- package/dist/inline-plugins/mention/widget.d.ts +16 -0
- package/dist/inline-plugins/mention/widget.js +75 -0
- package/dist/inline-tools/align.d.ts +14 -0
- package/dist/inline-tools/align.js +120 -0
- package/dist/inline-tools/bold.d.ts +8 -0
- package/dist/inline-tools/bold.js +12 -0
- package/dist/inline-tools/caseTransform.d.ts +8 -0
- package/dist/inline-tools/caseTransform.js +87 -0
- package/dist/inline-tools/clearFormatting.d.ts +8 -0
- package/dist/inline-tools/clearFormatting.js +74 -0
- package/dist/inline-tools/code.d.ts +8 -0
- package/dist/inline-tools/code.js +12 -0
- package/dist/inline-tools/colorPicker.d.ts +7 -0
- package/dist/inline-tools/colorPicker.js +271 -0
- package/dist/inline-tools/defaults.d.ts +10 -0
- package/dist/inline-tools/defaults.js +57 -0
- package/dist/inline-tools/fontSize.d.ts +7 -0
- package/dist/inline-tools/fontSize.js +524 -0
- package/dist/inline-tools/italic.d.ts +8 -0
- package/dist/inline-tools/italic.js +12 -0
- package/dist/inline-tools/link.d.ts +12 -0
- package/dist/inline-tools/link.js +251 -0
- package/dist/inline-tools/marker.d.ts +8 -0
- package/dist/inline-tools/marker.js +12 -0
- package/dist/inline-tools/scriptTool.d.ts +11 -0
- package/dist/inline-tools/scriptTool.js +149 -0
- package/dist/inline-tools/strikethrough.d.ts +8 -0
- package/dist/inline-tools/strikethrough.js +12 -0
- package/dist/inline-tools/utils.d.ts +220 -0
- package/dist/inline-tools/utils.js +992 -0
- package/dist/locale/en.d.ts +311 -0
- package/dist/locale/en.js +52 -0
- package/dist/locale/ru.d.ts +315 -0
- package/dist/locale/ru.js +52 -0
- package/dist/plugins/BlockPluginAbstract.d.ts +82 -0
- package/dist/plugins/BlockPluginAbstract.js +111 -0
- package/dist/plugins/README.md +44 -0
- package/dist/plugins/README.ru.md +40 -0
- package/dist/plugins/async.d.ts +35 -0
- package/dist/plugins/async.js +79 -0
- package/dist/plugins/attaches/README.md +86 -0
- package/dist/plugins/attaches/README.ru.md +87 -0
- package/dist/plugins/attaches/attaches.css +374 -0
- package/dist/plugins/attaches/index.d.ts +133 -0
- package/dist/plugins/attaches/index.js +921 -0
- package/dist/plugins/attaches/locale/en.d.ts +23 -0
- package/dist/plugins/attaches/locale/en.js +19 -0
- package/dist/plugins/attaches/locale/keys.d.ts +5 -0
- package/dist/plugins/attaches/locale/ru.d.ts +25 -0
- package/dist/plugins/attaches/locale/ru.js +19 -0
- package/dist/plugins/carousel/README.md +108 -0
- package/dist/plugins/carousel/README.ru.md +109 -0
- package/dist/plugins/carousel/carousel.css +198 -0
- package/dist/plugins/carousel/icons.d.ts +12 -0
- package/dist/plugins/carousel/icons.js +13 -0
- package/dist/plugins/carousel/index.d.ts +137 -0
- package/dist/plugins/carousel/index.js +859 -0
- package/dist/plugins/carousel/locale/en.d.ts +41 -0
- package/dist/plugins/carousel/locale/en.js +40 -0
- package/dist/plugins/carousel/locale/keys.d.ts +2 -0
- package/dist/plugins/carousel/locale/ru.d.ts +41 -0
- package/dist/plugins/carousel/locale/ru.js +40 -0
- package/dist/plugins/carousel/state.d.ts +29 -0
- package/dist/plugins/carousel/state.js +42 -0
- package/dist/plugins/checklist/README.md +54 -0
- package/dist/plugins/checklist/README.ru.md +55 -0
- package/dist/plugins/checklist/checklist.css +80 -0
- package/dist/plugins/checklist/index.d.ts +75 -0
- package/dist/plugins/checklist/index.js +407 -0
- package/dist/plugins/checklist/locale/en.d.ts +5 -0
- package/dist/plugins/checklist/locale/en.js +4 -0
- package/dist/plugins/checklist/locale/keys.d.ts +5 -0
- package/dist/plugins/checklist/locale/ru.d.ts +5 -0
- package/dist/plugins/checklist/locale/ru.js +4 -0
- package/dist/plugins/checklist/mapTextFields.d.ts +1 -0
- package/dist/plugins/checklist/mapTextFields.js +1 -0
- package/dist/plugins/code/README.md +59 -0
- package/dist/plugins/code/README.ru.md +60 -0
- package/dist/plugins/code/code.css +360 -0
- package/dist/plugins/code/index.d.ts +109 -0
- package/dist/plugins/code/index.js +1012 -0
- package/dist/plugins/code/locale/en.d.ts +11 -0
- package/dist/plugins/code/locale/en.js +10 -0
- package/dist/plugins/code/locale/keys.d.ts +5 -0
- package/dist/plugins/code/locale/ru.d.ts +11 -0
- package/dist/plugins/code/locale/ru.js +10 -0
- package/dist/plugins/columns/README.md +55 -0
- package/dist/plugins/columns/README.ru.md +56 -0
- package/dist/plugins/columns/columns.css +103 -0
- package/dist/plugins/columns/index.d.ts +58 -0
- package/dist/plugins/columns/index.js +236 -0
- package/dist/plugins/columns/locale/en.d.ts +6 -0
- package/dist/plugins/columns/locale/en.js +5 -0
- package/dist/plugins/columns/locale/keys.d.ts +5 -0
- package/dist/plugins/columns/locale/ru.d.ts +6 -0
- package/dist/plugins/columns/locale/ru.js +5 -0
- package/dist/plugins/delimiter/README.md +48 -0
- package/dist/plugins/delimiter/README.ru.md +49 -0
- package/dist/plugins/delimiter/delimiter.css +22 -0
- package/dist/plugins/delimiter/index.d.ts +38 -0
- package/dist/plugins/delimiter/index.js +69 -0
- package/dist/plugins/delimiter/locale/en.d.ts +4 -0
- package/dist/plugins/delimiter/locale/en.js +3 -0
- package/dist/plugins/delimiter/locale/keys.d.ts +5 -0
- package/dist/plugins/delimiter/locale/ru.d.ts +4 -0
- package/dist/plugins/delimiter/locale/ru.js +3 -0
- package/dist/plugins/embed/README.md +81 -0
- package/dist/plugins/embed/README.ru.md +82 -0
- package/dist/plugins/embed/embed.css +558 -0
- package/dist/plugins/embed/index.d.ts +271 -0
- package/dist/plugins/embed/index.js +967 -0
- package/dist/plugins/embed/locale/en.d.ts +20 -0
- package/dist/plugins/embed/locale/en.js +19 -0
- package/dist/plugins/embed/locale/keys.d.ts +5 -0
- package/dist/plugins/embed/locale/ru.d.ts +20 -0
- package/dist/plugins/embed/locale/ru.js +19 -0
- package/dist/plugins/embed/player.d.ts +1 -0
- package/dist/plugins/embed/player.js +1 -0
- package/dist/plugins/gallery/README.md +92 -0
- package/dist/plugins/gallery/README.ru.md +93 -0
- package/dist/plugins/gallery/css.d.ts +52 -0
- package/dist/plugins/gallery/css.js +67 -0
- package/dist/plugins/gallery/gallery.css +860 -0
- package/dist/plugins/gallery/icons.d.ts +13 -0
- package/dist/plugins/gallery/icons.js +45 -0
- package/dist/plugins/gallery/index.d.ts +126 -0
- package/dist/plugins/gallery/index.js +355 -0
- package/dist/plugins/gallery/layout.d.ts +35 -0
- package/dist/plugins/gallery/layout.js +79 -0
- package/dist/plugins/gallery/locale/en.d.ts +36 -0
- package/dist/plugins/gallery/locale/en.js +35 -0
- package/dist/plugins/gallery/locale/keys.d.ts +5 -0
- package/dist/plugins/gallery/locale/ru.d.ts +36 -0
- package/dist/plugins/gallery/locale/ru.js +35 -0
- package/dist/plugins/gallery/settings.d.ts +25 -0
- package/dist/plugins/gallery/settings.js +199 -0
- package/dist/plugins/gallery/slot.d.ts +73 -0
- package/dist/plugins/gallery/slot.js +260 -0
- package/dist/plugins/gallery/state.d.ts +67 -0
- package/dist/plugins/gallery/state.js +133 -0
- package/dist/plugins/gallery/styles.d.ts +10 -0
- package/dist/plugins/gallery/styles.js +24 -0
- package/dist/plugins/gallery/uploader.d.ts +31 -0
- package/dist/plugins/gallery/uploader.js +111 -0
- package/dist/plugins/gallery/view-empty.d.ts +41 -0
- package/dist/plugins/gallery/view-empty.js +56 -0
- package/dist/plugins/gallery/view-filled.d.ts +55 -0
- package/dist/plugins/gallery/view-filled.js +353 -0
- package/dist/plugins/heading/HeadingLevelSelect.d.ts +16 -0
- package/dist/plugins/heading/HeadingLevelSelect.js +183 -0
- package/dist/plugins/heading/README.md +58 -0
- package/dist/plugins/heading/README.ru.md +59 -0
- package/dist/plugins/heading/heading.css +76 -0
- package/dist/plugins/heading/index.d.ts +124 -0
- package/dist/plugins/heading/index.js +273 -0
- package/dist/plugins/heading/locale/en.d.ts +11 -0
- package/dist/plugins/heading/locale/en.js +10 -0
- package/dist/plugins/heading/locale/keys.d.ts +5 -0
- package/dist/plugins/heading/locale/ru.d.ts +11 -0
- package/dist/plugins/heading/locale/ru.js +10 -0
- package/dist/plugins/heading/mapTextFields.d.ts +1 -0
- package/dist/plugins/heading/mapTextFields.js +1 -0
- package/dist/plugins/image/README.md +93 -0
- package/dist/plugins/image/README.ru.md +94 -0
- package/dist/plugins/image/css.d.ts +47 -0
- package/dist/plugins/image/css.js +47 -0
- package/dist/plugins/image/icons.d.ts +13 -0
- package/dist/plugins/image/icons.js +26 -0
- package/dist/plugins/image/image.css +559 -0
- package/dist/plugins/image/index.d.ts +129 -0
- package/dist/plugins/image/index.js +346 -0
- package/dist/plugins/image/locale/en.d.ts +39 -0
- package/dist/plugins/image/locale/en.js +38 -0
- package/dist/plugins/image/locale/keys.d.ts +5 -0
- package/dist/plugins/image/locale/ru.d.ts +39 -0
- package/dist/plugins/image/locale/ru.js +38 -0
- package/dist/plugins/image/settings.d.ts +23 -0
- package/dist/plugins/image/settings.js +347 -0
- package/dist/plugins/image/state.d.ts +86 -0
- package/dist/plugins/image/state.js +151 -0
- package/dist/plugins/image/styles.d.ts +21 -0
- package/dist/plugins/image/styles.js +58 -0
- package/dist/plugins/image/uploader.d.ts +31 -0
- package/dist/plugins/image/uploader.js +92 -0
- package/dist/plugins/image/view-empty.d.ts +44 -0
- package/dist/plugins/image/view-empty.js +61 -0
- package/dist/plugins/image/view-filled.d.ts +49 -0
- package/dist/plugins/image/view-filled.js +282 -0
- package/dist/plugins/index.d.ts +21 -0
- package/dist/plugins/index.js +22 -0
- package/dist/plugins/link-preview/README.md +69 -0
- package/dist/plugins/link-preview/README.ru.md +70 -0
- package/dist/plugins/link-preview/index.d.ts +169 -0
- package/dist/plugins/link-preview/index.js +698 -0
- package/dist/plugins/link-preview/link-preview.css +254 -0
- package/dist/plugins/link-preview/locale/en.d.ts +14 -0
- package/dist/plugins/link-preview/locale/en.js +13 -0
- package/dist/plugins/link-preview/locale/keys.d.ts +5 -0
- package/dist/plugins/link-preview/locale/ru.d.ts +14 -0
- package/dist/plugins/link-preview/locale/ru.js +13 -0
- package/dist/plugins/list/README.md +59 -0
- package/dist/plugins/list/README.ru.md +60 -0
- package/dist/plugins/list/index.d.ts +134 -0
- package/dist/plugins/list/index.js +566 -0
- package/dist/plugins/list/list.css +57 -0
- package/dist/plugins/list/locale/en.d.ts +5 -0
- package/dist/plugins/list/locale/en.js +4 -0
- package/dist/plugins/list/locale/keys.d.ts +5 -0
- package/dist/plugins/list/locale/ru.d.ts +5 -0
- package/dist/plugins/list/locale/ru.js +4 -0
- package/dist/plugins/list/mapTextFields.d.ts +1 -0
- package/dist/plugins/list/mapTextFields.js +1 -0
- package/dist/plugins/paragraph/README.md +57 -0
- package/dist/plugins/paragraph/README.ru.md +58 -0
- package/dist/plugins/paragraph/index.d.ts +112 -0
- package/dist/plugins/paragraph/index.js +158 -0
- package/dist/plugins/paragraph/locale/en.d.ts +5 -0
- package/dist/plugins/paragraph/locale/en.js +4 -0
- package/dist/plugins/paragraph/locale/keys.d.ts +5 -0
- package/dist/plugins/paragraph/locale/ru.d.ts +5 -0
- package/dist/plugins/paragraph/locale/ru.js +4 -0
- package/dist/plugins/paragraph/mapTextFields.d.ts +1 -0
- package/dist/plugins/paragraph/mapTextFields.js +1 -0
- package/dist/plugins/paragraph/paragraph.css +56 -0
- package/dist/plugins/person/README.md +72 -0
- package/dist/plugins/person/README.ru.md +73 -0
- package/dist/plugins/person/index.d.ts +189 -0
- package/dist/plugins/person/index.js +777 -0
- package/dist/plugins/person/locale/en.d.ts +18 -0
- package/dist/plugins/person/locale/en.js +17 -0
- package/dist/plugins/person/locale/keys.d.ts +5 -0
- package/dist/plugins/person/locale/ru.d.ts +18 -0
- package/dist/plugins/person/locale/ru.js +17 -0
- package/dist/plugins/person/person.css +431 -0
- package/dist/plugins/person/socialResolver.d.ts +18 -0
- package/dist/plugins/person/socialResolver.js +69 -0
- package/dist/plugins/poll/README.md +96 -0
- package/dist/plugins/poll/README.ru.md +97 -0
- package/dist/plugins/poll/index.d.ts +134 -0
- package/dist/plugins/poll/index.js +825 -0
- package/dist/plugins/poll/locale/en.d.ts +23 -0
- package/dist/plugins/poll/locale/en.js +22 -0
- package/dist/plugins/poll/locale/keys.d.ts +5 -0
- package/dist/plugins/poll/locale/ru.d.ts +23 -0
- package/dist/plugins/poll/locale/ru.js +22 -0
- package/dist/plugins/poll/poll.css +400 -0
- package/dist/plugins/quote/README.md +55 -0
- package/dist/plugins/quote/README.ru.md +56 -0
- package/dist/plugins/quote/index.d.ts +81 -0
- package/dist/plugins/quote/index.js +165 -0
- package/dist/plugins/quote/locale/en.d.ts +6 -0
- package/dist/plugins/quote/locale/en.js +5 -0
- package/dist/plugins/quote/locale/keys.d.ts +5 -0
- package/dist/plugins/quote/locale/ru.d.ts +6 -0
- package/dist/plugins/quote/locale/ru.js +5 -0
- package/dist/plugins/quote/mapTextFields.d.ts +1 -0
- package/dist/plugins/quote/mapTextFields.js +1 -0
- package/dist/plugins/quote/quote.css +72 -0
- package/dist/plugins/raw/README.md +54 -0
- package/dist/plugins/raw/README.ru.md +55 -0
- package/dist/plugins/raw/index.d.ts +62 -0
- package/dist/plugins/raw/index.js +227 -0
- package/dist/plugins/raw/locale/en.d.ts +7 -0
- package/dist/plugins/raw/locale/en.js +6 -0
- package/dist/plugins/raw/locale/keys.d.ts +5 -0
- package/dist/plugins/raw/locale/ru.d.ts +7 -0
- package/dist/plugins/raw/locale/ru.js +6 -0
- package/dist/plugins/raw/raw.css +127 -0
- package/dist/plugins/shared/actionBar.d.ts +19 -0
- package/dist/plugins/shared/actionBar.js +35 -0
- package/dist/plugins/shared/dropzone.d.ts +106 -0
- package/dist/plugins/shared/dropzone.js +107 -0
- package/dist/plugins/shared/fileInput.d.ts +44 -0
- package/dist/plugins/shared/fileInput.js +62 -0
- package/dist/plugins/spoiler/README.md +55 -0
- package/dist/plugins/spoiler/README.ru.md +56 -0
- package/dist/plugins/spoiler/index.d.ts +61 -0
- package/dist/plugins/spoiler/index.js +169 -0
- package/dist/plugins/spoiler/locale/en.d.ts +7 -0
- package/dist/plugins/spoiler/locale/en.js +6 -0
- package/dist/plugins/spoiler/locale/keys.d.ts +5 -0
- package/dist/plugins/spoiler/locale/ru.d.ts +7 -0
- package/dist/plugins/spoiler/locale/ru.js +6 -0
- package/dist/plugins/spoiler/spoiler.css +101 -0
- package/dist/plugins/table/README.md +55 -0
- package/dist/plugins/table/README.ru.md +56 -0
- package/dist/plugins/table/index.d.ts +85 -0
- package/dist/plugins/table/index.js +399 -0
- package/dist/plugins/table/locale/en.d.ts +9 -0
- package/dist/plugins/table/locale/en.js +8 -0
- package/dist/plugins/table/locale/keys.d.ts +5 -0
- package/dist/plugins/table/locale/ru.d.ts +9 -0
- package/dist/plugins/table/locale/ru.js +8 -0
- package/dist/plugins/table/table.css +58 -0
- package/dist/plugins/toggle/README.md +56 -0
- package/dist/plugins/toggle/README.ru.md +57 -0
- package/dist/plugins/toggle/index.d.ts +69 -0
- package/dist/plugins/toggle/index.js +191 -0
- package/dist/plugins/toggle/locale/en.d.ts +7 -0
- package/dist/plugins/toggle/locale/en.js +6 -0
- package/dist/plugins/toggle/locale/keys.d.ts +5 -0
- package/dist/plugins/toggle/locale/ru.d.ts +7 -0
- package/dist/plugins/toggle/locale/ru.js +6 -0
- package/dist/plugins/toggle/toggle.css +101 -0
- package/dist/plugins/warning/README.md +55 -0
- package/dist/plugins/warning/README.ru.md +56 -0
- package/dist/plugins/warning/index.d.ts +66 -0
- package/dist/plugins/warning/index.js +167 -0
- package/dist/plugins/warning/locale/en.d.ts +6 -0
- package/dist/plugins/warning/locale/en.js +5 -0
- package/dist/plugins/warning/locale/keys.d.ts +5 -0
- package/dist/plugins/warning/locale/ru.d.ts +6 -0
- package/dist/plugins/warning/locale/ru.js +5 -0
- package/dist/plugins/warning/warning.css +63 -0
- package/dist/renderer/async.d.ts +1 -0
- package/dist/renderer/async.js +7 -0
- package/dist/renderer/errors.d.ts +27 -0
- package/dist/renderer/errors.js +48 -0
- package/dist/renderer/index.d.ts +82 -0
- package/dist/renderer/index.js +413 -0
- package/dist/renderer/inline.d.ts +5 -0
- package/dist/renderer/inline.js +61 -0
- package/dist/renderer/locale/en.d.ts +35 -0
- package/dist/renderer/locale/en.js +29 -0
- package/dist/renderer/locale/ru.d.ts +37 -0
- package/dist/renderer/locale/ru.js +29 -0
- package/dist/renderer/renderers/README.md +7 -0
- package/dist/renderer/renderers/README.ru.md +5 -0
- package/dist/renderer/renderers/async.d.ts +51 -0
- package/dist/renderer/renderers/async.js +99 -0
- package/dist/renderer/renderers/attaches/README.md +48 -0
- package/dist/renderer/renderers/attaches/README.ru.md +48 -0
- package/dist/renderer/renderers/attaches/index.d.ts +37 -0
- package/dist/renderer/renderers/attaches/index.js +451 -0
- package/dist/renderer/renderers/attaches/locale/en.d.ts +11 -0
- package/dist/renderer/renderers/attaches/locale/en.js +7 -0
- package/dist/renderer/renderers/attaches/locale/ru.d.ts +13 -0
- package/dist/renderer/renderers/attaches/locale/ru.js +7 -0
- package/dist/renderer/renderers/attaches/styles.css +196 -0
- package/dist/renderer/renderers/carousel/README.md +43 -0
- package/dist/renderer/renderers/carousel/README.ru.md +43 -0
- package/dist/renderer/renderers/carousel/index.d.ts +7 -0
- package/dist/renderer/renderers/carousel/index.js +110 -0
- package/dist/renderer/renderers/carousel/styles.css +7 -0
- package/dist/renderer/renderers/checklist/README.md +33 -0
- package/dist/renderer/renderers/checklist/README.ru.md +33 -0
- package/dist/renderer/renderers/checklist/index.d.ts +7 -0
- package/dist/renderer/renderers/checklist/index.js +55 -0
- package/dist/renderer/renderers/checklist/styles.css +54 -0
- package/dist/renderer/renderers/code/README.md +33 -0
- package/dist/renderer/renderers/code/README.ru.md +33 -0
- package/dist/renderer/renderers/code/index.d.ts +9 -0
- package/dist/renderer/renderers/code/index.js +137 -0
- package/dist/renderer/renderers/code/locale/en.d.ts +5 -0
- package/dist/renderer/renderers/code/locale/en.js +4 -0
- package/dist/renderer/renderers/code/locale/ru.d.ts +5 -0
- package/dist/renderer/renderers/code/locale/ru.js +4 -0
- package/dist/renderer/renderers/code/styles.css +330 -0
- package/dist/renderer/renderers/columns/README.md +33 -0
- package/dist/renderer/renderers/columns/README.ru.md +33 -0
- package/dist/renderer/renderers/columns/index.d.ts +7 -0
- package/dist/renderer/renderers/columns/index.js +53 -0
- package/dist/renderer/renderers/columns/styles.css +5 -0
- package/dist/renderer/renderers/delimiter/README.md +33 -0
- package/dist/renderer/renderers/delimiter/README.ru.md +33 -0
- package/dist/renderer/renderers/delimiter/index.d.ts +7 -0
- package/dist/renderer/renderers/delimiter/index.js +28 -0
- package/dist/renderer/renderers/delimiter/styles.css +12 -0
- package/dist/renderer/renderers/embed/README.md +35 -0
- package/dist/renderer/renderers/embed/README.ru.md +35 -0
- package/dist/renderer/renderers/embed/index.d.ts +8 -0
- package/dist/renderer/renderers/embed/index.js +66 -0
- package/dist/renderer/renderers/embed/styles.css +217 -0
- package/dist/renderer/renderers/gallery/README.md +41 -0
- package/dist/renderer/renderers/gallery/README.ru.md +41 -0
- package/dist/renderer/renderers/gallery/index.d.ts +8 -0
- package/dist/renderer/renderers/gallery/index.js +329 -0
- package/dist/renderer/renderers/gallery/styles.css +419 -0
- package/dist/renderer/renderers/heading/README.md +33 -0
- package/dist/renderer/renderers/heading/README.ru.md +33 -0
- package/dist/renderer/renderers/heading/index.d.ts +8 -0
- package/dist/renderer/renderers/heading/index.js +38 -0
- package/dist/renderer/renderers/heading/styles.css +26 -0
- package/dist/renderer/renderers/image/README.md +42 -0
- package/dist/renderer/renderers/image/README.ru.md +42 -0
- package/dist/renderer/renderers/image/index.d.ts +7 -0
- package/dist/renderer/renderers/image/index.js +93 -0
- package/dist/renderer/renderers/image/styles.css +40 -0
- package/dist/renderer/renderers/index.d.ts +47 -0
- package/dist/renderer/renderers/index.js +125 -0
- package/dist/renderer/renderers/link-preview/README.md +43 -0
- package/dist/renderer/renderers/link-preview/README.ru.md +43 -0
- package/dist/renderer/renderers/link-preview/index.d.ts +9 -0
- package/dist/renderer/renderers/link-preview/index.js +118 -0
- package/dist/renderer/renderers/link-preview/styles.css +246 -0
- package/dist/renderer/renderers/list/README.md +33 -0
- package/dist/renderer/renderers/list/README.ru.md +33 -0
- package/dist/renderer/renderers/list/index.d.ts +8 -0
- package/dist/renderer/renderers/list/index.js +42 -0
- package/dist/renderer/renderers/list/styles.css +20 -0
- package/dist/renderer/renderers/paragraph/README.md +33 -0
- package/dist/renderer/renderers/paragraph/README.ru.md +33 -0
- package/dist/renderer/renderers/paragraph/index.d.ts +7 -0
- package/dist/renderer/renderers/paragraph/index.js +35 -0
- package/dist/renderer/renderers/paragraph/styles.css +3 -0
- package/dist/renderer/renderers/person/README.md +44 -0
- package/dist/renderer/renderers/person/README.ru.md +44 -0
- package/dist/renderer/renderers/person/index.d.ts +7 -0
- package/dist/renderer/renderers/person/index.js +213 -0
- package/dist/renderer/renderers/person/locale/en.d.ts +6 -0
- package/dist/renderer/renderers/person/locale/en.js +5 -0
- package/dist/renderer/renderers/person/locale/ru.d.ts +6 -0
- package/dist/renderer/renderers/person/locale/ru.js +5 -0
- package/dist/renderer/renderers/person/styles.css +47 -0
- package/dist/renderer/renderers/poll/README.md +68 -0
- package/dist/renderer/renderers/poll/README.ru.md +68 -0
- package/dist/renderer/renderers/poll/index.d.ts +9 -0
- package/dist/renderer/renderers/poll/index.js +339 -0
- package/dist/renderer/renderers/poll/styles.css +148 -0
- package/dist/renderer/renderers/quote/README.md +33 -0
- package/dist/renderer/renderers/quote/README.ru.md +33 -0
- package/dist/renderer/renderers/quote/index.d.ts +7 -0
- package/dist/renderer/renderers/quote/index.js +59 -0
- package/dist/renderer/renderers/quote/styles.css +43 -0
- package/dist/renderer/renderers/raw/README.md +33 -0
- package/dist/renderer/renderers/raw/README.ru.md +33 -0
- package/dist/renderer/renderers/raw/index.d.ts +7 -0
- package/dist/renderer/renderers/raw/index.js +27 -0
- package/dist/renderer/renderers/spoiler/README.md +33 -0
- package/dist/renderer/renderers/spoiler/README.ru.md +33 -0
- package/dist/renderer/renderers/spoiler/index.d.ts +9 -0
- package/dist/renderer/renderers/spoiler/index.js +84 -0
- package/dist/renderer/renderers/spoiler/locale/en.d.ts +4 -0
- package/dist/renderer/renderers/spoiler/locale/en.js +3 -0
- package/dist/renderer/renderers/spoiler/locale/ru.d.ts +4 -0
- package/dist/renderer/renderers/spoiler/locale/ru.js +3 -0
- package/dist/renderer/renderers/spoiler/styles.css +61 -0
- package/dist/renderer/renderers/table/README.md +33 -0
- package/dist/renderer/renderers/table/README.ru.md +33 -0
- package/dist/renderer/renderers/table/index.d.ts +7 -0
- package/dist/renderer/renderers/table/index.js +83 -0
- package/dist/renderer/renderers/table/styles.css +34 -0
- package/dist/renderer/renderers/toggle/README.md +33 -0
- package/dist/renderer/renderers/toggle/README.ru.md +33 -0
- package/dist/renderer/renderers/toggle/index.d.ts +7 -0
- package/dist/renderer/renderers/toggle/index.js +59 -0
- package/dist/renderer/renderers/toggle/styles.css +49 -0
- package/dist/renderer/renderers/warning/README.md +33 -0
- package/dist/renderer/renderers/warning/README.ru.md +33 -0
- package/dist/renderer/renderers/warning/index.d.ts +7 -0
- package/dist/renderer/renderers/warning/index.js +62 -0
- package/dist/renderer/renderers/warning/styles.css +41 -0
- package/dist/renderer/styles/base.css +168 -0
- package/dist/renderer/types.d.ts +464 -0
- package/dist/shared/blockDataNormalizers.d.ts +14 -0
- package/dist/shared/blockDataNormalizers.js +235 -0
- package/dist/shared/blockDataValidators.d.ts +81 -0
- package/dist/shared/blockDataValidators.js +313 -0
- package/dist/shared/blockTypes.d.ts +2 -0
- package/dist/shared/blockTypes.js +26 -0
- package/dist/shared/carouselData.d.ts +34 -0
- package/dist/shared/carouselData.js +111 -0
- package/dist/shared/cloneEditorData.d.ts +11 -0
- package/dist/shared/cloneEditorData.js +20 -0
- package/dist/shared/documentTypes.d.ts +35 -0
- package/dist/shared/embedPlayer.d.ts +42 -0
- package/dist/shared/embedPlayer.js +141 -0
- package/dist/shared/fileUtils.d.ts +33 -0
- package/dist/shared/fileUtils.js +109 -0
- package/dist/shared/highlightRuntime.d.ts +38 -0
- package/dist/shared/highlightRuntime.js +75 -0
- package/dist/shared/inlineMarshal.d.ts +82 -0
- package/dist/shared/inlineMarshal.js +215 -0
- package/dist/shared/jsonData.d.ts +11 -0
- package/dist/shared/jsonData.js +53 -0
- package/dist/shared/localeTypes.d.ts +11 -0
- package/dist/shared/mapTextFields.d.ts +89 -0
- package/dist/shared/mapTextFields.js +119 -0
- package/dist/shared/pollData.d.ts +90 -0
- package/dist/shared/pollData.js +231 -0
- package/dist/shared/resolvePath.d.ts +8 -0
- package/dist/shared/resolvePath.js +10 -0
- package/dist/shared/runtime/dompurify.js +2424 -0
- package/dist/shared/runtime/highlightBundle.js +10 -0
- package/dist/shared/runtime/jszip.js +13 -0
- package/dist/shared/sanitize/allowlist.d.ts +21 -0
- package/dist/shared/sanitize/allowlist.js +40 -0
- package/dist/shared/sanitize/escapeHtml.d.ts +6 -0
- package/dist/shared/sanitize/escapeHtml.js +11 -0
- package/dist/shared/sanitize/index.d.ts +8 -0
- package/dist/shared/sanitize/index.js +8 -0
- package/dist/shared/sanitize/parseInline.d.ts +11 -0
- package/dist/shared/sanitize/parseInline.js +25 -0
- package/dist/shared/sanitize/sanitizeHtml.d.ts +8 -0
- package/dist/shared/sanitize/sanitizeHtml.js +19 -0
- package/dist/shared/sanitize/sanitizeRawHtml.d.ts +15 -0
- package/dist/shared/sanitize/sanitizeRawHtml.js +162 -0
- package/dist/shared/sanitize/sanitizeStyle.d.ts +9 -0
- package/dist/shared/sanitize/sanitizeStyle.js +39 -0
- package/dist/shared/sanitize/sanitizeUrl.d.ts +28 -0
- package/dist/shared/sanitize/sanitizeUrl.js +86 -0
- package/dist/shared/sanitize/walker.d.ts +15 -0
- package/dist/shared/sanitize/walker.js +98 -0
- package/dist/shared/styleRegistry.d.ts +4 -0
- package/dist/shared/styleRegistry.js +38 -0
- package/dist/shared/textFormat.d.ts +32 -0
- package/dist/shared/textFormat.js +48 -0
- package/dist/shared/zipRuntime.d.ts +19 -0
- package/dist/shared/zipRuntime.js +54 -0
- package/dist/styles/editor.css +3 -0
- package/dist/types.d.ts +1 -0
- package/package.json +149 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { EditorEvent } from '../editorEvents.js'
|
|
2
|
+
|
|
3
|
+
/** @param {import('../types').IBlock} block */
|
|
4
|
+
function editableElement(block) {
|
|
5
|
+
const root = block.contentElement
|
|
6
|
+
if (root.matches('[contenteditable="true"]')) return root
|
|
7
|
+
return /** @type {HTMLElement | null} */ (root.querySelector('[contenteditable="true"]'))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Operations on cross-block selections (text spanning multiple blocks).
|
|
12
|
+
*
|
|
13
|
+
* Cross-block selection is held in `ICrossBlockSelection` (a custom range
|
|
14
|
+
* since the native browser API can't span editing hosts). This class
|
|
15
|
+
* encapsulates the multi-step delete sequence:
|
|
16
|
+
*
|
|
17
|
+
* 1. Delete tail of last block (from rangeEnd → end of CE).
|
|
18
|
+
* 2. Remove all middle blocks.
|
|
19
|
+
* 3. Delete head of first block (from start of CE → rangeStart).
|
|
20
|
+
* 4. Merge first + last (first block keeps the merged content).
|
|
21
|
+
* 5. Restore caret at the merge boundary.
|
|
22
|
+
*
|
|
23
|
+
* Wrapped in an UNDO_BATCH so the whole operation is one undo step.
|
|
24
|
+
*/
|
|
25
|
+
export class CrossBlockEditor {
|
|
26
|
+
/** @type {HTMLElement} */
|
|
27
|
+
#rootEl
|
|
28
|
+
|
|
29
|
+
/** @type {import('../types').IBlockManager} */
|
|
30
|
+
#blocks
|
|
31
|
+
|
|
32
|
+
/** @type {import('../types').ISelectionManager} */
|
|
33
|
+
#selection
|
|
34
|
+
|
|
35
|
+
/** @type {import('../types').ICrossBlockSelection} */
|
|
36
|
+
#crossBlockSelection
|
|
37
|
+
|
|
38
|
+
/** @type {import('../types').IEventBus} */
|
|
39
|
+
#events
|
|
40
|
+
|
|
41
|
+
/** @type {string} */
|
|
42
|
+
#defaultBlockType
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {HTMLElement} rootEl
|
|
46
|
+
* @param {import('../types').IBlockManager} blocks
|
|
47
|
+
* @param {import('../types').ISelectionManager} selection
|
|
48
|
+
* @param {import('../types').ICrossBlockSelection} crossBlockSelection
|
|
49
|
+
* @param {import('../types').IEventBus} events
|
|
50
|
+
* @param {string} defaultBlockType
|
|
51
|
+
*/
|
|
52
|
+
constructor(rootEl, blocks, selection, crossBlockSelection, events, defaultBlockType) {
|
|
53
|
+
this.#rootEl = rootEl
|
|
54
|
+
this.#blocks = blocks
|
|
55
|
+
this.#selection = selection
|
|
56
|
+
this.#crossBlockSelection = crossBlockSelection
|
|
57
|
+
this.#events = events
|
|
58
|
+
this.#defaultBlockType = defaultBlockType
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Place the native caret at the end of a cross-block range.
|
|
63
|
+
* Required so UndoManager captures the correct caret position when
|
|
64
|
+
* the next snapshot is taken.
|
|
65
|
+
*
|
|
66
|
+
* @param {Range} range
|
|
67
|
+
*/
|
|
68
|
+
setCaretToRangeEnd(range) {
|
|
69
|
+
const endNode = range.endContainer
|
|
70
|
+
const endOffset = range.endOffset
|
|
71
|
+
|
|
72
|
+
const endBlock = this.#blocks.getBlockByChildNode(endNode)
|
|
73
|
+
if (!endBlock) return
|
|
74
|
+
|
|
75
|
+
const idx = this.#blocks.getBlockIndex(endBlock.id)
|
|
76
|
+
if (idx >= 0) this.#blocks.setCurrentIndex(idx)
|
|
77
|
+
|
|
78
|
+
endBlock.focus()
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const sel = window.getSelection()
|
|
82
|
+
const r = document.createRange()
|
|
83
|
+
r.setStart(endNode, endOffset)
|
|
84
|
+
r.collapse(true)
|
|
85
|
+
sel?.removeAllRanges()
|
|
86
|
+
sel?.addRange(r)
|
|
87
|
+
} catch {
|
|
88
|
+
// Range may be detached after a focus shift; ignore.
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Delete the content covered by a cross-block range and merge the
|
|
94
|
+
* surviving head + tail into the first block.
|
|
95
|
+
*
|
|
96
|
+
* @param {Range} crossRange
|
|
97
|
+
* @param {(...blocks: import('../types').IBlock[]) => void} notifyChanged
|
|
98
|
+
*/
|
|
99
|
+
deleteContent(crossRange, notifyChanged) {
|
|
100
|
+
const blocks = this.#blocks
|
|
101
|
+
|
|
102
|
+
const firstBlock = blocks.getBlockByChildNode(crossRange.startContainer)
|
|
103
|
+
const lastBlock = blocks.getBlockByChildNode(crossRange.endContainer)
|
|
104
|
+
if (!firstBlock || !lastBlock || firstBlock === lastBlock) return
|
|
105
|
+
|
|
106
|
+
const firstIndex = blocks.getBlockIndex(firstBlock.id)
|
|
107
|
+
const lastIndex = blocks.getBlockIndex(lastBlock.id)
|
|
108
|
+
if (firstIndex < 0 || lastIndex <= firstIndex) return
|
|
109
|
+
|
|
110
|
+
const rangeStart = { node: crossRange.startContainer, offset: crossRange.startOffset }
|
|
111
|
+
const rangeEnd = { node: crossRange.endContainer, offset: crossRange.endOffset }
|
|
112
|
+
|
|
113
|
+
const firstCe = editableElement(firstBlock)
|
|
114
|
+
const lastCe = editableElement(lastBlock)
|
|
115
|
+
|
|
116
|
+
// The cross-block algorithm requires two editable endpoints. Validate
|
|
117
|
+
// before opening a history batch so malformed/detached ranges cannot
|
|
118
|
+
// leave UndoManager permanently batching.
|
|
119
|
+
if (!firstCe || !lastCe) return
|
|
120
|
+
|
|
121
|
+
this.#events.emit(EditorEvent.UNDO_BATCH_START)
|
|
122
|
+
try {
|
|
123
|
+
// 1. Delete tail of last block.
|
|
124
|
+
if (lastCe) {
|
|
125
|
+
try {
|
|
126
|
+
const delRange = document.createRange()
|
|
127
|
+
delRange.selectNodeContents(lastCe)
|
|
128
|
+
delRange.setEnd(rangeEnd.node, rangeEnd.offset)
|
|
129
|
+
delRange.deleteContents()
|
|
130
|
+
lastCe.normalize()
|
|
131
|
+
} catch {
|
|
132
|
+
// Range may have been invalidated mid-operation; ignore.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 2. Remove middle blocks (skip first and last).
|
|
137
|
+
for (let index = lastIndex - 1; index > firstIndex; index--) {
|
|
138
|
+
blocks.remove(index)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 3. Delete head of first block.
|
|
142
|
+
if (firstCe) {
|
|
143
|
+
try {
|
|
144
|
+
const delRange = document.createRange()
|
|
145
|
+
delRange.selectNodeContents(firstCe)
|
|
146
|
+
delRange.setStart(rangeStart.node, rangeStart.offset)
|
|
147
|
+
delRange.deleteContents()
|
|
148
|
+
firstCe.normalize()
|
|
149
|
+
} catch {
|
|
150
|
+
// ignore
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Caret target = end of remaining text in firstCe (the merge boundary).
|
|
155
|
+
const caretOffset = (firstCe?.textContent || '').length
|
|
156
|
+
|
|
157
|
+
// 4. Merge first + last.
|
|
158
|
+
if (lastCe !== firstCe) {
|
|
159
|
+
// Preserve boundary whitespace exactly. Trimming here joins words
|
|
160
|
+
// when a range ends immediately before a leading space in the tail.
|
|
161
|
+
const remaining = lastCe.innerHTML
|
|
162
|
+
if (remaining) {
|
|
163
|
+
const tpl = document.createElement('template')
|
|
164
|
+
tpl.innerHTML = remaining
|
|
165
|
+
firstCe.append(...tpl.content.childNodes)
|
|
166
|
+
}
|
|
167
|
+
const index = blocks.getBlockIndex(lastBlock.id)
|
|
168
|
+
if (index >= 0) blocks.remove(index)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
blocks.clearSelection()
|
|
172
|
+
this.#crossBlockSelection.deactivate(this.#rootEl)
|
|
173
|
+
|
|
174
|
+
if (blocks.getBlockCount() === 0) blocks.insert(this.#defaultBlockType)
|
|
175
|
+
|
|
176
|
+
// 5. Restore caret at the merge boundary.
|
|
177
|
+
const focusIdx = blocks.getBlockIndex(firstBlock.id)
|
|
178
|
+
if (focusIdx >= 0) {
|
|
179
|
+
blocks.setCurrentIndex(focusIdx)
|
|
180
|
+
const block = blocks.getBlockByIndex(focusIdx)
|
|
181
|
+
if (block) {
|
|
182
|
+
block.focus()
|
|
183
|
+
if (caretOffset > 0) {
|
|
184
|
+
this.#selection.setCaretToOffset(block.id, caretOffset)
|
|
185
|
+
} else {
|
|
186
|
+
this.#selection.setCaretToBlock(block.id, 'start')
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Mark the surviving block dirty and notify while UndoManager is still
|
|
192
|
+
// batching, so the final snapshot observes the DOM mutation.
|
|
193
|
+
notifyChanged(firstBlock)
|
|
194
|
+
} finally {
|
|
195
|
+
this.#events.emit(EditorEvent.UNDO_BATCH_END)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing table built from each plugin's `pasteConfig`.
|
|
3
|
+
*
|
|
4
|
+
* Three independent maps:
|
|
5
|
+
* - tag → plugin (for HTML element matching)
|
|
6
|
+
* - file MIME pattern → plugin (for `e.clipboardData.files`)
|
|
7
|
+
* - text regex → plugin (for plain-text URL/pattern matching)
|
|
8
|
+
*
|
|
9
|
+
* Built once at construction time, then queried by Clipboard during paste.
|
|
10
|
+
*/
|
|
11
|
+
export class PasteRouter {
|
|
12
|
+
/** @param {Iterable<import('../types.js').BlockPlugin>} plugins */
|
|
13
|
+
constructor(plugins: Iterable<import("../types.js").BlockPlugin>);
|
|
14
|
+
/** @param {string} tag @returns {import('../types.js').BlockPlugin | undefined} */
|
|
15
|
+
findByTag(tag: string): import("../types.js").BlockPlugin | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} mime
|
|
18
|
+
* @returns {import('../types.js').BlockPlugin | undefined}
|
|
19
|
+
*/
|
|
20
|
+
findByFile(mime: string): import("../types.js").BlockPlugin | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} text
|
|
23
|
+
* @returns {import('../types.js').BlockPlugin | undefined}
|
|
24
|
+
*/
|
|
25
|
+
findByPattern(text: string): import("../types.js").BlockPlugin | undefined;
|
|
26
|
+
#private;
|
|
27
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing table built from each plugin's `pasteConfig`.
|
|
3
|
+
*
|
|
4
|
+
* Three independent maps:
|
|
5
|
+
* - tag → plugin (for HTML element matching)
|
|
6
|
+
* - file MIME pattern → plugin (for `e.clipboardData.files`)
|
|
7
|
+
* - text regex → plugin (for plain-text URL/pattern matching)
|
|
8
|
+
*
|
|
9
|
+
* Built once at construction time, then queried by Clipboard during paste.
|
|
10
|
+
*/
|
|
11
|
+
export class PasteRouter {
|
|
12
|
+
/** @type {Map<string, import('../types').BlockPlugin>} */
|
|
13
|
+
#tagRoutes = new Map()
|
|
14
|
+
|
|
15
|
+
/** @type {Array<{ regex: RegExp, plugin: import('../types').BlockPlugin }>} */
|
|
16
|
+
#fileRoutes = []
|
|
17
|
+
|
|
18
|
+
/** @type {Array<{ regex: RegExp, plugin: import('../types').BlockPlugin }>} */
|
|
19
|
+
#patternRoutes = []
|
|
20
|
+
|
|
21
|
+
/** @param {Iterable<import('../types').BlockPlugin>} plugins */
|
|
22
|
+
constructor(plugins) {
|
|
23
|
+
for (const plugin of plugins) {
|
|
24
|
+
const config = plugin.pasteConfig
|
|
25
|
+
if (!config) continue
|
|
26
|
+
|
|
27
|
+
if (config.tags) {
|
|
28
|
+
for (const tag of config.tags) {
|
|
29
|
+
this.#tagRoutes.set(tag.toLowerCase(), plugin)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (config.files) {
|
|
34
|
+
for (const mime of config.files) {
|
|
35
|
+
// Convert "image/*" → /^image\/.*$/i
|
|
36
|
+
const regex = new RegExp(
|
|
37
|
+
'^' + mime.replace(/[.+]/g, '\\$&').replace(/\*/g, '.*') + '$',
|
|
38
|
+
'i',
|
|
39
|
+
)
|
|
40
|
+
this.#fileRoutes.push({ regex, plugin })
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (config.patterns) {
|
|
45
|
+
for (const pattern of config.patterns) {
|
|
46
|
+
this.#patternRoutes.push({ regex: pattern, plugin })
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @param {string} tag @returns {import('../types').BlockPlugin | undefined} */
|
|
53
|
+
findByTag(tag) {
|
|
54
|
+
return this.#tagRoutes.get(tag.toLowerCase())
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {string} mime
|
|
59
|
+
* @returns {import('../types').BlockPlugin | undefined}
|
|
60
|
+
*/
|
|
61
|
+
findByFile(mime) {
|
|
62
|
+
return this.#fileRoutes.find((r) => r.regex.test(mime))?.plugin
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} text
|
|
67
|
+
* @returns {import('../types').BlockPlugin | undefined}
|
|
68
|
+
*/
|
|
69
|
+
findByPattern(text) {
|
|
70
|
+
return this.#patternRoutes.find((route) => this.#matches(route.regex, text))?.plugin
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Plugin-owned expressions may be global or sticky. RegExp.test() mutates
|
|
75
|
+
* lastIndex for both flags, so every routing decision must start from the
|
|
76
|
+
* same state and leave the expression reusable by the next paste.
|
|
77
|
+
* @param {RegExp} regex
|
|
78
|
+
* @param {string} value
|
|
79
|
+
*/
|
|
80
|
+
#matches(regex, value) {
|
|
81
|
+
regex.lastIndex = 0
|
|
82
|
+
const matched = regex.test(value)
|
|
83
|
+
regex.lastIndex = 0
|
|
84
|
+
return matched
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} InsertContext
|
|
3
|
+
* @property {import('../types.js').IBlockManager} blocks
|
|
4
|
+
* @property {import('../types.js').ISelectionManager} selection
|
|
5
|
+
* @property {import('../BlockOperations.js').BlockOperations} blockOps
|
|
6
|
+
* @property {string} defaultBlockType
|
|
7
|
+
* @property {import('./PasteRouter.js').PasteRouter} router
|
|
8
|
+
* @property {(...blocks: import('../types.js').IBlock[]) => void} notifyChanged
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Insert plain text into the editor at the current caret.
|
|
12
|
+
* Multi-line text becomes one block per non-empty line.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} text
|
|
15
|
+
* @param {InsertContext} ctx
|
|
16
|
+
*/
|
|
17
|
+
export function pastePlainText(text: string, ctx: InsertContext): void;
|
|
18
|
+
/**
|
|
19
|
+
* Insert HTML into the editor at the current caret.
|
|
20
|
+
* Splits multi-element HTML into multiple blocks via `extractBlockElements`,
|
|
21
|
+
* routes known tags to plugins via `pasteConfig.tags`, and falls back to
|
|
22
|
+
* the default block type for unknown tags.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} html
|
|
25
|
+
* @param {InsertContext} ctx
|
|
26
|
+
*/
|
|
27
|
+
export function pasteHtml(html: string, ctx: InsertContext): void;
|
|
28
|
+
export type InsertContext = {
|
|
29
|
+
blocks: import("../types.js").IBlockManager;
|
|
30
|
+
selection: import("../types.js").ISelectionManager;
|
|
31
|
+
blockOps: import("../BlockOperations.js").BlockOperations;
|
|
32
|
+
defaultBlockType: string;
|
|
33
|
+
router: import("./PasteRouter.js").PasteRouter;
|
|
34
|
+
notifyChanged: (...blocks: import("../types.js").IBlock[]) => void;
|
|
35
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { sanitizeHtml } from '../sanitize.js'
|
|
2
|
+
import { extractBlockElements } from './pasteUtils.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} InsertContext
|
|
6
|
+
* @property {import('../types').IBlockManager} blocks
|
|
7
|
+
* @property {import('../types').ISelectionManager} selection
|
|
8
|
+
* @property {import('../BlockOperations').BlockOperations} blockOps
|
|
9
|
+
* @property {string} defaultBlockType
|
|
10
|
+
* @property {import('./PasteRouter.js').PasteRouter} router
|
|
11
|
+
* @property {(...blocks: import('../types').IBlock[]) => void} notifyChanged
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Insert plain text into the editor at the current caret.
|
|
16
|
+
* Multi-line text becomes one block per non-empty line.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} text
|
|
19
|
+
* @param {InsertContext} ctx
|
|
20
|
+
*/
|
|
21
|
+
export function pastePlainText(text, ctx) {
|
|
22
|
+
const lines = text.split(/\n/)
|
|
23
|
+
const nonEmpty = lines.filter((line) => line.length > 0)
|
|
24
|
+
if (nonEmpty.length === 0) return
|
|
25
|
+
// Keep the original target before a multi-block paste moves current/focus
|
|
26
|
+
// to the last inserted block. This existing block is mutated directly and
|
|
27
|
+
// must be invalidated explicitly for the pre/post-paste history snapshots.
|
|
28
|
+
const targetBlock = ctx.blocks.getCurrentBlock()
|
|
29
|
+
|
|
30
|
+
if (nonEmpty.length === 1) {
|
|
31
|
+
insertTextOrReplace(/** @type {string} */ (nonEmpty[0]), ctx.blocks)
|
|
32
|
+
if (targetBlock) ctx.notifyChanged(targetBlock)
|
|
33
|
+
else ctx.notifyChanged()
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
insertTextOrReplace(/** @type {string} */ (nonEmpty[0]), ctx.blocks)
|
|
38
|
+
|
|
39
|
+
const currentIndex = ctx.blocks.getCurrentIndex()
|
|
40
|
+
let insertIndex = currentIndex + 1
|
|
41
|
+
|
|
42
|
+
for (let i = 1; i < nonEmpty.length; i++) {
|
|
43
|
+
const escaped = /** @type {string} */ (nonEmpty[i])
|
|
44
|
+
.replace(/&/g, '&')
|
|
45
|
+
.replace(/</g, '<')
|
|
46
|
+
.replace(/>/g, '>')
|
|
47
|
+
ctx.blocks.insert(ctx.defaultBlockType, { text: escaped }, insertIndex)
|
|
48
|
+
insertIndex++
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Focus the last inserted block once (avoid intermediate focus shifts).
|
|
52
|
+
const lastBlock = ctx.blocks.getBlockByIndex(insertIndex - 1)
|
|
53
|
+
if (lastBlock) {
|
|
54
|
+
ctx.blocks.setCurrentIndex(insertIndex - 1)
|
|
55
|
+
ctx.selection.setCaretToBlock(lastBlock.id, 'end')
|
|
56
|
+
lastBlock.focus()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (targetBlock) ctx.notifyChanged(targetBlock)
|
|
60
|
+
else ctx.notifyChanged()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Insert HTML into the editor at the current caret.
|
|
65
|
+
* Splits multi-element HTML into multiple blocks via `extractBlockElements`,
|
|
66
|
+
* routes known tags to plugins via `pasteConfig.tags`, and falls back to
|
|
67
|
+
* the default block type for unknown tags.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} html
|
|
70
|
+
* @param {InsertContext} ctx
|
|
71
|
+
*/
|
|
72
|
+
export function pasteHtml(html, ctx) {
|
|
73
|
+
// Parse clipboard markup in inert template content. Individual extracted
|
|
74
|
+
// blocks are sanitized before insertion or handed to an explicit plugin
|
|
75
|
+
// paste handler; no untrusted subtree is connected to the live document.
|
|
76
|
+
const template = document.createElement('template')
|
|
77
|
+
template.innerHTML = html
|
|
78
|
+
|
|
79
|
+
const extracted = extractBlockElements(
|
|
80
|
+
template.content,
|
|
81
|
+
(tag) => !!ctx.router.findByTag(tag),
|
|
82
|
+
)
|
|
83
|
+
// Routed non-text elements (for example IMG/HR) may have no textContent
|
|
84
|
+
// and still carry meaningful paste data in their attributes.
|
|
85
|
+
.filter((b) => (b.element.textContent || '').trim() || !!ctx.router.findByTag(b.tag))
|
|
86
|
+
|
|
87
|
+
if (extracted.length === 0) return
|
|
88
|
+
|
|
89
|
+
const first = /** @type {import('./pasteUtils.js').ExtractedBlock} */ (extracted[0])
|
|
90
|
+
const targetBlock = ctx.blocks.getCurrentBlock()
|
|
91
|
+
|
|
92
|
+
// Single paragraph → inline insert into the current block.
|
|
93
|
+
if (extracted.length === 1 && first.tag === 'p') {
|
|
94
|
+
const sanitized = sanitizeHtml(first.element.innerHTML)
|
|
95
|
+
if (sanitized) insertHtmlAtCaret(sanitized)
|
|
96
|
+
if (targetBlock) ctx.notifyChanged(targetBlock)
|
|
97
|
+
else ctx.notifyChanged()
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const currentIndex = ctx.blocks.getCurrentIndex()
|
|
102
|
+
const currentBlock = ctx.blocks.getBlockByIndex(currentIndex)
|
|
103
|
+
const currentIsEmpty = currentBlock?.isEmpty()
|
|
104
|
+
|
|
105
|
+
const firstIsTextLike = first.tag === 'p' || first.tag === 'div'
|
|
106
|
+
|
|
107
|
+
if (firstIsTextLike) {
|
|
108
|
+
const sanitized = sanitizeHtml(first.element.innerHTML)
|
|
109
|
+
if (sanitized) {
|
|
110
|
+
if (currentIsEmpty && currentBlock) {
|
|
111
|
+
currentBlock.contentElement.innerHTML = sanitized
|
|
112
|
+
} else {
|
|
113
|
+
insertHtmlAtCaret(sanitized)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
insertBlockFromExtracted(first, currentIndex + 1, ctx)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let insertIndex = currentIndex + 1
|
|
121
|
+
if (!firstIsTextLike) insertIndex++
|
|
122
|
+
|
|
123
|
+
for (let i = 1; i < extracted.length; i++) {
|
|
124
|
+
const item = /** @type {import('./pasteUtils.js').ExtractedBlock} */ (extracted[i])
|
|
125
|
+
insertBlockFromExtracted(item, insertIndex, ctx)
|
|
126
|
+
insertIndex++
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (extracted.length > 1 || first.tag !== 'p') {
|
|
130
|
+
const lastIdx = insertIndex - 1
|
|
131
|
+
const lastBlock = ctx.blocks.getBlockByIndex(lastIdx)
|
|
132
|
+
if (lastBlock) {
|
|
133
|
+
ctx.blocks.setCurrentIndex(lastIdx)
|
|
134
|
+
ctx.selection.setCaretToBlock(lastBlock.id, 'end')
|
|
135
|
+
lastBlock.focus()
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (firstIsTextLike && targetBlock) ctx.notifyChanged(targetBlock)
|
|
140
|
+
else ctx.notifyChanged()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {import('./pasteUtils.js').ExtractedBlock} extracted
|
|
145
|
+
* @param {number} index
|
|
146
|
+
* @param {InsertContext} ctx
|
|
147
|
+
*/
|
|
148
|
+
function insertBlockFromExtracted(extracted, index, ctx) {
|
|
149
|
+
const plugin = ctx.router.findByTag(extracted.tag)
|
|
150
|
+
|
|
151
|
+
if (plugin?.onPaste) {
|
|
152
|
+
const data = plugin.onPaste({
|
|
153
|
+
type: 'tag',
|
|
154
|
+
element: extracted.element,
|
|
155
|
+
tag: extracted.tag,
|
|
156
|
+
})
|
|
157
|
+
if (data) {
|
|
158
|
+
ctx.blocks.insert(plugin.type, data, index)
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const sanitized = sanitizeHtml(extracted.element.innerHTML)
|
|
164
|
+
ctx.blocks.insert(ctx.defaultBlockType, { text: sanitized }, index)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Insert text at the native caret. Falls back to overwriting the current
|
|
169
|
+
* block's content if no native selection exists.
|
|
170
|
+
*
|
|
171
|
+
* @param {string} text
|
|
172
|
+
* @param {import('../types').IBlockReader} blocks
|
|
173
|
+
*/
|
|
174
|
+
function insertTextOrReplace(text, blocks) {
|
|
175
|
+
const sel = window.getSelection()
|
|
176
|
+
if (sel && sel.rangeCount > 0) {
|
|
177
|
+
insertTextAtCaret(text)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
const block = blocks.getCurrentBlock()
|
|
181
|
+
if (block) block.contentElement.textContent = text
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** @param {string} text */
|
|
185
|
+
function insertTextAtCaret(text) {
|
|
186
|
+
const sel = window.getSelection()
|
|
187
|
+
if (!sel || sel.rangeCount === 0) return
|
|
188
|
+
const range = sel.getRangeAt(0)
|
|
189
|
+
range.deleteContents()
|
|
190
|
+
const textNode = document.createTextNode(text)
|
|
191
|
+
range.insertNode(textNode)
|
|
192
|
+
range.setStartAfter(textNode)
|
|
193
|
+
range.collapse(true)
|
|
194
|
+
sel.removeAllRanges()
|
|
195
|
+
sel.addRange(range)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** @param {string} html */
|
|
199
|
+
function insertHtmlAtCaret(html) {
|
|
200
|
+
const sel = window.getSelection()
|
|
201
|
+
if (!sel || sel.rangeCount === 0) return
|
|
202
|
+
const range = sel.getRangeAt(0)
|
|
203
|
+
range.deleteContents()
|
|
204
|
+
const template = document.createElement('template')
|
|
205
|
+
template.innerHTML = html
|
|
206
|
+
const frag = template.content
|
|
207
|
+
const lastNode = frag.lastChild
|
|
208
|
+
range.insertNode(frag)
|
|
209
|
+
if (lastNode) {
|
|
210
|
+
range.setStartAfter(lastNode)
|
|
211
|
+
range.collapse(true)
|
|
212
|
+
sel.removeAllRanges()
|
|
213
|
+
sel.addRange(range)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} ExtractedBlock
|
|
3
|
+
* @property {string} tag Lowercase tag name (for example, `p`, `pre`, or `h2`).
|
|
4
|
+
* @property {HTMLElement} element Extracted DOM element.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Extract block-level elements from parsed HTML.
|
|
8
|
+
* Returns an array of {tag, element} objects for each block-level element found.
|
|
9
|
+
* Inline-only content is returned as a synthetic 'p' wrapper.
|
|
10
|
+
*
|
|
11
|
+
* @param {ParentNode} container Inert parsed HTML container.
|
|
12
|
+
* @param {(tag: string) => boolean} [isRoutedTag] Tests plugin-owned container tags.
|
|
13
|
+
* @returns {ExtractedBlock[]}
|
|
14
|
+
*/
|
|
15
|
+
export function extractBlockElements(container: ParentNode, isRoutedTag?: (tag: string) => boolean): ExtractedBlock[];
|
|
16
|
+
export type ExtractedBlock = {
|
|
17
|
+
/**
|
|
18
|
+
* Lowercase tag name (for example, `p`, `pre`, or `h2`).
|
|
19
|
+
*/
|
|
20
|
+
tag: string;
|
|
21
|
+
/**
|
|
22
|
+
* Extracted DOM element.
|
|
23
|
+
*/
|
|
24
|
+
element: HTMLElement;
|
|
25
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/** Block-level tags that represent separate paragraphs */
|
|
2
|
+
const BLOCK_TAGS = new Set([
|
|
3
|
+
'p', 'div', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
4
|
+
'blockquote', 'pre', 'tr', 'td', 'th', 'figure',
|
|
5
|
+
])
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a paragraph that remains owned by inert template content. Cloning
|
|
9
|
+
* untrusted media into a regular detached element can still start loading and
|
|
10
|
+
* fire event attributes before the paste sanitizer gets a chance to run.
|
|
11
|
+
* @returns {HTMLParagraphElement}
|
|
12
|
+
*/
|
|
13
|
+
function createInertParagraph() {
|
|
14
|
+
const template = document.createElement('template')
|
|
15
|
+
template.innerHTML = '<p></p>'
|
|
16
|
+
return /** @type {HTMLParagraphElement} */ (template.content.firstElementChild)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} ExtractedBlock
|
|
21
|
+
* @property {string} tag Lowercase tag name (for example, `p`, `pre`, or `h2`).
|
|
22
|
+
* @property {HTMLElement} element Extracted DOM element.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Extract block-level elements from parsed HTML.
|
|
27
|
+
* Returns an array of {tag, element} objects for each block-level element found.
|
|
28
|
+
* Inline-only content is returned as a synthetic 'p' wrapper.
|
|
29
|
+
*
|
|
30
|
+
* @param {ParentNode} container Inert parsed HTML container.
|
|
31
|
+
* @param {(tag: string) => boolean} [isRoutedTag] Tests plugin-owned container tags.
|
|
32
|
+
* @returns {ExtractedBlock[]}
|
|
33
|
+
*/
|
|
34
|
+
export function extractBlockElements(container, isRoutedTag = () => false) {
|
|
35
|
+
/** @type {ExtractedBlock[]} */
|
|
36
|
+
const results = []
|
|
37
|
+
const isBlockTag = (tag) => BLOCK_TAGS.has(tag) || isRoutedTag(tag)
|
|
38
|
+
|
|
39
|
+
const hasBlockChild = Array.from(container.children).some(
|
|
40
|
+
c => isBlockTag(c.tagName.toLowerCase())
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if (!hasBlockChild) {
|
|
44
|
+
// Wrap flat HTML without block structure as a single paragraph.
|
|
45
|
+
const p = createInertParagraph()
|
|
46
|
+
for (const child of container.childNodes) p.appendChild(child.cloneNode(true))
|
|
47
|
+
if (p.innerHTML.trim()) {
|
|
48
|
+
results.push({ tag: 'p', element: p })
|
|
49
|
+
}
|
|
50
|
+
return results
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {import('../types').DOMNode} node
|
|
55
|
+
*/
|
|
56
|
+
const walk = (node) => {
|
|
57
|
+
for (const child of node.childNodes) {
|
|
58
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
59
|
+
const text = child.textContent?.trim()
|
|
60
|
+
if (text) {
|
|
61
|
+
const p = createInertParagraph()
|
|
62
|
+
p.textContent = text
|
|
63
|
+
results.push({ tag: 'p', element: p })
|
|
64
|
+
}
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (child.nodeType !== Node.ELEMENT_NODE) continue
|
|
69
|
+
|
|
70
|
+
const el = /** @type {HTMLElement} */ (child)
|
|
71
|
+
const tag = el.tagName.toLowerCase()
|
|
72
|
+
|
|
73
|
+
if (tag === 'br') continue
|
|
74
|
+
|
|
75
|
+
if (isBlockTag(tag)) {
|
|
76
|
+
results.push({ tag, element: el })
|
|
77
|
+
} else {
|
|
78
|
+
// Inline or unknown — check for nested block children
|
|
79
|
+
const nested = Array.from(el.children).some(
|
|
80
|
+
c => isBlockTag(c.tagName.toLowerCase())
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if (nested) {
|
|
84
|
+
walk(el)
|
|
85
|
+
} else {
|
|
86
|
+
const p = createInertParagraph()
|
|
87
|
+
for (const child of el.childNodes) p.appendChild(child.cloneNode(true))
|
|
88
|
+
if (p.innerHTML.trim()) {
|
|
89
|
+
results.push({ tag: 'p', element: p })
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
walk(container)
|
|
97
|
+
return results
|
|
98
|
+
}
|