{"version":3,"sources":["webpack://roosterjs/webpack/bootstrap","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/index.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/getTagOfNode.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/safeInstanceOf.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/contains.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/Position.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/toArray.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/blockElements/getBlockElementAtNode.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/getLeafSibling.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/index.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/createRange.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/utils/execCommand.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/wrap.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/queryElements.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/splitParentNode.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/isNodeAfter.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/findClosestElementAncestor.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementAtNode.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/collapseNodes.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/isBlockElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/createElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/Browser.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/shouldSkipNode.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/changeElementTag.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/region/isNodeInRegion.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/utils/blockFormat.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/contentTraverser/ContentTraverser.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/inlineElements/NodeInlineElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/splitTextNode.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/inlineElements/PartialInlineElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/moveChildNodes.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/isNodeEmpty.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/unwrap.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getEditInfoFromImage.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/isVoidHtmlElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/applyTextStyle.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/getComputedStyles.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/inlineElements/LinkInlineElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementBeforeAfter.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/readFile.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/arrayPush.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/normalizeRect.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/list/VList.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/list/getListTypeFromNode.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/region/getSelectionRangeInRegion.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/style/getStyles.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/style/setStyles.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/snapshots/canMoveCurrentSnapshot.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/deleteEditInfo.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/types/ImageEditInfo.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getGeneratedImageSize.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/index.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/blockElements/NodeBlockElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/matchesSelector.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/fromHtml.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/blockElements/getFirstLastBlockElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/inlineElements/ImageInlineElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/inlineElements/getFirstLastInlineElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/getLeafNode.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/inlineElements/EmptyInlineElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/contentTraverser/PositionContentSearcher.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItems.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItemsForIE.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/setColor.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/getInnerHTML.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/isPositionAtBeginningOf.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/table/VTable.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/list/VListItem.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/list/getRootListNode.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/region/getSelectedBlockElementsInRegion.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/region/getRegionsFromRange.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/region/collapseNodesInRegion.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/region/mergeBlocksInRegion.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/htmlSanitizer/getPredefinedCssForElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/snapshots/clearProceedingSnapshots.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/htmlSanitizer/getInheritableStyles.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/htmlSanitizer/cloneObject.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/event/isModifierKey.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/hasFocus.ts","webpack://roosterjs/./packages/roosterjs-color-utils/lib/index.ts","webpack://roosterjs/./node_modules/color-name/index.js","webpack://roosterjs/./node_modules/color-convert/conversions.js","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/getAllFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/clearFormat.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/experiment/experimentCommitListChains.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setBackgroundColor.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setFontName.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setFontSize.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setTextColor.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleBold.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleItalic.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleUnderline.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/utils/toggleListType.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/utils/blockWrap.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/ImageEdit.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/applyChange.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/checkEditInfoState.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/canRegenerateImage.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Resizer.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getTargetSizeByPercentage.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/isResizedTo.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/WordCustomData.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/LevelLists.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/constants.ts","webpack://roosterjs/./packages/roosterjs/lib/index.ts","webpack://roosterjs/./packages/roosterjs/lib/createEditor.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/editor/Editor.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/coreApiMap.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/addUndoSnapshot.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/blockElements/StartEndBlockElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/contentTraverser/BodyScoper.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/contentTraverser/SelectionBlockScoper.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/contentTraverser/SelectionScoper.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/clipboard/extractClipboardEvent.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/applyFormat.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/getPendableFormatState.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/matchLink.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/getTextContent.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/utils/adjustInsertPosition.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/list/createVListFromRegion.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/list/VListChain.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/region/regionTypeData.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/getPositionRect.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/getHtmlWithSelectionPath.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/setHtmlWithSelectionPath.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/addRangeToSelection.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/selection/deleteSelectedContent.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/snapshots/addSnapshot.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/snapshots/moveCurrentSnapshot.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/snapshots/createSnapshots.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/snapshots/canUndoAutoComplete.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/htmlSanitizer/HtmlSanitizer.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/htmlSanitizer/getAllowedValues.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/htmlSanitizer/createDefaultHtmlSanitizerOptions.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/htmlSanitizer/chainSanitizerCallback.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/entity/commitEntity.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/entity/getEntityFromElement.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/entity/getEntitySelector.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/event/cacheGetEventData.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/event/clearEventDataCache.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/event/isCharacterValue.ts","webpack://roosterjs/./packages/roosterjs-editor-dom/lib/event/isCtrlOrMetaPressed.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/attachDomEvent.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/createPasteFragment.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/ensureTypeInContainer.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/focus.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/getContent.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/getPendableFormatState.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/getSelectionRange.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/getStyleBasedFormatState.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/insertNode.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/restoreUndoSnapshot.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/selectRange.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/setContent.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/switchShadowEdit.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/transformColor.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/coreApi/triggerEvent.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/createCorePlugins.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/CopyPastePlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/DOMEventPlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/EditPlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/EntityPlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/LifecyclePlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/MouseUpPlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/PendingFormatStatePlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/TypeInContainerPlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-core/lib/corePlugins/UndoPlugin.ts","webpack://roosterjs/./packages/roosterjs-color-utils/lib/utils/getDarkColor.ts","webpack://roosterjs/./node_modules/color/index.js","webpack://roosterjs/./node_modules/color-string/index.js","webpack://roosterjs/./node_modules/simple-swizzle/index.js","webpack://roosterjs/./node_modules/is-arrayish/index.js","webpack://roosterjs/./node_modules/color-convert/index.js","webpack://roosterjs/./node_modules/color-convert/route.js","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/ContentEdit.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/ContentEdit.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/autoLinkFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/changeFontSize.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/clearBlockFormat.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/createLink.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/getFormatState.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/insertEntity.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/insertImage.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/table/insertTable.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/table/editTable.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/table/formatTable.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/removeLink.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/replaceWithNode.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/rotateElement.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setAlignment.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setDirection.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/utils/collapseSelectedBlocks.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setImageAltText.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setIndentation.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/changeCapitalization.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleBullet.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleNumbering.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/setOrderedListNumbering.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleBlockQuote.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleCodeBlock.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleStrikethrough.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleSubscript.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleSuperscript.ts","webpack://roosterjs/./packages/roosterjs-editor-api/lib/format/toggleHeader.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/cursorFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/entityFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/listFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/markdownFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/quoteFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/shortcutFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/structuredNodeFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/tableFeatures.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/ContextMenu.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContextMenu/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ContextMenu/ContextMenu.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/CustomReplace.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/CustomReplace/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/CustomReplace/CustomReplace.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/CutPasteListChain.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/CutPasteListChain/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/CutPasteListChain/CutPasteListChain.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/HyperLink.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/HyperLink/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/HyperLink/HyperLink.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/ImageEdit.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/generateDataURL.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/saveEditInfo.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Cropper.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/pluginUtils/DragAndDropHelper.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Rotator.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resizeByPercentage.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resetImage.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/ImageResize.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/Paste.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/Paste.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/imageConverter/convertPasteContentForSingleImage.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/commonConverter/convertPastedContentForLI.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/excelConverter/convertPastedContentFromExcel.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/pptConverter/convertPastedContentFromPowerPoint.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/convertPastedContentFromWord.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/wordConverter.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/WordConverterArguments.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/converterUtils.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/lineMerge/handleLineMerge.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromWordOnline.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/ListItemBlock.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/Picker.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Picker/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Picker/PickerPlugin.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/TableResize.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/TableResize/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/TableResize/TableResize.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/Watermark.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Watermark/index.ts","webpack://roosterjs/./packages/roosterjs-editor-plugins/lib/plugins/Watermark/Watermark.ts"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","default","Browser","getBrowserInfo","getComputedStyle","PendableFormatCommandMap","splitBalancedNodeRange","getNextLeafSibling","getPreviousLeafSibling","getFirstLeafNode","getLastLeafNode","KnownCreateElementData","moveCurrentSnapsnot","node","nodeType","tagName","toUpperCase","getTargetWindow","source","commonAncestorContainer","document","ownerDocument","toString","apply","defaultView","window","obj","typeName","targetWindow","targetType","mainWindow","mainWindowType","container","contained","treatSameNodeAsContain","parentNode","contains","internalContains","nodeOrPosition","offsetOrPosType","isFromEndOfRange","this","offset","getIndexOfNode","isAtEnd","nextSibling","getEndOffset","endOffset","Math","max","min","element","normalize","firstChild","newOffset","nextNode","lastChild","childNodes","Position","equalTo","position","isAfter","move","getStart","range","startContainer","startOffset","getEnd","collapsed","endContainer","previousSibling","nodeValue","length","collection","slice","findHeadTailLeafNode","containerBlockNode","isTail","result","sibling","rootNode","getBlockContext","headNode","tailNode","nodes","getLeafSibling","startNode","isNext","skipTags","ignoreSpace","getSibling","getChild","curNode","shouldContinue","indexOf","FONT_SIZES","getElementBasedFormatState","getFocusablePosition","isNodePosition","arg","getPositionFromPath","path","arg1","arg2","arg3","arg4","start","end","Array","isArray","createRange","setStart","setEnd","editor","command","focus","formatter","getDocument","execCommand","getSelectionRange","addUndoSnapshot","formatState","getPendableFormatState","formatName","keys","filter","x","triggerPluginEvent","wrapper","test","createElement","insertBefore","appendChild","checkPosition","targets","some","target","selector","forEachCallback","scope","elements","querySelectorAll","child","endNode","nodeContainedByRangeOnly","startPosition","compareDocumentPosition","endPosition","targetPositions","push","isIntersectWithNodeRange","forEach","splitParentNode","splitBefore","newParent","cloneNode","removeAttribute","innerHTML","temp","node1","node2","root","closest","parentElement","parent","parentBlock","inlineElement","nodeChain","currentNode","tag","resolveInlineElement","callback","getTagOfNode","createTextNode","insertNode","applyTextStyle","select","firstNode","lastNode","contentTraverser","getSelectionTraverser","currentInlineElement","nextInlineElement","getNextInlineElement","applyStyle","isInnerNode","collapse","ref","isStart","canSplitParent","startIndex","endIndex","BLOCK_ELEMENT_TAGS","split","BLOCK_DISPLAY_STYLES","style","display","isEdge","children","attributes","contenteditable","elementData","namespace","className","dataset","createElementNS","setAttribute","datasetName","attrName","userAgent","appVersion","isIE11OrGreater","isIE","isChrome","isFirefox","isSafari","isWebKit","isMac","isWin","isIEOrEdge","navigator","CRLF","CRLF_SPACE","shouldSkipNode","textContent","replace","newTag","newElement","attr","marginTop","marginBottom","replaceChild","region","nodeBefore","nodeAfter","beforeRunCallback","regions","getSelectedRegions","VListChain","createListChains","scoper","createBodyTraverser","ContentTraverser","createSelectionTraverser","createBlockTraverser","currentBlock","getStartBlockElement","getNextBlockElement","getPreviousNextBlockElement","getPreviousBlockElement","current","currentBlockElement","leaf","getEndNode","getStartNode","newBlock","isBlockInScope","currentInline","getStartInlineElement","getPreviousNextInlineElement","getPreviousInlineElement","newInline","getInlineElementBeforeAfter","getStartPosition","getParentBlock","getContainerNode","previousInlineElement","getNextPreviousInlineElement","trimInlineElement","containerNode","getTextContent","getEndPosition","isTextualInlineElement","pos","styler","textNode","returnFirstPart","firstPart","substr","secondPart","newNode","getDecoratedInline","PartialInlineElement","thisStart","otherEnd","from","to","previousNode","keepExistingChildren","removeChild","VISIBLE_ELEMENT_TAGS","VISIBLE_CHILD_ELEMENT_SELECTOR","join","ZERO_WIDTH_SPACE","trim","trimContent","image","json","JSON","parse","safeParseJSON","IMAGE_EDIT_INFO_NAME","src","widthPx","clientWidth","heightPx","clientHeight","naturalWidth","naturalHeight","leftPercent","rightPercent","topPercent","bottomPercent","angleRad","getInitialEditInfo","HTML_VOID_ELEMENTS","STYLET_AGS","callStylerWithInnerNode","formatNodes","formatNode","parentTag","every","shift","getComputedStyles","styleNames","styles","getPropertyValue","toLowerCase","px2Pt","px","round","parseFloat","styleName","isPartial","file","FileReader","onload","onerror","readAsDataURL","mainArray","itemsArray","clientRect","left","right","top","bottom","rootList","items","Error","moveChildNodesToLi","moveLiToList","populateItems","item","getLastItemNumber","undefined","getListType","getLevel","isDummy","writeBack","lastList","doc","listStack","createDocumentFragment","placeholder","getNewListStart","splice","topList","separator","startNumber","index","getNode","setNewListStart","setIndentation","indentation","softOutdent","findListItems","setIsDummy","outdent","indent","changeListType","needChangeType","appendItem","type","nodeTag","mergeVList","list","listStartPos","listEndPos","listTypes","newListTypes","isListElement","currentItem","li","furtherNodes","getListTypeFromNode","regionBase","regionRange","fullSelectionEnd","fullSelectionStart","isRegion","regionStart","regionEnd","getAttribute","pair","valueIndex","map","snapshots","step","newIndex","currentIndex","editInfo","beforeCrop","width","height","angle","originalWidth","originalHeight","visibleWidth","visibleHeight","targetWidth","abs","cos","sin","targetHeight","collapseToSingleElement","equals","blockElement","matches","msMatchesSelector","html","isFirst","getLeafNode","WHITESPACE_REGEX","text","inlineElements","getWordBefore","word","traverse","getInlineElementBefore","inlineBefore","getInlineElementAfter","inlineAfter","getSubStringBefore","getRangeFromText","exactMatch","textIndex","forEachTextInlineElement","textInline","nodeContent","nodeIndex","charCodeAt","getNearestNonTextInlineElement","nearestNonTextInlineElement","traverser","traversingComplete","previousInline","exec","CLIPBOARD_HTML_HEADER_REGEX","ContentHandlers","data","rawHtml","headerValues","parseInt","substring","workaroundForEdge","customValues","tryParseLinkPreview","linkPreview","options","types","contentHandlers","allowLinkPreview","Promise","all","allowedCustomPasteType","textType","getAllowedCustomType","resolve","getAsString","getAsFile","dataUrl","imageDataUri","then","dataTransfer","clipboardData","getData","files","nextStep","getTempDiv","removeTempDiv","contentEditable","setTimeout","color","isBackgroundColor","isDarkMode","colorString","modeIndependentColor","setProperty","darkModeColor","lightModeColor","dataSetName","tempNode","areAllPreviousNodesEmpty","targetNode","normalizeSize","trs","table","td","getTableFromTd","rows","cells","row","tr","rowIndex","sourceCol","targetCol","col","colSpan","rowSpan","hasTd","rect","getBoundingClientRect","spanLeft","spanAbove","cell","recalculateSpans","applyFormat","format","borderCollapse","backgroundColor","bgColorOdd","bgColorEven","borderTop","getBorderStyle","topBorderColor","borderBottom","bottomBorderColor","borderLeft","verticalBorderColor","borderRight","edit","operation","currentRow","currentCell","cloneCell","countSpanAbove","colIndex","nextCell","getCell","newCell","getTd","forEachCellOfCurrentColumn","countSpanLeft","forEachCellOfColumn","forEachCellOfCurrentRow","rowStep","aboveCell","belowCell","colStep","leftCell","rightCell","splitRow","getCellsWithBorder","borderPos","getLeftCells","j","cellRect","found","forEachCellOfRow","getCurrentTd","isNaN","normalizeEmptyTableCells","normalizeTableCellSize","setHTMLElementSizeInPx","newWidth","newHeight","boxSizing","orderListStyles","newListStart","dummy","isOrphanItem","canMerge","mergeItems","wrapIfNotBlockNode","listType","pop","originalRoot","nextLevel","newList","createListElement","newRoot","listStyleType","checkFirst","checkLast","ancestor","createBlockIfEmpty","blocks","block","getRegionCreator","fullRange","firstNodeOfRegion","lastNodeOfRegion","firstNodeValid","lastNodeValid","bothValid","areNodesValid","innerSelector","boundaryTree","allBoundaries","innerNode","outerSelector","inSelectionOuterNode","thisInnerNode","thisOuterNode","boundary","outerNode","boundaries","buildBoundaryTree","iterateNodes","creator","started","ended","previousOuterNode","newRegions","concat","nodesOrBlockElements","refNode","blockRoot","commonContainer","nodeToRemove","nodeToMerge","PREDEFINED_CSS_FOR_ELEMENT","B","EM","I","U","P","PRE","S","STRIKE","SUB","SUP","additionalPredefinedCssForElement","getPositionPath","unshift","isPreviousText","removedSize","totalSize","autoCompleteIndex","INHERITABLE_PROPERTIES","win","cloneObject","assign","existingObj","event","isCtrlKey","ctrlKey","isAltKey","altKey","isMetaKey","metaKey","hasFocus","core","activeElement","contentDiv","cssKeywords","reverseKeywords","convert","rgb","channels","labels","hsl","hsv","hwb","cmyk","xyz","lab","lch","hex","keyword","ansi16","ansi256","hcg","apple","gray","model","h","g","b","delta","rdif","gdif","bdif","v","diff","diffc","k","reversed","currentClosestKeyword","y","currentClosestDistance","Infinity","distance","pow","z","t1","t2","t3","val","smin","lmin","hi","floor","f","q","sl","vmin","wh","bl","ratio","y2","x2","z2","a","atan2","PI","sqrt","hr","args","arguments","ansi","mult","rem","string","match","char","integer","hue","chroma","mg","pure","w","allFeatures","ListFeatures","QuoteFeatures","TableFeatures","StructuredNodeFeatures","AutoLinkFeatures","ShortcutFeatures","CursorFeatures","MarkdownFeatures","EntityFeatures","STYLES_TO_REMOVE","TAGS_TO_UNWRAP","ATTRIBUTES_TO_PRESERVE","TAGS_TO_STOP_UNWRAP","clearNodeFormat","areAllChildrenBlock","toArray","returnBlockElement","isBlockElement","isVoidHtmlElement","wrap","unwrap","isTableCell","safeInstanceOf","removeNonBorderStyles","clearAttribute","getStyles","setStyles","clearBlockFormat","getSelectedBlockElementsInRegion","collapseNodesInRegion","nonborderStyles","isNodeInRegion","clearFormat","formatType","queryElements","defaultFormat","getDefaultFormat","isDefaultFormatEmpty","removeProperty","fontFamily","fontSize","textColor","textColors","backgroundColors","bold","italic","underline","clearInlineFormat","transverser","isMultiBlockSelection","clearAutoDetectFormat","chains","chain","commit","setColor","fontName","lineHeight","canAppendAtCursor","vList","createVListAtBlock","getBlockElementAtNode","createVListFromRegion","wrapFunction","NodeTag","previousSrc","newSrc","initEditInfo","srcChanged","originalSrc","ROTATE_KEYS","CROP_KEYS","ROTATE_CROP_KEYS","ALL_KEYS","areSameNumber","n1","n2","compareTo","img","canvas","context","getContext","drawImage","getImageData","Xs","Ys","Resizer","onDragStart","onDragging","e","base","deltaX","deltaY","horizontalOnly","verticalOnly","shouldPreserveRatio","preserveRatio","shiftKey","minWidth","minHeight","rotateCoordinate","hypotenuse","getResizeHandleHTML","borderColor","leftOrRight","topOrBottom","actualWidth","actualHeight","resizeBorderColor","percentage","getAndSetNodeId","wordCustomData","id","nextNodeId","dict","listsMetadata","currentUniqueListId","WORD_ORDERED_LIST_SELECTOR","WORD_UNORDERED_LIST_SELECTOR","WORD_ONLINE_IDENTIFYING_SELECTOR","LIST_CONTAINER_ELEMENT_CLASS_NAME","UNORDERED_LIST_TAG_NAME","ORDERED_LIST_TAG_NAME","WAC_IDENTIFY_SELECTOR","additionalPlugins","initialContent","plugins","HyperLink","Paste","ContentEdit","getDarkColor","Editor","corePlugins","PLACEHOLDER_PLUGIN_NAME","arrayPush","api","coreApiMap","coreApiOverride","getPluginState","trustedHTMLHandler","plugin","initialize","ensureTypeInContainer","dispose","reverse","isDisposed","option","deleteNode","replaceNode","existingNode","toNode","transformColorForDarkMode","transformColor","scopeOrCallback","Function","collapseNodes","isEmpty","isNodeEmpty","getContent","setContent","content","triggerContentChangedEvent","insertContent","body","DOMParser","parseFromString","allNodes","insertOnNewLine","deleteSelectedContent","paste","pasteAsText","applyCurrentFormat","snapshotBeforePaste","fragment","createPasteFragment","tryGetFromCache","getSelectionPath","selectRange","getFocusedPosition","sel","getSelection","focusNode","focusOffset","getElementAtCursor","startFrom","cacheGetEventData","findClosestElementAncestor","isPositionAtBeginning","isPositionAtBeginningOf","getRegionsFromRange","addDomEventHandler","nameOrMap","handler","eventsToMap","attachDomEvent","eventType","broadcast","triggerEvent","undo","restoreUndoSnapshot","redo","changeSource","canUndoByBackspace","getUndoState","hasNewContent","snapshotsService","canUndo","canMove","canRedo","getScrollContainer","domEvent","scrollContainer","getCustomData","disposer","lifecycle","customData","isInIME","getBodyTraverser","getBlockTraverser","getContentSearcherOfCursor","PositionContentSearcher","runAsync","requestAnimationFrame","setEditorDomAttribute","getEditorDomAttribute","getRelativeDistanceToEditor","addScroll","editorRect","elementRect","scrollLeft","scrollTop","addContentEditFeature","feature","array","features","getStyleBasedFormatState","forceGetStateFromDOM","keyboardEvent","setDarkModeState","nextDarkMode","currentContent","startShadowEdit","switchShadowEdit","stopShadowEdit","isInShadowEdit","shadowEditFragment","isFeatureEnabled","experimentalFeatures","getTrustedHTMLHandler","undoState","isNested","isShadowEdit","addSnapshot","autoCompletePosition","STRUCTURE_NODE_TAGS","StartEndBlockElement","blockContext","getFirstInlineElement","blockNode","getLastInlineElement","getFirstLastInlineElementFromBlockElement","startInline","startBlock","inScope","selStartBlock","selEndBlock","inline","startPartial","endPartial","preventDefault","elementStyle","fontWeight","fontStyle","textDecoration","isBold","isItalic","isUnderline","isStrikeThrough","isSubscript","isSuperscript","reduce","state","queryCommandState","httpExcludeRegEx","domainPortWithUrlRegEx","domainPortRegEx","linkMatchRules","http","RegExp","except","normalizeUrl","url","https","mailto","notes","unc","ftp","news","telnet","gopher","wais","schema","rule","scheme","originalUrl","normalizedUrl","adjustSteps","nodeToInsert","anchor","safeRemove","querySelector","normalizedPosition","splitter","rootNodeToInsert","rootNodes","hasBrNextToRoot","listItem","listNode","tdNode","trNode","newTable","currentTable","shouldInsertListAsText","div","searcher","inlineElementBefore","inlineElementAfter","tryIncludeSiblingNode","includeSiblingLists","nodeForItem","createVListFromItemNode","lastChainIndex","lastNumber","lastNumberBeforeCursor","nameGenerator","ol","canAppendToTail","createListChainName","afterCurrentNode","append","applyChainName","lists","getLists","vlist","isAfterCurrentNode","regionTypeData","rects","getClientRects","span","isDOMChanged","tbody","selectionPath","stringify","LastCommentRegex","lastComment","alternativeComment","pathCommentValue","pathCommentNode","skipSameRange","selection","needAddRange","rangeCount","currentRange","getRangeAt","removeAllRanges","addRange","ensureBeforeAndAfter","emptyNode","nodesToDelete","nodesPairToMerge","beforeEnd","afterEnd","beforeStart","afterStart","snapshot","isAutoCompleteSnapshot","removeCount","maxSize","moveCurrentSnapshot","elementCallbacks","styleCallbacks","getStyleCallbacks","cssStyleCallbacks","attributeCallbacks","tagReplacements","getTagReplacement","additionalTagReplacements","allowedAttributes","getAllowedAttributes","additionalAllowedAttributes","allowedCssClassesRegex","getAllowedCssClassesRegex","additionalAllowedCssClasses","defaultStyleValues","getDefaultStyleValues","additionalDefaultStyleValues","additionalGlobalStyleNodes","preserveHtmlComments","unknownTagReplacement","convertInlineCss","additionalStyleNodes","HtmlSanitizer","sanitizeHtml","sanitizer","currentStyles","currentElementOrStyle","convertCssOnly","unsafeConvertToTrustedHTML","convertGlobalCssToInlineCss","sanitize","processNode","styleNodes","sheet","styleSheet","styleRule","cssRules","cssText","CSSRule","STYLE_RULE","selectorText","currentStyle","isElement","isText","isFragment","isComment","shouldKeep","replacement","whiteSpace","thisStyle","processAttributes","preprocessCss","processCss","next","predefinedStyles","isInheritable","keep","attribute","newValue","processCssClass","originalValue","calculatedValue","originalClasses","calculatedClasses","trustedTypes","policy","createPolicy","createHTML","HTML_TAG_REPLACEMENT","abbr","address","area","article","aside","bdi","bdo","blockquote","br","button","caption","center","cite","code","colgroup","datalist","dd","del","details","dfn","dialog","dir","dl","dt","em","fieldset","figcaption","figure","font","footer","h1","h2","h3","h4","h5","h6","head","header","hgroup","input","ins","kbd","label","legend","main","mark","menu","menuitem","meter","nav","optgroup","output","picture","pre","progress","rp","rt","ruby","samp","section","small","strike","strong","sub","summary","sup","template","textarea","tfoot","th","thead","time","tt","u","ul","var","wbr","xmp","form","applet","audio","basefont","embed","frame","frameset","iframe","link","meta","noscript","param","script","slot","title","track","video","ALLOWED_HTML_ATTRIBUTES","DEFAULT_STYLE_VALUES","overflow","padding","border","float","ALLOWED_CSS_CLASSES","removeValue","removeWidthForLiAndDiv","additionalReplacements","replacements","additionalAttributes","self","toLocaleLowerCase","additionalCssClasses","patterns","additionalDefaultStyles","callbacks","newCallback","isReadonly","isEntity","eventDataCache","isCtrlOrMetaPressed","eventMap","disposers","eventName","handlerObj","pluginEventType","beforeDispatch","onEvent","rawEvent","addEventListener","removeEventListener","processStyles","applyCurrentStyle","sanitizingOption","createDefaultHtmlSanitizerOptions","htmlBefore","htmlAfter","htmlAttributes","createBeforePasteEvent","attrs","htmlFirstLevelChildTags","lastIndexOf","moveChildNodes","pendableFormat","styleBasedFormat","getCurrentFormat","line","lines","maxWidth","getInheritableStyles","shouldSetNodeStyles","innerText","wasNodeJustCreatedByKeyboardEvent","updateCursor","replaceSelection","selectionRange","triggerExtractContentEvent","includeSelectionMarker","clonedRoot","clonedNode","originalRange","shadowEditSelectionPath","getHtmlWithSelectionPath","isRangeCollapsed","cachedPendableFormatState","pendingFormatState","pendableFormatState","cachedPosition","pendableFormatPosition","currentPosition","isSamePosition","isCollapsed","pendablekeys","PendableStyleCheckers","CssFalsyCheckers","queryCommandStateFromDOM","verticalAlign","ogTextColorNode","ogBackgroundColorNode","isBegin","getFirstLastBlockElement","insertedNode","rangeToRestore","cloneRange","deleteContents","adjustInsertPosition","nodeForCursor","isRestoring","addRangeToSelection","restorePendingFormatState","contentChanged","newContent","setHtmlWithSelectionPath","isOn","wasInShadowEdit","ColorAttributeName","transformToLightMode","names","getValueOrDefault","defaultValue","includeSelf","direction","elementsToTransform","allChildren","getElementsByTagName","getAll","transformFunction","onExternalContentTransform","computedValues","styleColor","attrColor","newColor","transformToDarkMode","pluginEvent","onPluginEvent","willHandleEventExclusively","handledExclusively","corePluginOverride","typeInContainer","_placeholder","typeAfterLink","mouseUp","copyPaste","entity","getState","onPaste","extractClipboardEvent","cleanUpAndRestoreSelection","getName","copy","onCutCopy","cut","isCut","newRange","forceInLightMode","tempDiv","onDrop","onFocus","onKeyDownDocument","which","defaultPrevented","cacheSelection","onMouseDownDocument","onScroll","onKeyboardEvent","isCharacterValue","stopPropagation","onInputEvent","onContextMenuEvent","allItems","elementBeforeCursor","eventTargetNode","contextMenuProviders","provider","getContextMenuItems","stopPrintableKeyboardEventPropagation","allowKeyboardEventPropagation","isContextMenuProvider","eventHandlers","keypress","getEventHandler","keydown","keyup","mousedown","contextmenu","compositionstart","compositionend","drop","hasFunctionKey","ctrlOrMeta","allowFunctionKeys","shouldHandleEvent","handleEvent","ENTITY_ID_REGEX","handleCutEvent","checkRemoveEntityForRange","clickingPoint","knownEntityElements","handleMouseDownEvent","handleMouseUpEvent","handleKeyDownEvent","handleBeforePasteEvent","handleContentChangedEvent","handleExtractContentWithDomEvent","handleContextMenuEvent","entityElement","getEntitySelector","pageX","pageY","isContentEditable","workaroundSelectionIssueForIE","resetAll","allId","getEntityFromElement","hydrateEntity","editableEntityElements","isFullyCovered","knownIds","baseId","newId","num","commitEntity","workaroundButton","onblur","COMMANDS","DARK_MODE_DEFAULT_FORMAT","contentDivFormat","initializer","setSelectStyle","adjustColor","doNotAdjustEditorColor","inDarkMode","recalculateDefaultFormat","adjustBrowserBehavior","userSelect","msUserSelect","webkitUserSelect","baseFormat","onMouseUp","removeMouseUpEventListener","isClicking","mouseDownX","mouseDownY","mouseUpEventListerAdded","clear","getCurrentPosition","shouldAlwaysApplyDefaultFormat","undoSnapshotService","createSnapshots","canMoveCurrentSnapshot","clearRedo","clearProceedingSnapshots","canUndoAutoComplete","onKeyDown","onKeyPress","clearRedoForInput","evt","lastKeyPress","computedColor","Color","colorLab","newLValue","alpha","_slice","skippedModels","hashedModelKeys","sort","limiters","valpha","newArr","zeroArray","hashedKeys","limit","freeze","getset","channel","modifier","maxfn","assertArray","arr","toJSON","places","percentString","percent","unitArray","unitObject","Number","toFixed","roundTo","roundToPlace","red","green","blue","saturationl","lightness","saturationv","white","wblack","cyan","magenta","yellow","black","rgbNumber","luminosity","lum","chan","contrast","color2","lum1","lum2","level","contrastRatio","isDark","isLight","negate","lighten","darken","saturate","desaturate","whiten","blacken","grayscale","fade","opaquer","rotate","degrees","mix","mixinColor","weight","color1","w1","w2","newAlpha","raw","colorNames","swizzle","reverseNames","cs","clamp","hexDouble","str","hexAlpha","i2","rgba","hsla","hwba","isArrayish","results","len","fn","getOwnPropertyDescriptor","constructor","conversions","route","fromModel","routes","toModel","wrappedFn","conversion","wrapRounded","wrapRaw","deriveBFS","graph","models","buildGraph","queue","adjacents","adjacent","wrapConversion","cur","settingsOverride","additionalFeatures","hasSettingForKey","defaultDisabled","TRAILING_PUNCTUATION_REGEX","AutoLink","cacheGetLinkData","linkData","href","replaceWithNode","clearEventDataCache","UnlinkWhenBackspaceAfterLink","LinkInlineElement","removeLink","matchLink","trailingPunctuation","autoLink","unlinkWhenBackspaceAfterLink","getNewFontSize","pt","changeBase","fontSizes","ceil","last","change","URI_REGEX","MAILTO_REGEX","FTP_REGEX","getAnchorNodeAtCursor","updateAnchorDisplayText","displayText","altText","checkXss","prefix","search","applyLinkPrefix","listTag","headerTag","isBullet","isNumbering","headerLevel","canUnlink","canAddImageAltText","isBlockQuote","contentNode","isBlock","contentPosition","existingEntity","insertImageWithSrc","imageFile","readFile","getTableCellWidth","columns","cellSpacing","cellPadding","vtable","VTable","cellToSelect","currentCol","newRow","newCol","calculateCellToSelect","textOrRange","backupRange","transform","alignment","align","textAlign","isEmptyBlockUnderTR","quote","blockGroups","group","capitalization","language","getCapitalizedText","originalText","toLocaleUpperCase","wordArray","charAt","regex","DEFAULT_STYLER","paddingLeft","PRE_TAG","wrapped","NoCycleCursorMove","rtl","noCycleCursorMove","ClickOnEntityFeature","cacheGetReadonlyEntityElement","EscapeFromEntityFeature","EnterBeforeReadonlyEntityFeature","cacheGetNeighborEntityElement","newContainer","BackspaceAfterEntityFeature","DeleteBeforeEntityFeature","collapseOnly","entityNode","clickOnEntity","escapeFromEntity","enterBeforeReadonlyEntity","backspaceAfterEntity","deleteBeforeEntity","IndentWhenTab","cacheGetListElement","OutdentWhenShiftTab","MergeInNewLine","blockFormat","toggleListAndPreventDefault","OutdentWhenBackOn1stEmptyLine","MaintainListChainWhenDelete","nextSibiling","getCacheNextSibiling","getListChains","experimentCommitListChains","OutdentWhenEnterOnEmptyLine","AutoBullet","isAListPattern","textBeforeCursor","rangeToDelete","prepareAutoBullet","toggleBullet","toggleNumbering","MaintainListChain","listInfo","listElement","autoBullet","indentWhenTab","outdentWhenShiftTab","outdentWhenBackspaceOnEmptyFirstLine","outdentWhenEnterOnEmptyLine","mergeInNewLineWhenBackspaceOnFirstChar","maintainListChain","maintainListChainWhenDelete","generateBasicMarkdownFeature","triggerCharacter","elementTag","useShiftKey","cacheGetRangeForMarkdownOperation","textContentRange","elementToWrap","extractContents","nonPrintedSpaceTextNode","handleMarkdownEvent","textInlineElement","inlineTextContent","contentIndex","MarkdownBold","MarkdownItalic","MarkdownStrikethrough","MarkdownInlineCode","markdownBold","markdownItalic","markdownStrikethru","markdownInlineCode","QUOTE_TAG","STRUCTURED_TAGS","UnquoteWhenBackOnEmpty1stLine","childOfQuote","cacheGetQuoteChild","splitQuote","UnquoteWhenEnterOnEmptyLine","unquoteWhenBackspaceOnEmptyFirstLine","unquoteWhenEnterOnEmptyLine","createCommand","winKey","macKey","action","commands","toggleBold","toggleItalic","toggleUnderline","changeFontSize","DefaultShortcut","cacheGetCommand","cmd","defaultShortcut","CHILD_PARENT_TAG_MAP","TD","TH","LI","CHILD_SELECTOR","InsertLineBeforeStructuredNodeFeature","cacheGetStructuredElement","insertLineBeforeStructuredNodeFeature","TabInTable","cacheGetTableCell","editTable","UpDownInTable","isUp","hasShiftKey","targetTd","anchorNode","anchorOffset","newPos","setBaseAndExtent","firstTd","tabInTable","upDownInTable","onDismiss","isMenuShowing","dismiss","allowDefaultMenu","initContainer","render","makeReplacement","sourceString","replacementHTML","matchSourceCaseSensitive","defaultReplacements","updateReplacements","newReplacements","longestReplacementLength","replacementEndCharacters","endChars","Set","lastChar","add","getReplacementEndCharacters","has","stringToSearch","getMatchingReplacement","matchingText","matchingRange","parsingSpan","lowerCaseStringToSearch","sourceMatch","replacementMatch","cacheListChains","expectedChangeSource","getTooltipCallback","onLinkClick","trackedLink","onMouse","tryGetHref","onBlur","updateLinkHrefIfShouldUpdate","resetLinkTracking","mouseover","mouseout","blur","isContentEditValue","shouldCheckUpdateLink","originalHref","doesLinkDisplayMatchHref","srcElement","open","updateLinkHref","escapedDisplay","DirectionRad","DirectionOrder","FeatureToOperationMap","DefaultOptions","minRotateDeg","imageSelector","rotateIconHTML","ImageEditHTMLMap","getCornerResizeHTML","getSideResizeHTML","getRotateHTML","getCropHTML","allowedOperations","setEditingImage","removeWrapper","margin","updateWrapper","getImageWrapper","cropContainers","getEditElements","cropOverlays","rotateCenter","rotateHandle","resizeHandles","cropHandles","isCropping","marginHorizontal","marginVertical","cropLeftPx","cropRightPx","cropTopPx","cropBottomPx","getPx","setSize","updateHandleCursor","elementClass","doubleCheckResize","cosAngle","adjustedDistance","MAX_SAFE_INTEGER","rotateGap","ROTATE_GAP","rotateTop","ROTATE_SIZE","clearDndHelpers","matchesSelector","operationOrSelect","selectImage","lastSrc","createWrapper","dndHelpers","createDndHelpers","rotateHandleBackColor","htmlData","thisOperation","dragAndDrop","commonContext","helper","rotateHandles","radIndex","idx","handleRadIndexCalculator","originalDirection","originalIndex","handles","handle","cursor","imageWidth","imageHeight","translate","toDataURL","ROTATION","sw","nw","ne","se","Cropper","dx","dy","widthPercent","heightPercent","fullWidth","fullHeight","newLeft","crop","newRight","newTop","newBottom","basePercentage","deltaValue","fullValue","currentPercentage","minValue","maxValue","getCropHandleHTML","layer","getCropHandleHTMLInternal","overlayHTML","containerHTML","getCropHTMLInternal","trigger","onSubmit","onMouseDown","addDocumentEvents","initX","initY","initValue","onMouseMove","removeDocumentEvents","onDragEnd","DEG_PER_RAD","DEFAULT_ROTATE_HANDLE_HEIGHT","Rotator","newX","newY","angleInRad","angleInDeg","getRotateIconHTML","stroke","handleLeft","selectionBorderColor","forcePreserveRatio","resizableImageSelector","showResizeHandle","hideResizeHandle","selectImageAfterUnSelect","ImageEdit","ImageResize","wacListElements","el","isWordOnlineWithList","isPureLiNode","childNode","changeElementTag","LAST_TD_END_REGEX","LAST_TR_END_REGEX","LAST_TR_REGEX","LAST_TABLE_REGEX","excelHandler","trMatch","tableMatch","chainSanitizerCallback","borderStyle","wordConverter","createWordConverter","wordConverterArgs","createWordConverterArguments","processNodesDiscovery","processNodeConvert","nextUniqueId","numBulletsConverted","numNumberedConverted","createCustomData","listItems","currentListIdsByLevels","createLevelLists","lastProcessedItem","LINE_BREAKS","getOrCreateListForNode","metadata","listMetadata","recurringGetOrCreateListAtNode","possibleList","getRealPreviousSibling","listId","getObject","uniqueListId","setObject","convertListIfNeeded","cleanupListIgnore","levels","nodesToRemove","isEmptySpan","fixWordListComments","isIgnoreNode","getListItemMetadata","listAttribute","getStyleValue","listProps","wordListId","originalNode","isFakeBullet","fakeBullet","getFakeBulletText","removeComments","nextElement","endComment","getRealNextSibling","newSpan","prevSibling","isEmptyTextNode","resetCurrentLists","ll","itemMetadata","levelInfo","ignore","numberOfItems","secondFakeBullet","firstFakeBullet","processBlock","checkAndAddBr","insertConvertedListToDoc","convertedListElement","listItemBlock","insertPositionNode","startElement","replaceRegex","prevParent","nextParent","sanitizeListItemContainer","curListItemBlock","listElements","curItem","listItemContainers","lastItemInCurBlock","endElement","createListItemBlock","getListItemBlocks","itemBlock","flattenListBlock","listItemContainer","getContainerListType","itemLevel","listRootElement","itemToInsert","curListLevel","lastElementChild","lastChildTag","firstElementChild","insertListItem","parentContainer","ESC_CHAR_CODE","LEFT_ARROW_CHAR_CODE","UP_ARROW_CHAR_CODE","RIGHT_ARROW_CHAR_CODE","DOWN_ARROW_CHAR_CODE","DELETE_CHAR_CODE","UNIDENTIFIED_CODE","dataProvider","pickerOptions","isPendingInputEventHandling","onInitalize","htmlNode","wordToReplace","getWord","lastKnownRange","setIsSuggesting","handleAutoComplete","isSuggesting","onDispose","onContentChanged","elementIdPrefix","eventHandledOnKeyDown","isAndroidKeyboardEvent","currentInputLength","calcInputLength","onKeyDownEvent","onAndroidInputEvent","shouldHandleKeyUpEvent","onKeyUpDomEvent","setLastKnownRange","onIsSuggestingChanged","setAriaOwns","setAriaActiveDescendant","cancelDefaultKeyDownEvent","stopImmediatePropagation","getIdValue","getNamedItem","getWordBeforeCursor","replacementNode","getRangeUntilAt","startPos","endPos","hasMatched","isModifierKey","trimmedWordBeforeCursor","wordBeforeCursorWithoutTriggerChar","wordBeforeCursor","queryStringUpdated","blockSuggestions","setCursorPoint","rangeNode","nodeBeforeCursor","setRangeStart","nodeBeforeNodeBeforeCursor","detach","targetPoint","bufferZone","shiftHighlight","isHorizontal","getSelectedIndex","selectOption","tryRemoveNode","nodeAfterCursor","nodeId","onRemove","newInputLength","inputType","wordBeforCursor","getInlineElementBeforeCursor","wordFromRange","wordFromCache","nodeOffset","suggestionsLabel","selectedIndex","suggestionLabelPrefix","charCode","getHorizontalDistance","toLeft","tableRectMap","currentCellsToResize","nextCellsToResize","resizingState","insertingState","onMouseOutInserter","setCurrentInsertTd","cacheRects","setCurrentTable","isRTL","setTableResizer","tdRect","normalizeRect","verticalInserterTd","preTd","previousElementSibling","setCurrentTd","buttons","horizontalInserterTd","insertTd","currentInsertTd","startResizingTable","resizingVtable","currentTableVerticalBorder","currentTableHorizontalBorder","startResizeCells","startHorizontalResizeCells","currentTd","startVerticalResizeCells","frameAnimateResizeCells","resizeCells","resizeTable","mouseX","mouseY","ratioX","ratioY","shouldResizeX","shouldResizeY","wordBreak","resizeRows","canResizeColumns","resizeColumns","isShiftPressed","endResizeCells","setupResizerContainer","onMouseMoveDisposer","removeResizerContainer","resizerContainer","tableResizerContainer","tableRect","inserter","createInserter","inserterBackgroundColor","inserterColor","outerDivStyle","HORIZONTAL_INSERTER","VERTICAL_INSERTER","resizerPosX","horizontalResizer","verticalResizer","createCellsResizer","hasChildNodes","tableResizer","createTableResizer","TABLE_RESIZER_LENGTH","horizontal","watermark","showHideWatermark","watermarks","isShowing","removeWatermark","insertEntity","spellcheck"],"mappings":"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","file":"rooster-min.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 106);\n","export { default as getBlockElementAtNode } from './blockElements/getBlockElementAtNode';\nexport { default as getFirstLastBlockElement } from './blockElements/getFirstLastBlockElement';\n\nexport { default as ContentTraverser } from './contentTraverser/ContentTraverser';\nexport { default as PositionContentSearcher } from './contentTraverser/PositionContentSearcher';\n\nexport { default as getInlineElementAtNode } from './inlineElements/getInlineElementAtNode';\nexport { default as ImageInlineElement } from './inlineElements/ImageInlineElement';\nexport { default as LinkInlineElement } from './inlineElements/LinkInlineElement';\nexport { default as NodeInlineElement } from './inlineElements/NodeInlineElement';\nexport { default as PartialInlineElement } from './inlineElements/PartialInlineElement';\n\nexport { default as extractClipboardEvent } from './clipboard/extractClipboardEvent';\nexport { default as extractClipboardItems } from './clipboard/extractClipboardItems';\nexport { default as extractClipboardItemsForIE } from './clipboard/extractClipboardItemsForIE';\n\nexport { default as arrayPush } from './utils/arrayPush';\nexport { default as applyTextStyle } from './utils/applyTextStyle';\nexport { Browser, getBrowserInfo } from './utils/Browser';\nexport { default as applyFormat } from './utils/applyFormat';\nexport { default as changeElementTag } from './utils/changeElementTag';\nexport { default as collapseNodes } from './utils/collapseNodes';\nexport { default as contains } from './utils/contains';\nexport { default as findClosestElementAncestor } from './utils/findClosestElementAncestor';\nexport { default as fromHtml } from './utils/fromHtml';\nexport { default as getComputedStyles, getComputedStyle } from './utils/getComputedStyles';\nexport {\n default as getPendableFormatState,\n PendableFormatCommandMap,\n PendableFormatNames,\n} from './utils/getPendableFormatState';\nexport { default as getTagOfNode } from './utils/getTagOfNode';\nexport { default as isBlockElement } from './utils/isBlockElement';\nexport { default as isNodeEmpty } from './utils/isNodeEmpty';\nexport { default as isVoidHtmlElement } from './utils/isVoidHtmlElement';\nexport { default as matchLink } from './utils/matchLink';\nexport { default as queryElements } from './utils/queryElements';\nexport { default as splitParentNode, splitBalancedNodeRange } from './utils/splitParentNode';\nexport { default as unwrap } from './utils/unwrap';\nexport { default as wrap } from './utils/wrap';\nexport { getNextLeafSibling, getPreviousLeafSibling } from './utils/getLeafSibling';\nexport { getFirstLeafNode, getLastLeafNode } from './utils/getLeafNode';\nexport { default as getTextContent } from './utils/getTextContent';\nexport { default as splitTextNode } from './utils/splitTextNode';\nexport { default as normalizeRect } from './utils/normalizeRect';\nexport { default as toArray } from './utils/toArray';\nexport { default as safeInstanceOf } from './utils/safeInstanceOf';\nexport { default as readFile } from './utils/readFile';\nexport { default as getInnerHTML } from './utils/getInnerHTML';\nexport { default as setColor } from './utils/setColor';\nexport { default as matchesSelector } from './utils/matchesSelector';\nexport { default as adjustInsertPosition } from './utils/adjustInsertPosition';\nexport { default as createElement, KnownCreateElementData } from './utils/createElement';\nexport { default as moveChildNodes } from './utils/moveChildNodes';\n\nexport { default as VTable } from './table/VTable';\nexport { default as VList } from './list/VList';\nexport { default as VListItem } from './list/VListItem';\nexport { default as createVListFromRegion } from './list/createVListFromRegion';\nexport { default as VListChain } from './list/VListChain';\n\nexport { default as getRegionsFromRange } from './region/getRegionsFromRange';\nexport { default as getSelectedBlockElementsInRegion } from './region/getSelectedBlockElementsInRegion';\nexport { default as collapseNodesInRegion } from './region/collapseNodesInRegion';\nexport { default as isNodeInRegion } from './region/isNodeInRegion';\nexport { default as getSelectionRangeInRegion } from './region/getSelectionRangeInRegion';\nexport { default as mergeBlocksInRegion } from './region/mergeBlocksInRegion';\n\nexport { default as Position } from './selection/Position';\nexport { default as createRange } from './selection/createRange';\nexport { default as getPositionRect } from './selection/getPositionRect';\nexport { default as isPositionAtBeginningOf } from './selection/isPositionAtBeginningOf';\nexport { default as getSelectionPath } from './selection/getSelectionPath';\nexport { default as getHtmlWithSelectionPath } from './selection/getHtmlWithSelectionPath';\nexport { default as setHtmlWithSelectionPath } from './selection/setHtmlWithSelectionPath';\nexport { default as addRangeToSelection } from './selection/addRangeToSelection';\nexport { default as deleteSelectedContent } from './selection/deleteSelectedContent';\n\nexport { default as addSnapshot } from './snapshots/addSnapshot';\nexport { default as canMoveCurrentSnapshot } from './snapshots/canMoveCurrentSnapshot';\nexport { default as clearProceedingSnapshots } from './snapshots/clearProceedingSnapshots';\nexport {\n default as moveCurrentSnapshot,\n moveCurrentSnapsnot,\n} from './snapshots/moveCurrentSnapshot';\nexport { default as createSnapshots } from './snapshots/createSnapshots';\nexport { default as canUndoAutoComplete } from './snapshots/canUndoAutoComplete';\n\nexport { default as HtmlSanitizer } from './htmlSanitizer/HtmlSanitizer';\nexport { default as getInheritableStyles } from './htmlSanitizer/getInheritableStyles';\nexport { default as createDefaultHtmlSanitizerOptions } from './htmlSanitizer/createDefaultHtmlSanitizerOptions';\nexport { default as chainSanitizerCallback } from './htmlSanitizer/chainSanitizerCallback';\n\nexport { default as commitEntity } from './entity/commitEntity';\nexport { default as getEntityFromElement } from './entity/getEntityFromElement';\nexport { default as getEntitySelector } from './entity/getEntitySelector';\n\nexport { default as cacheGetEventData } from './event/cacheGetEventData';\nexport { default as clearEventDataCache } from './event/clearEventDataCache';\nexport { default as isModifierKey } from './event/isModifierKey';\nexport { default as isCharacterValue } from './event/isCharacterValue';\nexport { default as isCtrlOrMetaPressed } from './event/isCtrlOrMetaPressed';\n\nexport { default as getStyles } from './style/getStyles';\nexport { default as setStyles } from './style/setStyles';\n","import { NodeType } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Get the html tag of a node, or empty if it is not an element\r\n * @param node The node to get tag of\r\n * @returns Tag name in upper case if the given node is an Element, or empty string otherwise\r\n */\r\nexport default function getTagOfNode(node: Node): string {\r\n return node && node.nodeType == NodeType.Element ? (node).tagName.toUpperCase() : '';\r\n}\r\n","import { TargetWindow } from 'roosterjs-editor-types';\n\n// NOTE: Type TargetWindow is an auto-generated type.\n// Run node ./tools/generateTargetWindow.js to generate it.\n\n/**\n * @internal Export for test only\n * Try get window from the given node or range\n * @param source Source node or range\n */\nexport function getTargetWindow(source: Node | Range): TargetWindow {\n const node = source && ((source).commonAncestorContainer || source);\n const document =\n node &&\n (node.ownerDocument ||\n (Object.prototype.toString.apply(node) == '[object HTMLDocument]'\n ? node\n : null));\n\n // If document exists but document.defaultView doesn't exist, it is a detached object, just use current window instead\n const targetWindow = document && ((document.defaultView || window) as any);\n return targetWindow as TargetWindow;\n}\n\n/**\n * Check if the given object is instance of the target type\n * @param obj Object to check\n * @param typeName Target type name\n */\nexport default function safeInstanceOf(\n obj: any,\n typeName: T\n): obj is TargetWindow[T] {\n const targetWindow = getTargetWindow(obj);\n const targetType = targetWindow && (targetWindow[typeName] as any);\n const mainWindow = (window as any) as TargetWindow;\n const mainWindowType = mainWindow && (mainWindow[typeName] as any);\n return (\n (mainWindowType && obj instanceof mainWindowType) ||\n (targetType && obj instanceof targetType)\n );\n}\n","import safeInstanceOf from '../utils/safeInstanceOf';\r\nimport { NodeType } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Test if a node contains another node\r\n * @param container The container node\r\n * @param contained The node to check if it is inside container\r\n * @param treatSameNodeAsContain When container and contained are the same node,\r\n * return true if this param is set to true, otherwise return false. Default value is false\r\n * @returns True if contained is inside container, or they are the same node when treatSameNodeAsContain is true.\r\n * Otherwise false.\r\n */\r\nexport default function contains(\r\n container: Node,\r\n contained: Node,\r\n treatSameNodeAsContain?: boolean\r\n): boolean;\r\n\r\n/**\r\n * Test if a node contains a given range\r\n * @param container The container node\r\n * @param contained The range to check if it is inside container\r\n * @returns True if contained is inside container, otherwise false\r\n */\r\nexport default function contains(container: Node, contained: Range): boolean;\r\n\r\nexport default function contains(\r\n container: Node,\r\n contained: Node | Range,\r\n treatSameNodeAsContain?: boolean\r\n): boolean {\r\n if (!container || !contained) {\r\n return false;\r\n }\r\n\r\n if (treatSameNodeAsContain && container == contained) {\r\n return true;\r\n }\r\n\r\n if (safeInstanceOf(contained, 'Range')) {\r\n contained = contained && contained.commonAncestorContainer;\r\n treatSameNodeAsContain = true;\r\n }\r\n\r\n if (contained && contained.nodeType == NodeType.Text) {\r\n contained = contained.parentNode;\r\n treatSameNodeAsContain = true;\r\n }\r\n\r\n if (container.nodeType != NodeType.Element && container.nodeType != NodeType.DocumentFragment) {\r\n return !!treatSameNodeAsContain && container == contained;\r\n }\r\n\r\n return (\r\n !!(treatSameNodeAsContain || container != contained) &&\r\n internalContains(container, contained)\r\n );\r\n}\r\n\r\nfunction internalContains(container: Node, contained: Node): boolean {\r\n if (container.contains) {\r\n return container.contains(contained);\r\n } else {\r\n while (contained) {\r\n if (contained == container) {\r\n return true;\r\n }\r\n\r\n contained = contained.parentNode;\r\n }\r\n\r\n return false;\r\n }\r\n}\r\n","import findClosestElementAncestor from '../utils/findClosestElementAncestor';\r\nimport isNodeAfter from '../utils/isNodeAfter';\r\nimport { NodePosition, NodeType, PositionType } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Represent a position in DOM tree by the node and its offset index\r\n */\r\nexport default class Position implements NodePosition {\r\n readonly node: Node;\r\n readonly element: HTMLElement;\r\n readonly offset: number;\r\n readonly isAtEnd: boolean;\r\n\r\n /**\r\n * Clone and validate a position from existing position.\r\n * If the given position has invalid offset, this function will return a corrected value.\r\n * @param position The original position to clone from\r\n */\r\n constructor(position: NodePosition);\r\n\r\n /**\r\n * Create a Position from node and an offset number\r\n * @param node The node of this position\r\n * @param offset Offset of this position\r\n * @param isFromEndOfRange Whether this position is created from end of a range. An position\r\n * created from end of range has different behavior when normalize, it will use the child node\r\n * before current position if any as a deeper level node and set isAtEnd to true.\r\n */\r\n constructor(node: Node, offset: number, isFromEndOfRange?: boolean);\r\n\r\n /**\r\n * Create a Position from node and a type of position\r\n * @param node The node of this position\r\n * @param positionType Type of the position, can be Begin, End, Before, After\r\n */\r\n constructor(node: Node, positionType: PositionType);\r\n\r\n constructor(\r\n nodeOrPosition: Node | NodePosition,\r\n offsetOrPosType?: number,\r\n private readonly isFromEndOfRange?: boolean\r\n ) {\r\n if ((nodeOrPosition).node) {\r\n this.node = (nodeOrPosition).node;\r\n offsetOrPosType = (nodeOrPosition).offset;\r\n } else {\r\n this.node = nodeOrPosition;\r\n }\r\n\r\n switch (offsetOrPosType) {\r\n case PositionType.Before:\r\n this.offset = getIndexOfNode(this.node);\r\n this.node = this.node.parentNode;\r\n this.isAtEnd = false;\r\n break;\r\n\r\n case PositionType.After:\r\n this.offset = getIndexOfNode(this.node) + 1;\r\n this.isAtEnd = !this.node.nextSibling;\r\n this.node = this.node.parentNode;\r\n break;\r\n\r\n case PositionType.End:\r\n this.offset = getEndOffset(this.node);\r\n this.isAtEnd = true;\r\n break;\r\n\r\n default:\r\n let endOffset = getEndOffset(this.node);\r\n this.offset = Math.max(0, Math.min(offsetOrPosType, endOffset));\r\n this.isAtEnd = offsetOrPosType > 0 && offsetOrPosType >= endOffset;\r\n break;\r\n }\r\n\r\n this.element = findClosestElementAncestor(this.node);\r\n }\r\n\r\n /**\r\n * Normalize this position to the leaf node, return the normalize result.\r\n * If current position is already using leaf node, return this position object itself\r\n */\r\n normalize(): NodePosition {\r\n if (this.node.nodeType == NodeType.Text || !this.node.firstChild) {\r\n return this;\r\n }\r\n\r\n let node = this.node;\r\n let newOffset: number | PositionType.Begin | PositionType.End = this.isAtEnd\r\n ? PositionType.End\r\n : this.offset;\r\n while (node.nodeType == NodeType.Element || node.nodeType == NodeType.DocumentFragment) {\r\n const nextNode = this.isFromEndOfRange\r\n ? newOffset == PositionType.End\r\n ? node.lastChild\r\n : node.childNodes[newOffset - 1]\r\n : newOffset == PositionType.Begin\r\n ? node.firstChild\r\n : newOffset == PositionType.End\r\n ? node.lastChild\r\n : node.childNodes[newOffset];\r\n\r\n if (nextNode) {\r\n node = nextNode;\r\n newOffset =\r\n this.isAtEnd || this.isFromEndOfRange ? PositionType.End : PositionType.Begin;\r\n } else {\r\n break;\r\n }\r\n }\r\n return new Position(node, newOffset, this.isFromEndOfRange);\r\n }\r\n\r\n /**\r\n * Check if this position is equal to the given position\r\n * @param position The position to check\r\n */\r\n equalTo(position: NodePosition): boolean {\r\n return (\r\n position &&\r\n (this == position ||\r\n (this.node == position.node &&\r\n this.offset == position.offset &&\r\n this.isAtEnd == position.isAtEnd))\r\n );\r\n }\r\n\r\n /**\r\n * Checks if this position is after the given position\r\n */\r\n isAfter(position: NodePosition): boolean {\r\n return this.node == position.node\r\n ? (this.isAtEnd && !position.isAtEnd) || this.offset > position.offset\r\n : isNodeAfter(this.node, position.node);\r\n }\r\n\r\n /**\r\n * Move this position with offset, returns a new position with a valid offset in the same node\r\n * @param offset Offset to move with\r\n */\r\n move(offset: number) {\r\n return new Position(this.node, Math.max(this.offset + offset, 0));\r\n }\r\n\r\n /**\r\n * Get start position of the given Range\r\n * @param range The range to get position from\r\n */\r\n static getStart(range: Range) {\r\n return new Position(range.startContainer, range.startOffset);\r\n }\r\n\r\n /**\r\n * Get end position of the given Range\r\n * @param range The range to get position from\r\n */\r\n static getEnd(range: Range) {\r\n // For collapsed range, always return the same value of start container to make sure\r\n // end position is not before start position\r\n return range.collapsed\r\n ? Position.getStart(range)\r\n : new Position(range.endContainer, range.endOffset, true /*isFromEndOfRange*/);\r\n }\r\n}\r\n\r\nfunction getIndexOfNode(node: Node): number {\r\n let i = 0;\r\n while ((node = node.previousSibling)) {\r\n i++;\r\n }\r\n return i;\r\n}\r\n\r\nfunction getEndOffset(node: Node): number {\r\n if (node.nodeType == NodeType.Text) {\r\n return node.nodeValue.length;\r\n } else if (node.nodeType == NodeType.Element) {\r\n return node.childNodes.length;\r\n } else {\r\n return 1;\r\n }\r\n}\r\n","/**\n * Convert a named node map to an array\n * @param collection The map to convert\n */\nexport default function toArray(collection: NamedNodeMap): Attr[];\n\n/**\n * Convert a named node map to an array\n * @param collection The map to convert\n */\nexport default function toArray(collection: DataTransferItemList): DataTransferItem[];\n\n/**\n * Convert a collection to an array\n * @param collection The collection to convert\n */\nexport default function toArray(collection: NodeListOf): T[];\n\n/**\n * Convert a collection to an array\n * @param collection The collection to convert\n */\nexport default function toArray(collection: HTMLCollectionOf): T[];\n\n/**\n * Convert an array to an array.\n * This is to satisfy typescript compiler. For some cases the object can be a collection at runtime,\n * but the declaration is an array. e.g. ClipboardData.types\n * @param array The array to convert\n */\nexport default function toArray(array: readonly T[]): T[];\n\nexport default function toArray(collection: any): any[] {\n return [].slice.call(collection);\n}\n","import collapseNodes from '../utils/collapseNodes';\r\nimport contains from '../utils/contains';\r\nimport getTagOfNode from '../utils/getTagOfNode';\r\nimport isBlockElement from '../utils/isBlockElement';\r\nimport NodeBlockElement from './NodeBlockElement';\r\nimport StartEndBlockElement from './StartEndBlockElement';\r\nimport { BlockElement } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * This produces a block element from a a node\r\n * It needs to account for various HTML structure. Examples:\r\n * 1) <root><div>abc</div></root>\r\n * This is most common the case, user passes in a node pointing to abc, and get back a block representing <div>abc</div>\r\n * 2) <root><p><br></p></root>\r\n * Common content for empty block, user passes node pointing to <br>, and get back a block representing <p><br></p>\r\n * 3) <root>abc</root>\r\n * Not common, but does happen. It is still a block in user's view. User passes in abc, and get back a start-end block representing abc\r\n * NOTE: abc could be just one node. However, since it is not a html block, it is more appropriate to use start-end block although they point to same node\r\n * 4) <root><div>abc<br>123</div></root>\r\n * A bit tricky, but can happen when user use Ctrl+Enter which simply inserts a <BR> to create a link break. There're two blocks:\r\n * block1: 1) abc<br> block2: 123\r\n * 5) <root><div>abc<div>123</div></div></root>\r\n * Nesting div and there is text node in same level as a DIV. Two blocks: 1) abc 2) <div>123</div>\r\n * 6) <root><div>abc<span>123<br>456</span></div></root>\r\n * This is really tricky. Essentially there is a <BR> in middle of a span breaking the span into two blocks;\r\n * block1: abc<span>123<br> block2: 456\r\n * In summary, given any arbitrary node (leaf), to identify the head and tail of the block, following rules need to be followed:\r\n * 1) to identify the head, it needs to crawl DOM tre left/up till a block node or BR is encountered\r\n * 2) same for identifying tail\r\n * 3) should also apply a block ceiling, meaning as it crawls up, it should stop at a block node\r\n * @param rootNode Root node of the scope, the block element will be inside of this node\r\n * @param node The node to get BlockElement start from\r\n */\r\nexport default function getBlockElementAtNode(rootNode: Node, node: Node): BlockElement {\r\n if (!contains(rootNode, node)) {\r\n return null;\r\n }\r\n\r\n // Identify the containing block. This serves as ceiling for traversing down below\r\n // NOTE: this container block could be just the rootNode,\r\n // which cannot be used to create block element. We will special case handle it later on\r\n let containerBlockNode = StartEndBlockElement.getBlockContext(node);\r\n if (containerBlockNode == node) {\r\n return new NodeBlockElement(containerBlockNode);\r\n }\r\n\r\n // Find the head and leaf node in the block\r\n let headNode = findHeadTailLeafNode(node, containerBlockNode, false /*isTail*/);\r\n let tailNode = findHeadTailLeafNode(node, containerBlockNode, true /*isTail*/);\r\n\r\n // At this point, we have the head and tail of a block, here are some examples and where head and tail point to\r\n // 1) <root><div>hello<br></div></root>, head: hello, tail: <br>\r\n // 2) <root><div>hello<span style=\"font-family: Arial\">world</span></div></root>, head: hello, tail: world\r\n // Both are actually completely and exclusively wrapped in a parent div, and can be represented with a Node block\r\n // So we shall try to collapse as much as we can to the nearest common ancestor\r\n let nodes = collapseNodes(rootNode, headNode, tailNode, false /*canSplitParent*/);\r\n headNode = nodes[0];\r\n tailNode = nodes[nodes.length - 1];\r\n\r\n if (headNode.parentNode != tailNode.parentNode) {\r\n // Un-Balanced start and end, create a start-end block\r\n return new StartEndBlockElement(rootNode, headNode, tailNode);\r\n } else {\r\n // Balanced start and end (point to same parent), need to see if further collapsing can be done\r\n while (!headNode.previousSibling && !tailNode.nextSibling) {\r\n let parentNode = headNode.parentNode;\r\n if (parentNode == containerBlockNode) {\r\n // Has reached the container block\r\n if (containerBlockNode != rootNode) {\r\n // If the container block is not the root, use the container block\r\n headNode = tailNode = parentNode;\r\n }\r\n break;\r\n } else if (parentNode != rootNode) {\r\n // Continue collapsing to parent\r\n headNode = tailNode = parentNode;\r\n } else {\r\n break;\r\n }\r\n }\r\n\r\n // If head and tail are same and it is a block element, create NodeBlock, otherwise start-end block\r\n return headNode == tailNode && isBlockElement(headNode)\r\n ? new NodeBlockElement(headNode as HTMLElement)\r\n : new StartEndBlockElement(rootNode, headNode, tailNode);\r\n }\r\n}\r\n\r\n/**\r\n * Given a node and container block, identify the first/last leaf node\r\n * A leaf node is defined as deepest first/last node in a block\r\n * i.e. <div><span style=\"font-family: Arial\">abc</span></div>, abc is the head leaf of the block\r\n * Often <br> or a child <div> is used to create a block. In that case, the leaf after the sibling div or br should be the head leaf\r\n * i.e. <div>123<br>abc</div>, abc is the head of a block because of a previous sibling <br>\r\n * i.e. <div><div>123</div>abc</div>, abc is also the head of a block because of a previous sibling <div>\r\n */\r\nfunction findHeadTailLeafNode(node: Node, containerBlockNode: Node, isTail: boolean): Node {\r\n let result = node;\r\n\r\n if (getTagOfNode(result) == 'BR' && isTail) {\r\n return result;\r\n }\r\n\r\n while (result) {\r\n let sibling = node;\r\n while (!(sibling = isTail ? node.nextSibling : node.previousSibling)) {\r\n node = node.parentNode;\r\n if (node == containerBlockNode) {\r\n return result;\r\n }\r\n }\r\n\r\n while (sibling) {\r\n if (isBlockElement(sibling)) {\r\n return result;\r\n } else if (getTagOfNode(sibling) == 'BR') {\r\n return isTail ? sibling : result;\r\n }\r\n\r\n node = sibling;\r\n sibling = isTail ? node.firstChild : node.lastChild;\r\n }\r\n\r\n result = node;\r\n }\r\n return result;\r\n}\r\n","import contains from './contains';\r\nimport getTagOfNode from './getTagOfNode';\r\nimport shouldSkipNode from './shouldSkipNode';\r\n\r\n/**\r\n * This walks forwards/backwards DOM tree to get next meaningful node\r\n * @param rootNode Root node to scope the leaf sibling node\r\n * @param startNode current node to get sibling node from\r\n * @param isNext True to get next leaf sibling node, false to get previous leaf sibling node\r\n * @param skipTags (Optional) tags that child elements will be skipped\r\n * @param ignoreSpace (Optional) Ignore pure space text node when check if the node should be skipped\r\n */\r\nexport function getLeafSibling(\r\n rootNode: Node,\r\n startNode: Node,\r\n isNext: boolean,\r\n skipTags?: string[],\r\n ignoreSpace?: boolean\r\n): Node {\r\n let result = null;\r\n let getSibling = isNext\r\n ? (node: Node) => node.nextSibling\r\n : (node: Node) => node.previousSibling;\r\n let getChild = isNext ? (node: Node) => node.firstChild : (node: Node) => node.lastChild;\r\n if (contains(rootNode, startNode)) {\r\n let curNode = startNode;\r\n let shouldContinue = true;\r\n\r\n while (shouldContinue) {\r\n // Find next/previous node, starting from next/previous sibling, then one level up to find next/previous sibling from parent\r\n // till a non-null nextSibling/previousSibling is found or the ceiling is encountered (rootNode)\r\n let parentNode = curNode.parentNode;\r\n curNode = getSibling(curNode);\r\n while (!curNode && parentNode != rootNode) {\r\n curNode = getSibling(parentNode);\r\n parentNode = parentNode.parentNode;\r\n }\r\n\r\n // Now traverse down to get first/last child\r\n while (\r\n curNode &&\r\n (!skipTags || skipTags.indexOf(getTagOfNode(curNode)) < 0) &&\r\n getChild(curNode)\r\n ) {\r\n curNode = getChild(curNode);\r\n }\r\n\r\n // Check special nodes (i.e. node that has a display:none etc.) and continue looping if so\r\n shouldContinue = curNode && shouldSkipNode(curNode, ignoreSpace);\r\n if (!shouldContinue) {\r\n // Found a good leaf node, assign and exit\r\n result = curNode;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * This walks forwards DOM tree to get next meaningful node\r\n * @param rootNode Root node to scope the leaf sibling node\r\n * @param startNode current node to get sibling node from\r\n * @param skipTags (Optional) tags that child elements will be skipped\r\n */\r\nexport function getNextLeafSibling(rootNode: Node, startNode: Node, skipTags?: string[]): Node {\r\n return getLeafSibling(rootNode, startNode, true /*isNext*/, skipTags);\r\n}\r\n\r\n/**\r\n * This walks backwards DOM tree to get next meaningful node\r\n * @param rootNode Root node to scope the leaf sibling node\r\n * @param startNode current node to get sibling node from\r\n * @param skipTags (Optional) tags that child elements will be skipped\r\n */\r\nexport function getPreviousLeafSibling(rootNode: Node, startNode: Node, skipTags?: string[]): Node {\r\n return getLeafSibling(rootNode, startNode, false /*isNext*/, skipTags);\r\n}\r\n","export { default as changeFontSize, FONT_SIZES } from './format/changeFontSize';\r\nexport { default as clearBlockFormat } from './format/clearBlockFormat';\r\nexport { default as clearFormat } from './format/clearFormat';\r\nexport { default as createLink } from './format/createLink';\r\nexport { default as getFormatState, getElementBasedFormatState } from './format/getFormatState';\r\nexport { default as insertEntity } from './format/insertEntity';\r\nexport { default as insertImage } from './format/insertImage';\r\nexport { default as insertTable } from './table/insertTable';\r\nexport { default as editTable } from './table/editTable';\r\nexport { default as formatTable } from './table/formatTable';\r\nexport { default as removeLink } from './format/removeLink';\r\nexport { default as replaceWithNode } from './format/replaceWithNode';\r\nexport { default as rotateElement } from './format/rotateElement';\r\nexport { default as setAlignment } from './format/setAlignment';\r\nexport { default as setBackgroundColor } from './format/setBackgroundColor';\r\nexport { default as setTextColor } from './format/setTextColor';\r\nexport { default as setDirection } from './format/setDirection';\r\nexport { default as setFontName } from './format/setFontName';\r\nexport { default as setFontSize } from './format/setFontSize';\r\nexport { default as setImageAltText } from './format/setImageAltText';\r\nexport { default as setIndentation } from './format/setIndentation';\r\nexport { default as changeCapitalization } from './format/changeCapitalization';\r\nexport { default as toggleBold } from './format/toggleBold';\r\nexport { default as toggleBullet } from './format/toggleBullet';\r\nexport { default as toggleItalic } from './format/toggleItalic';\r\nexport { default as toggleNumbering } from './format/toggleNumbering';\r\nexport { default as setOrderedListNumbering } from './format/setOrderedListNumbering';\r\nexport { default as toggleBlockQuote } from './format/toggleBlockQuote';\r\nexport { default as toggleCodeBlock } from './format/toggleCodeBlock';\r\nexport { default as toggleStrikethrough } from './format/toggleStrikethrough';\r\nexport { default as toggleSubscript } from './format/toggleSubscript';\r\nexport { default as toggleSuperscript } from './format/toggleSuperscript';\r\nexport { default as toggleUnderline } from './format/toggleUnderline';\r\nexport { default as toggleHeader } from './format/toggleHeader';\r\n\r\nexport { default as blockFormat } from './utils/blockFormat';\r\nexport { default as experimentCommitListChains } from './experiment/experimentCommitListChains';\r\n","import isVoidHtmlElement from '../utils/isVoidHtmlElement';\r\nimport Position from './Position';\r\nimport safeInstanceOf from '../utils/safeInstanceOf';\r\nimport { NodePosition, NodeType, PositionType } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Create a range around the given node(s)\r\n * @param startNode The start node to create range from\r\n * @param endNode The end node to create range from. If specified, the range will start before startNode and\r\n * end after endNode, otherwise, the range will start before and end after the start node\r\n * @returns A range start before the given node and end after the given node\r\n */\r\nexport default function createRange(startNode: Node, endNode?: Node): Range;\r\n\r\n/**\r\n * Create a collapsed range at the given node and offset\r\n * @param node The container node of the range\r\n * @param offset The offset of the range, can be a number or value of PositionType\r\n * @returns A range at the given node and offset\r\n */\r\nexport default function createRange(node: Node, offset: number | PositionType): Range;\r\n\r\n/**\r\n * Create a range with the given start/end container node and offset\r\n * @param startNode The start container node of the range\r\n * @param startOffset The start offset of the range\r\n * @param endNode The end container node of the range\r\n * @param endOffset The end offset of the range\r\n * @returns A range at the given start/end container node and offset\r\n */\r\nexport default function createRange(\r\n startNode: Node,\r\n startOffset: number | PositionType,\r\n endNode: Node,\r\n endOffset: number | PositionType\r\n): Range;\r\n\r\n/**\r\n * Create a range under the given rootNode with start and end selection paths\r\n * @param rootNode The root node that the selection paths start from\r\n * @param startPath The selection path of the start position of the range\r\n * @param endPath The selection path of the end position of the range\r\n * @returns A range with the given start and end selection paths\r\n */\r\nexport default function createRange(rootNode: Node, startPath: number[], endPath?: number[]): Range;\r\n\r\n/**\r\n * Create a range with the start and end position\r\n * @param startPosition The start position of the range\r\n * @param endPosition The end position of the range, if not specified, the range will be collapsed at start position\r\n * @returns A range start at startPosition, end at endPosition, or startPosition when endPosition is not specified\r\n */\r\nexport default function createRange(startPosition: NodePosition, endPosition?: NodePosition): Range;\r\n\r\nexport default function createRange(\r\n arg1: Node | NodePosition,\r\n arg2?: number | number[] | Node | NodePosition,\r\n arg3?: Node | number[],\r\n arg4?: number\r\n): Range {\r\n let start: NodePosition;\r\n let end: NodePosition;\r\n\r\n if (isNodePosition(arg1)) {\r\n // function createRange(startPosition: NodePosition, endPosition?: NodePosition): Range;\r\n start = arg1;\r\n end = isNodePosition(arg2) ? arg2 : null;\r\n } else if (safeInstanceOf(arg1, 'Node')) {\r\n if (Array.isArray(arg2)) {\r\n // function createRange(rootNode: Node, startPath: number[], endPath?: number[]): Range;\r\n start = getPositionFromPath(arg1, arg2);\r\n end = Array.isArray(arg3) ? getPositionFromPath(arg1, arg3) : null;\r\n } else if (typeof arg2 == 'number') {\r\n // function createRange(node: Node, offset: number | PositionType): Range;\r\n // function createRange(startNode: Node, startOffset: number | PositionType, endNode: Node, endOffset: number | PositionType): Range;\r\n start = new Position(arg1, arg2);\r\n end = safeInstanceOf(arg3, 'Node') ? new Position(arg3, arg4) : null;\r\n } else if (safeInstanceOf(arg2, 'Node') || !arg2) {\r\n // function createRange(startNode: Node, endNode?: Node): Range;\r\n start = new Position(arg1, PositionType.Before);\r\n end = new Position(arg2 || arg1, PositionType.After);\r\n }\r\n }\r\n\r\n if (start && start.node) {\r\n let range = start.node.ownerDocument.createRange();\r\n start = getFocusablePosition(start);\r\n end = getFocusablePosition(end || start);\r\n range.setStart(start.node, start.offset);\r\n range.setEnd(end.node, end.offset);\r\n\r\n return range;\r\n } else {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Convert to focusable position\r\n * If current node is a void element, we need to move up one level to put cursor outside void element\r\n */\r\nfunction getFocusablePosition(position: NodePosition) {\r\n return position.node.nodeType == NodeType.Element && isVoidHtmlElement(position.node)\r\n ? new Position(position.node, position.isAtEnd ? PositionType.After : PositionType.Before)\r\n : position;\r\n}\r\n\r\nfunction isNodePosition(arg: any): arg is NodePosition {\r\n return arg && arg.node;\r\n}\r\n\r\nfunction getPositionFromPath(node: Node, path: number[]): NodePosition {\r\n if (!node || !path) {\r\n return null;\r\n }\r\n\r\n // Iterate with a for loop to avoid mutating the passed in element path stack\r\n // or needing to copy it.\r\n let offset: number;\r\n\r\n for (let i = 0; i < path.length; i++) {\r\n offset = path[i];\r\n if (\r\n i < path.length - 1 &&\r\n node &&\r\n node.nodeType == NodeType.Element &&\r\n node.childNodes.length > offset\r\n ) {\r\n node = node.childNodes[offset];\r\n } else {\r\n break;\r\n }\r\n }\r\n\r\n return new Position(node, offset);\r\n}\r\n","import { ChangeSource, DocumentCommand, IEditor, PluginEventType } from 'roosterjs-editor-types';\r\nimport { PendableFormatCommandMap, PendableFormatNames } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * @internal\r\n * Execute a document command\r\n * @param editor The editor instance\r\n * @param command The command to execute\r\n * @param addUndoSnapshotWhenCollapsed Optional, set to true to always add undo snapshot even current selection is collapsed.\r\n * Default value is false.\r\n * @param doWorkaroundForList Optional, set to true to do workaround for list in order to keep current format.\r\n * Default value is false.\r\n */\r\nexport default function execCommand(editor: IEditor, command: DocumentCommand) {\r\n editor.focus();\r\n\r\n let formatter = () => editor.getDocument().execCommand(command, false, null);\r\n\r\n let range = editor.getSelectionRange();\r\n if (range && range.collapsed) {\r\n editor.addUndoSnapshot();\r\n const formatState = editor.getPendableFormatState(false /* forceGetStateFromDom */);\r\n formatter();\r\n const formatName = Object.keys(PendableFormatCommandMap).filter(\r\n (x: PendableFormatNames) => PendableFormatCommandMap[x] == command\r\n )[0] as PendableFormatNames;\r\n\r\n if (formatName) {\r\n formatState[formatName] = !formatState[formatName];\r\n editor.triggerPluginEvent(PluginEventType.PendingFormatStateChanged, {\r\n formatState: formatState,\r\n });\r\n }\r\n } else {\r\n editor.addUndoSnapshot(formatter, ChangeSource.Format);\r\n }\r\n}\r\n","import createElement from './createElement';\r\nimport fromHtml from './fromHtml';\r\nimport safeInstanceOf from '../utils/safeInstanceOf';\r\nimport { CreateElementData, KnownCreateElementDataIndex } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Wrap all the node with html and return the wrapped node, and put the wrapper node under the parent of the first node\r\n * @param nodes The node or node array to wrap\r\n * @param wrapper The wrapper HTML tag name\r\n * @returns The wrapper element\r\n */\r\nexport default function wrap(\r\n nodes: Node | Node[],\r\n wrapper?: T\r\n): HTMLElementTagNameMap[T];\r\n\r\n/**\r\n * @deprecated\r\n * Wrap all the node with html and return the wrapped node, and put the wrapper node under the parent of the first node\r\n * @param nodes The node or node array to wrap\r\n * @param wrapper The wrapper HTML string, default value is DIV\r\n * @returns The wrapper element\r\n */\r\nexport default function wrap(nodes: Node | Node[], wrapper?: string): HTMLElement;\r\n\r\n/**\r\n * Wrap all the node with html and return the wrapped node, and put the wrapper node under the parent of the first node\r\n * @param nodes The node or node array to wrap\r\n * @param wrapper The wrapper HTML element, default value is a new DIV element\r\n * @returns The wrapper element\r\n */\r\nexport default function wrap(nodes: Node | Node[], wrapper?: HTMLElement): HTMLElement;\r\n\r\nexport default function wrap(\r\n nodes: Node | Node[],\r\n wrapper?: CreateElementData | KnownCreateElementDataIndex\r\n): HTMLElement;\r\n\r\nexport default function wrap(\r\n nodes: Node | Node[],\r\n wrapper?: string | HTMLElement | CreateElementData | KnownCreateElementDataIndex\r\n): HTMLElement {\r\n nodes = !nodes ? [] : safeInstanceOf(nodes, 'Node') ? [nodes] : nodes;\r\n if (nodes.length == 0 || !nodes[0]) {\r\n return null;\r\n }\r\n\r\n if (!wrapper) {\r\n wrapper = 'div';\r\n }\r\n\r\n if (!safeInstanceOf(wrapper, 'HTMLElement')) {\r\n let document = nodes[0].ownerDocument;\r\n\r\n if (typeof wrapper === 'string') {\r\n wrapper = /^\\w+$/.test(wrapper)\r\n ? document.createElement(wrapper)\r\n : (fromHtml(wrapper, document)[0] as HTMLElement); // This will be removed in next major release\r\n } else {\r\n wrapper = createElement(wrapper, document) as HTMLElement;\r\n }\r\n }\r\n\r\n let parentNode = nodes[0].parentNode;\r\n\r\n if (parentNode) {\r\n parentNode.insertBefore(wrapper, nodes[0]);\r\n }\r\n\r\n for (let node of nodes) {\r\n wrapper.appendChild(node);\r\n }\r\n\r\n return wrapper;\r\n}\r\n","import toArray from './toArray';\r\nimport { DocumentPosition, NodeType, QueryScope } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Query HTML elements in the container by a selector string\r\n * @param container Container element to query from\r\n * @param selector Selector string to query\r\n * @param forEachCallback An optional callback to be invoked on each node in query result\r\n * @param scope The scope of the query, default value is QueryScope.Body\r\n * @param range The selection range to query with. This is required when scope is not Body\r\n * @returns HTML Element array of the query result\r\n */\r\nexport default function queryElements(\r\n container: ParentNode,\r\n selector: string,\r\n forEachCallback?: (node: HTMLElement) => any,\r\n scope: QueryScope = QueryScope.Body,\r\n range?: Range\r\n): HTMLElement[] {\r\n if (!container || !selector) {\r\n return [];\r\n }\r\n\r\n let elements = toArray(container.querySelectorAll(selector));\r\n\r\n if (scope != QueryScope.Body && range) {\r\n let { startContainer, startOffset, endContainer, endOffset } = range;\r\n if (startContainer.nodeType == NodeType.Element && startContainer.firstChild) {\r\n const child = startContainer.childNodes[startOffset];\r\n\r\n // range.startOffset can give a value of child.length+1 when selection is after the last child\r\n // In that case we will use the last child instead\r\n startContainer = child || startContainer.lastChild;\r\n }\r\n\r\n endContainer =\r\n endContainer.nodeType == NodeType.Element && endContainer.firstChild && endOffset > 0\r\n ? endContainer.childNodes[endOffset - 1]\r\n : endContainer;\r\n\r\n elements = elements.filter(element =>\r\n isIntersectWithNodeRange(\r\n element,\r\n startContainer,\r\n endContainer,\r\n scope == QueryScope.InSelection\r\n )\r\n );\r\n }\r\n\r\n if (forEachCallback) {\r\n elements.forEach(forEachCallback);\r\n }\r\n return elements;\r\n}\r\n\r\nfunction isIntersectWithNodeRange(\r\n node: Node,\r\n startNode: Node,\r\n endNode: Node,\r\n nodeContainedByRangeOnly: boolean\r\n): boolean {\r\n let startPosition = node.compareDocumentPosition(startNode);\r\n let endPosition = node.compareDocumentPosition(endNode);\r\n let targetPositions = [DocumentPosition.Same, DocumentPosition.Contains];\r\n\r\n if (!nodeContainedByRangeOnly) {\r\n targetPositions.push(DocumentPosition.ContainedBy);\r\n }\r\n\r\n return (\r\n checkPosition(startPosition, targetPositions) || // intersectStart\r\n checkPosition(endPosition, targetPositions) || // intersectEnd\r\n (checkPosition(startPosition, [DocumentPosition.Preceding]) && // Contains\r\n checkPosition(endPosition, [DocumentPosition.Following]) &&\r\n !checkPosition(endPosition, [DocumentPosition.ContainedBy]))\r\n );\r\n}\r\n\r\nfunction checkPosition(position: DocumentPosition, targets: DocumentPosition[]): boolean {\r\n return targets.some(target =>\r\n target == DocumentPosition.Same\r\n ? position == DocumentPosition.Same\r\n : (position & target) == target\r\n );\r\n}\r\n","import isNodeAfter from './isNodeAfter';\r\n\r\n/**\r\n * Split parent node of the given node before/after the given node.\r\n * When a parent node contains [A,B,C] and pass B as the given node,\r\n * If split before, the new nodes will be [A][B,C] and returns [A];\r\n * otherwise, it will be [A,B][C] and returns [C].\r\n * @param node The node to split before/after\r\n * @param splitBefore Whether split before or after\r\n * @param removeEmptyNewNode If the new node is empty (even then only child is space or ZER_WIDTH_SPACE),\r\n * we remove it. @default false\r\n * @returns The new parent node\r\n */\r\nexport default function splitParentNode(node: Node, splitBefore: boolean): Node {\r\n if (!node || !node.parentNode) {\r\n return null;\r\n }\r\n\r\n let parentNode = node.parentNode;\r\n let newParent = parentNode.cloneNode(false /*deep*/) as HTMLElement;\r\n newParent.removeAttribute('id');\r\n if (splitBefore) {\r\n while (parentNode.firstChild && parentNode.firstChild != node) {\r\n newParent.appendChild(parentNode.firstChild);\r\n }\r\n } else {\r\n while (node.nextSibling) {\r\n newParent.appendChild(node.nextSibling);\r\n }\r\n }\r\n\r\n // When the only child of new parent is ZERO_WIDTH_SPACE, we can still prevent keeping it by set removeEmptyNewNode to true\r\n if (newParent.firstChild && newParent.innerHTML != '') {\r\n parentNode.parentNode.insertBefore(\r\n newParent,\r\n splitBefore ? parentNode : parentNode.nextSibling\r\n );\r\n } else {\r\n newParent = null;\r\n }\r\n\r\n return newParent;\r\n}\r\n\r\n/**\r\n * Split parent node by a balanced node range\r\n * @param nodes The nodes to split from. If only one node is passed, split it from all its siblings.\r\n * If two or nodes are passed, will split before the first one and after the last one, all other nodes will be ignored\r\n * @returns The parent node of the given node range if the given nodes are balanced, otherwise null\r\n */\r\nexport function splitBalancedNodeRange(nodes: Node | Node[]): HTMLElement {\r\n let start = Array.isArray(nodes) ? nodes[0] : nodes;\r\n let end = Array.isArray(nodes) ? nodes[nodes.length - 1] : nodes;\r\n let parentNode = start && end && start.parentNode == end.parentNode ? start.parentNode : null;\r\n if (parentNode) {\r\n if (isNodeAfter(start, end)) {\r\n let temp = end;\r\n end = start;\r\n start = temp;\r\n }\r\n splitParentNode(start, true /*splitBefore*/);\r\n splitParentNode(end, false /*splitBefore*/);\r\n }\r\n\r\n return parentNode as HTMLElement;\r\n}\r\n","import { DocumentPosition } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Checks if node1 is after node2\r\n * @param node1 The node to check if it is after another node\r\n * @param node2 The node to check if another node is after this one\r\n * @returns True if node1 is after node2, otherwise false\r\n */\r\nexport default function isNodeAfter(node1: Node, node2: Node): boolean {\r\n return !!(\r\n node1 &&\r\n node2 &&\r\n (node2.compareDocumentPosition(node1) & DocumentPosition.Following) ==\r\n DocumentPosition.Following\r\n );\r\n}\r\n","import contains from './contains';\r\nimport matchesSelector from './matchesSelector';\r\nimport { NodeType } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Find closest element ancestor start from the given node which matches the given selector\r\n * @param node Find ancestor start from this node\r\n * @param root Root node where the search should stop at. The return value can never be this node\r\n * @param selector The expected selector. If null, return the first HTML Element found from start node\r\n * @returns An HTML element which matches the given selector. If the given start node matches the selector,\r\n * returns the given node\r\n */\r\nexport default function findClosestElementAncestor(\r\n node: Node,\r\n root?: Node,\r\n selector?: string\r\n): HTMLElement {\r\n node = !node ? null : node.nodeType == NodeType.Element ? node : node.parentNode;\r\n let element = node && node.nodeType == NodeType.Element ? node : null;\r\n\r\n if (element && selector) {\r\n if (element.closest) {\r\n element = element.closest(selector) as HTMLElement;\r\n } else {\r\n while (element && element != root && !matchesSelector(element, selector)) {\r\n element = element.parentElement;\r\n }\r\n }\r\n }\r\n\r\n return !root || contains(root, element) ? element : null;\r\n}\r\n","import getBlockElementAtNode from '../blockElements/getBlockElementAtNode';\r\nimport getTagOfNode from '../utils/getTagOfNode';\r\nimport ImageInlineElement from './ImageInlineElement';\r\nimport LinkInlineElement from './LinkInlineElement';\r\nimport NodeInlineElement from './NodeInlineElement';\r\nimport safeInstanceOf from '../utils/safeInstanceOf';\r\nimport { BlockElement, InlineElement } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Get the inline element at a node\r\n * @param rootNode The root node of current scope\r\n * @param node The node to get InlineElement from\r\n */\r\nexport default function getInlineElementAtNode(rootNode: Node, node: Node): InlineElement;\r\n\r\n/**\r\n * Get the inline element at a node\r\n * @param parentBlock Parent BlockElement of this node\r\n * @param node The node to get InlineElement from\r\n */\r\nexport default function getInlineElementAtNode(\r\n parentBlock: BlockElement,\r\n node: Node\r\n): InlineElement;\r\n\r\nexport default function getInlineElementAtNode(\r\n parent: Node | BlockElement,\r\n node: Node\r\n): InlineElement {\r\n // An inline element has to be in a block element, get the block first and then resolve through the factory\r\n let parentBlock = safeInstanceOf(parent, 'Node') ? getBlockElementAtNode(parent, node) : parent;\r\n return node && parentBlock && resolveInlineElement(node, parentBlock);\r\n}\r\n\r\n/**\r\n * Resolve an inline element by a leaf node\r\n * @param node The node to resolve from\r\n * @param parentBlock The parent block element\r\n */\r\nfunction resolveInlineElement(node: Node, parentBlock: BlockElement): InlineElement {\r\n let nodeChain = [node];\r\n for (\r\n let parent = node.parentNode;\r\n parent && parentBlock.contains(parent);\r\n parent = parent.parentNode\r\n ) {\r\n nodeChain.push(parent);\r\n }\r\n\r\n let inlineElement: InlineElement;\r\n\r\n for (let i = nodeChain.length - 1; i >= 0 && !inlineElement; i--) {\r\n let currentNode = nodeChain[i];\r\n let tag = getTagOfNode(currentNode);\r\n if (tag == 'A') {\r\n inlineElement = new LinkInlineElement(currentNode, parentBlock);\r\n } else if (tag == 'IMG') {\r\n inlineElement = new ImageInlineElement(currentNode, parentBlock);\r\n }\r\n }\r\n\r\n return inlineElement || new NodeInlineElement(node, parentBlock);\r\n}\r\n","import { applyTextStyle, getTagOfNode } from 'roosterjs-editor-dom';\r\nimport { ChangeSource, IEditor, NodeType, PositionType } from 'roosterjs-editor-types';\r\n\r\nconst ZERO_WIDTH_SPACE = '\\u200B';\r\n\r\n/**\r\n * @internal\r\n * Apply inline style to current selection\r\n * @param editor The editor instance\r\n * @param callback The callback function to apply style\r\n */\r\nexport default function applyInlineStyle(\r\n editor: IEditor,\r\n callback: (element: HTMLElement, isInnerNode?: boolean) => any\r\n) {\r\n editor.focus();\r\n let range = editor.getSelectionRange();\r\n\r\n if (range && range.collapsed) {\r\n let node = range.startContainer;\r\n let isEmptySpan =\r\n getTagOfNode(node) == 'SPAN' &&\r\n (!node.firstChild ||\r\n (getTagOfNode(node.firstChild) == 'BR' && !node.firstChild.nextSibling));\r\n if (isEmptySpan) {\r\n editor.addUndoSnapshot();\r\n callback(node as HTMLElement);\r\n } else {\r\n let isZWSNode =\r\n node &&\r\n node.nodeType == NodeType.Text &&\r\n node.nodeValue == ZERO_WIDTH_SPACE &&\r\n getTagOfNode(node.parentNode) == 'SPAN';\r\n\r\n if (!isZWSNode) {\r\n editor.addUndoSnapshot();\r\n // Create a new text node to hold the selection.\r\n // Some content is needed to position selection into the span\r\n // for here, we inject ZWS - zero width space\r\n node = editor.getDocument().createTextNode(ZERO_WIDTH_SPACE);\r\n range.insertNode(node);\r\n }\r\n\r\n applyTextStyle(node, callback);\r\n editor.select(node, PositionType.End);\r\n }\r\n } else {\r\n // This is start and end node that get the style. The start and end needs to be recorded so that selection\r\n // can be re-applied post-applying style\r\n editor.addUndoSnapshot(() => {\r\n let firstNode: Node;\r\n let lastNode: Node;\r\n let contentTraverser = editor.getSelectionTraverser();\r\n let inlineElement = contentTraverser && contentTraverser.currentInlineElement;\r\n while (inlineElement) {\r\n let nextInlineElement = contentTraverser.getNextInlineElement();\r\n inlineElement.applyStyle((element, isInnerNode) => {\r\n callback(element, isInnerNode);\r\n firstNode = firstNode || element;\r\n lastNode = element;\r\n });\r\n inlineElement = nextInlineElement;\r\n }\r\n if (firstNode && lastNode) {\r\n editor.select(firstNode, PositionType.Before, lastNode, PositionType.After);\r\n }\r\n }, ChangeSource.Format);\r\n }\r\n}\r\n","import contains from './contains';\r\nimport splitParentNode from './splitParentNode';\r\nimport toArray from './toArray';\r\n\r\n/**\r\n * Collapse nodes within the given start and end nodes to their common ancestor node,\r\n * split parent nodes if necessary\r\n * @param root The root node of the scope\r\n * @param start The start node\r\n * @param end The end node\r\n * @param canSplitParent True to allow split parent node there are nodes before start or after end under the same parent\r\n * and the returned nodes will be all nodes from start through end after splitting\r\n * False to disallow split parent\r\n * @returns When canSplitParent is true, returns all node from start through end after splitting,\r\n * otherwise just return start and end\r\n */\r\nexport default function collapseNodes(\r\n root: Node,\r\n start: Node,\r\n end: Node,\r\n canSplitParent: boolean\r\n): Node[] {\r\n if (!contains(root, start) || !contains(root, end)) {\r\n return [];\r\n }\r\n\r\n start = collapse(root, start, end, true /*isStart*/, canSplitParent);\r\n end = collapse(root, end, start, false /*isStart*/, canSplitParent);\r\n\r\n if (contains(start, end, true /*treatSameNodeAsContain*/)) {\r\n return [start];\r\n } else if (contains(end, start)) {\r\n return [end];\r\n } else if (start.parentNode == end.parentNode) {\r\n let nodes: Node[] = toArray(start.parentNode.childNodes);\r\n let startIndex = nodes.indexOf(start);\r\n let endIndex = nodes.indexOf(end);\r\n return nodes.slice(startIndex, endIndex + 1);\r\n } else {\r\n return [start, end];\r\n }\r\n}\r\n\r\n/**\r\n * Collapse a node by traversing its parent nodes until we get the common ancestor node of node and ref node\r\n * @param root Root node, traversing will be limited under this scope\r\n * @param node The node to collapse\r\n * @param ref Ref node. The result will be the nearest common ancestor node of the given node and this ref node\r\n * @param isStart Whether the given node is start of the sequence of nodes to collapse\r\n * @param canSplitParent Whether splitting parent node is allowed\r\n * @returns The common ancestor node of the given node ref node\r\n */\r\nexport function collapse(\r\n root: Node,\r\n node: Node,\r\n ref: Node,\r\n isStart: boolean,\r\n canSplitParent: boolean\r\n): Node {\r\n while (node.parentNode != root && !contains(node.parentNode, ref)) {\r\n if ((isStart && node.previousSibling) || (!isStart && node.nextSibling)) {\r\n if (!canSplitParent) {\r\n break;\r\n }\r\n splitParentNode(node, isStart);\r\n }\r\n node = node.parentNode;\r\n }\r\n return node;\r\n}\r\n","import getTagOfNode from './getTagOfNode';\r\n\r\nconst BLOCK_ELEMENT_TAGS = 'ADDRESS,ARTICLE,ASIDE,BLOCKQUOTE,CANVAS,DD,DIV,DL,DT,FIELDSET,FIGCAPTION,FIGURE,FOOTER,FORM,H1,H2,H3,H4,H5,H6,HEADER,HR,LI,MAIN,NAV,NOSCRIPT,OL,OUTPUT,P,PRE,SECTION,TABLE,TD,TH,TFOOT,UL,VIDEO'.split(\r\n ','\r\n);\r\nconst BLOCK_DISPLAY_STYLES = ['block', 'list-item', 'table-cell'];\r\n\r\n/**\r\n * Checks if the node is a block like element. Block like element are usually those P, DIV, LI, TD etc.\r\n * @param node The node to check\r\n * @returns True if the node is a block element, otherwise false\r\n */\r\nexport default function isBlockElement(node: Node): node is HTMLElement {\r\n let tag = getTagOfNode(node);\r\n return !!(\r\n tag &&\r\n (BLOCK_DISPLAY_STYLES.indexOf((node).style.display) >= 0 ||\r\n BLOCK_ELEMENT_TAGS.indexOf(tag) >= 0)\r\n );\r\n}\r\n","import safeInstanceOf from './safeInstanceOf';\nimport { Browser } from './Browser';\nimport { CreateElementData, KnownCreateElementDataIndex } from 'roosterjs-editor-types';\n\nexport const KnownCreateElementData: Record = {\n [KnownCreateElementDataIndex.None]: null,\n\n // Edge can sometimes lose current format when Enter to new line.\n // So here we add an extra SPAN for Edge to workaround this bug\n [KnownCreateElementDataIndex.EmptyLine]: Browser.isEdge\n ? { tag: 'div', children: [{ tag: 'span', children: [{ tag: 'br' }] }] }\n : { tag: 'div', children: [{ tag: 'br' }] },\n [KnownCreateElementDataIndex.BlockquoteWrapper]: {\n tag: 'blockquote',\n style: 'margin-top:0;margin-bottom:0',\n },\n [KnownCreateElementDataIndex.CopyPasteTempDiv]: {\n tag: 'div',\n style:\n 'width: 1px; height: 1px; overflow: hidden; position: fixed; top: 0; left; 0; -webkit-user-select: text',\n attributes: {\n contenteditable: 'true',\n },\n },\n [KnownCreateElementDataIndex.BlockListItem]: { tag: 'li', style: 'display:block' },\n [KnownCreateElementDataIndex.ContextMenuWrapper]: {\n tag: 'div',\n style: 'position: fixed; width: 0; height: 0',\n },\n [KnownCreateElementDataIndex.ImageEditWrapper]: {\n tag: 'div',\n style: 'width:100%;height:100%;position:relative;overflow:hidden',\n },\n [KnownCreateElementDataIndex.TableHorizontalResizer]: {\n tag: 'div',\n style: 'position: fixed; cursor: row-resize; user-select: none',\n },\n [KnownCreateElementDataIndex.TableVerticalResizer]: {\n tag: 'div',\n style: 'position: fixed; cursor: col-resize; user-select: none',\n },\n [KnownCreateElementDataIndex.TableResizerLTR]: {\n tag: 'div',\n style: 'position: fixed; cursor: nw-resize; user-select: none; border: 1px solid #808080',\n },\n [KnownCreateElementDataIndex.TableResizerRTL]: {\n tag: 'div',\n style: 'position: fixed; cursor: ne-resize; user-select: none; border: 1px solid #808080',\n },\n};\n\nexport default function createElement(\n elementData: CreateElementData | KnownCreateElementDataIndex,\n document: Document\n): Element {\n if (typeof elementData == 'number') {\n elementData = KnownCreateElementData[elementData];\n }\n\n if (!elementData || !elementData.tag) {\n return null;\n }\n\n const { tag, namespace, className, style, dataset, attributes, children } = elementData;\n const result = namespace\n ? document.createElementNS(namespace, tag)\n : document.createElement(tag);\n\n if (style) {\n result.setAttribute('style', style);\n }\n\n if (className) {\n result.className = className;\n }\n\n if (dataset && safeInstanceOf(result, 'HTMLElement')) {\n Object.keys(dataset).forEach(datasetName => {\n result.dataset[datasetName] = dataset[datasetName];\n });\n }\n\n if (attributes) {\n Object.keys(attributes).forEach(attrName => {\n result.setAttribute(attrName, attributes[attrName]);\n });\n }\n\n if (children) {\n children.forEach(child => {\n if (typeof child === 'string') {\n result.appendChild(document.createTextNode(child));\n } else if (child) {\n result.appendChild(createElement(child, document));\n }\n });\n }\n\n return result;\n}\n","import { BrowserInfo } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Get current browser information from user agent string\r\n * @param userAgent The userAgent string of a browser\r\n * @param appVersion The appVersion string of a browser\r\n * @returns The BrowserInfo object calculated from the given userAgent and appVersion\r\n */\r\nexport function getBrowserInfo(userAgent: string, appVersion: string): BrowserInfo {\r\n // checks whether the browser is running in IE\r\n // IE11 will use rv in UA instead of MSIE. Unfortunately Firefox also uses this. We should also look for \"Trident\" to confirm this.\r\n // There have been cases where companies using older version of IE and custom UserAgents have broken this logic (e.g. IE 10 and KellyServices)\r\n // therefore we should check that the Trident/rv combo is not just from an older IE browser\r\n let isIE11OrGreater = userAgent.indexOf('rv:') != -1 && userAgent.indexOf('Trident') != -1;\r\n let isIE = userAgent.indexOf('MSIE') != -1 || isIE11OrGreater;\r\n\r\n // IE11+ may also have 'Chrome', 'Firefox' and 'Safari' in user agent. But it will have 'trident' as well\r\n let isChrome = false;\r\n let isFirefox = false;\r\n let isSafari = false;\r\n let isEdge = false;\r\n let isWebKit = userAgent.indexOf('WebKit') != -1;\r\n\r\n if (!isIE) {\r\n isChrome = userAgent.indexOf('Chrome') != -1;\r\n isFirefox = userAgent.indexOf('Firefox') != -1;\r\n if (userAgent.indexOf('Safari') != -1) {\r\n // Android and Chrome have Safari in the user string\r\n isSafari = userAgent.indexOf('Chrome') == -1 && userAgent.indexOf('Android') == -1;\r\n }\r\n\r\n // Sample Edge UA: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10121\r\n isEdge = userAgent.indexOf('Edge') != -1;\r\n\r\n // When it is edge, it should not be chrome or firefox. and it is also not webkit\r\n if (isEdge) {\r\n isWebKit = isChrome = isFirefox = false;\r\n }\r\n }\r\n\r\n let isMac = appVersion.indexOf('Mac') != -1;\r\n let isWin = appVersion.indexOf('Win') != -1 || appVersion.indexOf('NT') != -1;\r\n\r\n return {\r\n isMac,\r\n isWin,\r\n isWebKit,\r\n isIE,\r\n isIE11OrGreater,\r\n isSafari,\r\n isChrome,\r\n isFirefox,\r\n isEdge,\r\n isIEOrEdge: isIE || isEdge,\r\n };\r\n}\r\n\r\n/**\r\n * Browser object contains browser and operating system information of current environment\r\n */\r\nexport const Browser = window\r\n ? getBrowserInfo(window.navigator.userAgent, window.navigator.appVersion)\r\n : {};\r\n","import getTagOfNode from './getTagOfNode';\r\nimport { getComputedStyle } from './getComputedStyles';\r\nimport { NodeType } from 'roosterjs-editor-types';\r\n\r\nconst CRLF = /^[\\r\\n]+$/gm;\r\nconst CRLF_SPACE = /[\\t\\r\\n\\u0020\\u200B]/gm; // We should only find new line, real space or ZeroWidthSpace (TAB, %20, but not  )\r\n\r\n/**\r\n * Skip a node when any of following conditions are true\r\n * - it is neither Element nor Text\r\n * - it is a text node but is empty\r\n * - it is a text node but contains just CRLF (noisy text node that often comes in-between elements)\r\n * - has a display:none\r\n * - it is just
\r\n * @param node The node to check\r\n * @param ignoreSpace (Optional) True to ignore pure space text node of the node when check.\r\n * If the value of a node value is only space, set this to true will treat this node can be skipped.\r\n * Default value is false\r\n */\r\nexport default function shouldSkipNode(node: Node, ignoreSpace?: boolean): boolean {\r\n if (node.nodeType == NodeType.Text) {\r\n if (!node.nodeValue || node.textContent == '' || CRLF.test(node.nodeValue)) {\r\n return true;\r\n } else if (ignoreSpace && node.nodeValue.replace(CRLF_SPACE, '') == '') {\r\n return true;\r\n } else {\r\n return false;\r\n }\r\n } else if (node.nodeType == NodeType.Element) {\r\n if (getComputedStyle(node, 'display') == 'none') {\r\n return true;\r\n }\r\n\r\n const tag = getTagOfNode(node);\r\n\r\n if (tag == 'DIV' || tag == 'SPAN') {\r\n // Empty SPAN/DIV or SPAN/DIV with only meaningless children is meaningless,\r\n // because it can render nothing. If we keep them here, there may be unexpected\r\n // LI elements added for those meaningless nodes.\r\n for (let child = node.firstChild; !!child; child = child.nextSibling) {\r\n if (!shouldSkipNode(child, ignoreSpace)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n } else {\r\n // There may still be other cases that the node is not meaningful.\r\n // We can add those cases here once we hit them.\r\n return false;\r\n }\r\n } else {\r\n return true;\r\n }\r\n}\r\n","import getComputedStyles from './getComputedStyles';\r\nimport getTagOfNode from './getTagOfNode';\r\nimport moveChildNodes from './moveChildNodes';\r\n\r\n/**\r\n * Change tag of an HTML Element to a new one, and replace it from DOM tree\r\n * @param element The element to change tag\r\n * @param newTag New tag to change to\r\n * @returns The new element with new tag\r\n */\r\nexport default function changeElementTag(\r\n element: HTMLElement,\r\n newTag: K\r\n): HTMLElementTagNameMap[K];\r\n\r\n/**\r\n * Change tag of an HTML Element to a new one, and replace it from DOM tree\r\n * @param element The element to change tag\r\n * @param newTag New tag to change to\r\n * @returns The new element with new tag\r\n */\r\nexport default function changeElementTag(element: HTMLElement, newTag: string): HTMLElement;\r\n\r\nexport default function changeElementTag(element: HTMLElement, newTag: string): HTMLElement {\r\n if (!element || !newTag) {\r\n return null;\r\n }\r\n\r\n let newElement = element.ownerDocument.createElement(newTag);\r\n\r\n for (let i = 0; i < element.attributes.length; i++) {\r\n let attr = element.attributes[i];\r\n newElement.setAttribute(attr.name, attr.value);\r\n }\r\n\r\n moveChildNodes(newElement, element);\r\n\r\n if (getTagOfNode(element) == 'P' || getTagOfNode(newElement) == 'P') {\r\n [newElement.style.marginTop, newElement.style.marginBottom] = getComputedStyles(element, [\r\n 'margin-top',\r\n 'margin-bottom',\r\n ]);\r\n }\r\n\r\n if (element.parentNode) {\r\n element.parentNode.replaceChild(newElement, element);\r\n }\r\n\r\n return newElement;\r\n}\r\n","import contains from '../utils/contains';\nimport { DocumentPosition, RegionBase } from 'roosterjs-editor-types';\n\n/**\n * Check if a given node is contained by the given region\n * @param region The region to check from\n * @param node The node or block element to check\n */\nexport default function isNodeInRegion(region: RegionBase, node: Node): boolean {\n return !!(\n region &&\n contains(region.rootNode, node) &&\n (!region.nodeBefore ||\n region.nodeBefore.compareDocumentPosition(node) == DocumentPosition.Following) &&\n (!region.nodeAfter ||\n region.nodeAfter.compareDocumentPosition(node) == DocumentPosition.Preceding)\n );\n}\n","import experimentCommitListChains from '../experiment/experimentCommitListChains';\nimport { ChangeSource, IEditor, NodePosition, Region } from 'roosterjs-editor-types';\nimport { VListChain } from 'roosterjs-editor-dom';\n\n/**\n * Split selection into regions, and perform a block-wise formatting action for each region.\n */\nexport default function blockFormat(\n editor: IEditor,\n callback: (\n region: Region,\n start: NodePosition,\n end: NodePosition,\n chains: VListChain[]\n ) => void,\n beforeRunCallback?: () => boolean\n) {\n editor.focus();\n editor.addUndoSnapshot((start, end) => {\n if (!beforeRunCallback || beforeRunCallback()) {\n const regions = editor.getSelectedRegions();\n const chains = VListChain.createListChains(regions, start?.node);\n regions.forEach(region => callback(region, start, end, chains));\n experimentCommitListChains(editor, chains);\n }\n editor.select(start, end);\n }, ChangeSource.Format);\n}\n","import BodyScoper from './BodyScoper';\r\nimport EmptyInlineElement from '../inlineElements/EmptyInlineElement';\r\nimport getBlockElementAtNode from '../blockElements/getBlockElementAtNode';\r\nimport getInlineElementAtNode from '../inlineElements/getInlineElementAtNode';\r\nimport PartialInlineElement from '../inlineElements/PartialInlineElement';\r\nimport SelectionBlockScoper from './SelectionBlockScoper';\r\nimport SelectionScoper from './SelectionScoper';\r\nimport TraversingScoper from './TraversingScoper';\r\nimport { getInlineElementBeforeAfter } from '../inlineElements/getInlineElementBeforeAfter';\r\nimport { getLeafSibling } from '../utils/getLeafSibling';\r\nimport {\r\n BlockElement,\r\n ContentPosition,\r\n IContentTraverser,\r\n InlineElement,\r\n NodePosition,\r\n} from 'roosterjs-editor-types';\r\n\r\n/**\r\n * The provides traversing of content inside editor.\r\n * There are two ways to traverse, block by block, or inline element by inline element\r\n * Block and inline traversing is independent from each other, meaning if you traverse block by block, it does not change\r\n * the current inline element position\r\n */\r\nexport default class ContentTraverser implements IContentTraverser {\r\n private currentInline: InlineElement;\r\n private currentBlock: BlockElement;\r\n\r\n /**\r\n * Create a content traverser for the whole body of given root node\r\n * @param scoper Traversing scoper object to help scope the traversing\r\n * @param skipTags (Optional) tags that child elements will be skipped\r\n */\r\n private constructor(private scoper: TraversingScoper, private skipTags?: string[]) {}\r\n\r\n /**\r\n * Create a content traverser for the whole body of given root node\r\n * @param rootNode The root node to traverse in\r\n * @param startNode The node to start from. If not passed, it will start from the beginning of the body\r\n * @param skipTags (Optional) tags that child elements will be skipped\r\n */\r\n public static createBodyTraverser(\r\n rootNode: Node,\r\n startNode?: Node,\r\n skipTags?: string[]\r\n ): IContentTraverser {\r\n return new ContentTraverser(new BodyScoper(rootNode, startNode));\r\n }\r\n\r\n /**\r\n * Create a content traverser for the given selection\r\n * @param rootNode The root node to traverse in\r\n * @param range The selection range to scope the traversing\r\n * @param skipTags (Optional) tags that child elements will be skipped\r\n */\r\n public static createSelectionTraverser(\r\n rootNode: Node,\r\n range: Range,\r\n skipTags?: string[]\r\n ): IContentTraverser {\r\n return new ContentTraverser(new SelectionScoper(rootNode, range), skipTags);\r\n }\r\n\r\n /**\r\n * Create a content traverser for a block element which contains the given position\r\n * @param rootNode The root node to traverse in\r\n * @param position A position inside a block, traversing will be scoped within this block.\r\n * If passing a range, the start position of this range will be used\r\n * @param startFrom Start position of traversing. The value can be Begin, End, SelectionStart\r\n * @param skipTags (Optional) tags that child elements will be skipped\r\n */\r\n public static createBlockTraverser(\r\n rootNode: Node,\r\n position: NodePosition | Range,\r\n start: ContentPosition = ContentPosition.SelectionStart,\r\n skipTags?: string[]\r\n ): IContentTraverser {\r\n return new ContentTraverser(new SelectionBlockScoper(rootNode, position, start));\r\n }\r\n\r\n /**\r\n * Get current block\r\n */\r\n public get currentBlockElement(): BlockElement {\r\n // Prepare currentBlock from the scoper\r\n if (!this.currentBlock) {\r\n this.currentBlock = this.scoper.getStartBlockElement();\r\n }\r\n\r\n return this.currentBlock;\r\n }\r\n\r\n /**\r\n * Get next block element\r\n */\r\n public getNextBlockElement(): BlockElement {\r\n return this.getPreviousNextBlockElement(true /*isNext*/);\r\n }\r\n\r\n /**\r\n * Get previous block element\r\n */\r\n public getPreviousBlockElement(): BlockElement {\r\n return this.getPreviousNextBlockElement(false /*isNext*/);\r\n }\r\n\r\n private getPreviousNextBlockElement(isNext: boolean): BlockElement {\r\n let current = this.currentBlockElement;\r\n\r\n if (!current) {\r\n return null;\r\n }\r\n\r\n let leaf = getLeafSibling(\r\n this.scoper.rootNode,\r\n isNext ? current.getEndNode() : current.getStartNode(),\r\n isNext,\r\n this.skipTags\r\n );\r\n let newBlock = leaf ? getBlockElementAtNode(this.scoper.rootNode, leaf) : null;\r\n\r\n // Make sure this is right block:\r\n // 1) the block is in scope per scoper\r\n // 2) the block is after (for next) or before (for previous) the current block\r\n // Then:\r\n // 1) Re-position current block to newly found block\r\n if (\r\n newBlock &&\r\n this.scoper.isBlockInScope(newBlock) &&\r\n ((isNext && newBlock.isAfter(current)) || (!isNext && current.isAfter(newBlock)))\r\n ) {\r\n this.currentBlock = newBlock;\r\n return this.currentBlock;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Current inline element getter\r\n */\r\n public get currentInlineElement(): InlineElement {\r\n // Retrieve a start inline from scoper\r\n if (!this.currentInline) {\r\n this.currentInline = this.scoper.getStartInlineElement();\r\n }\r\n\r\n return this.currentInline instanceof EmptyInlineElement ? null : this.currentInline;\r\n }\r\n\r\n /**\r\n * Get next inline element\r\n */\r\n public getNextInlineElement(): InlineElement {\r\n return this.getPreviousNextInlineElement(true /*isNext*/);\r\n }\r\n\r\n /**\r\n * Get previous inline element\r\n */\r\n public getPreviousInlineElement(): InlineElement {\r\n return this.getPreviousNextInlineElement(false /*isNext*/);\r\n }\r\n\r\n private getPreviousNextInlineElement(isNext: boolean): InlineElement {\r\n let current = this.currentInlineElement || this.currentInline;\r\n let newInline: InlineElement;\r\n\r\n if (!current) {\r\n return null;\r\n }\r\n\r\n if (current instanceof EmptyInlineElement) {\r\n newInline = getInlineElementBeforeAfter(\r\n this.scoper.rootNode,\r\n current.getStartPosition(),\r\n isNext\r\n );\r\n if (newInline && !current.getParentBlock().contains(newInline.getContainerNode())) {\r\n newInline = null;\r\n }\r\n } else {\r\n newInline = getNextPreviousInlineElement(this.scoper.rootNode, current, isNext);\r\n newInline =\r\n newInline &&\r\n current &&\r\n ((isNext && newInline.isAfter(current)) || (!isNext && current.isAfter(newInline)))\r\n ? newInline\r\n : null;\r\n }\r\n\r\n // For inline, we need to make sure:\r\n // 1) it is really next/previous to current\r\n // 2) pass on the new inline to this.scoper to do the trimming and we still get back an inline\r\n // Then\r\n // 1) re-position current inline\r\n if (newInline && (newInline = this.scoper.trimInlineElement(newInline))) {\r\n this.currentInline = newInline;\r\n return this.currentInline;\r\n }\r\n\r\n return null;\r\n }\r\n}\r\n\r\nfunction getNextPreviousInlineElement(\r\n rootNode: Node,\r\n current: InlineElement,\r\n isNext: boolean\r\n): InlineElement {\r\n if (!current) {\r\n return null;\r\n }\r\n if (current instanceof PartialInlineElement) {\r\n // if current is partial, get the other half of the inline unless it is no more\r\n let result = isNext ? current.nextInlineElement : current.previousInlineElement;\r\n\r\n if (result) {\r\n return result;\r\n }\r\n }\r\n\r\n // Get a leaf node after startNode and use that base to find next inline\r\n let startNode = current.getContainerNode();\r\n startNode = getLeafSibling(rootNode, startNode, isNext);\r\n return getInlineElementAtNode(rootNode, startNode);\r\n}\r\n","import applyTextStyle from '../utils/applyTextStyle';\r\nimport isNodeAfter from '../utils/isNodeAfter';\r\nimport Position from '../selection/Position';\r\nimport {\r\n BlockElement,\r\n InlineElement,\r\n NodePosition,\r\n NodeType,\r\n PositionType,\r\n} from 'roosterjs-editor-types';\r\n\r\n/**\r\n * This presents an inline element that can be represented by a single html node.\r\n * This serves as base for most inline element as it contains most implementation\r\n * of all operations that can happen on an inline element. Other sub inline elements mostly\r\n * just identify themselves for a certain type\r\n */\r\nexport default class NodeInlineElement implements InlineElement {\r\n constructor(private containerNode: Node, private parentBlock: BlockElement) {}\r\n\r\n /**\r\n * The text content for this inline element\r\n */\r\n public getTextContent(): string {\r\n // nodeValue is better way to retrieve content for a text. Others, just use textContent\r\n return this.containerNode.nodeType == NodeType.Text\r\n ? this.containerNode.nodeValue\r\n : this.containerNode.textContent;\r\n }\r\n\r\n /**\r\n * Get the container node\r\n */\r\n public getContainerNode(): Node {\r\n return this.containerNode;\r\n }\r\n\r\n // Get the parent block\r\n public getParentBlock(): BlockElement {\r\n return this.parentBlock;\r\n }\r\n\r\n /**\r\n * Get the start position of the inline element\r\n */\r\n public getStartPosition(): NodePosition {\r\n // For a position, we always want it to point to a leaf node\r\n // We should try to go get the lowest first child node from the container\r\n return new Position(this.containerNode, 0).normalize();\r\n }\r\n\r\n /**\r\n * Get the end position of the inline element\r\n */\r\n public getEndPosition(): NodePosition {\r\n // For a position, we always want it to point to a leaf node\r\n // We should try to go get the lowest last child node from the container\r\n return new Position(this.containerNode, PositionType.End).normalize();\r\n }\r\n\r\n /**\r\n * Checks if this inline element is a textual inline element\r\n */\r\n public isTextualInlineElement(): boolean {\r\n return this.containerNode && this.containerNode.nodeType == NodeType.Text;\r\n }\r\n\r\n /**\r\n * Checks if an inline element is after the current inline element\r\n */\r\n public isAfter(inlineElement: InlineElement): boolean {\r\n return inlineElement && isNodeAfter(this.containerNode, inlineElement.getContainerNode());\r\n }\r\n\r\n /**\r\n * Checks if the given position is contained in the inline element\r\n */\r\n public contains(pos: NodePosition): boolean {\r\n let start = this.getStartPosition();\r\n let end = this.getEndPosition();\r\n return pos && pos.isAfter(start) && end.isAfter(pos);\r\n }\r\n\r\n /**\r\n * Apply inline style to an inline element\r\n */\r\n public applyStyle(styler: (element: HTMLElement, isInnerNode?: boolean) => any): void {\r\n applyTextStyle(this.containerNode, styler);\r\n }\r\n}\r\n","/**\r\n * Split a text node into two parts by an offset number, and return one of them\r\n * @param textNode The text node to split\r\n * @param offset The offset number to split at\r\n * @param returnFirstPart True to return the first part, then the passed in textNode will become the second part.\r\n * Otherwise return the second part, and the passed in textNode will become the first part\r\n */\r\nexport default function splitTextNode(textNode: Text, offset: number, returnFirstPart: boolean) {\r\n const firstPart = textNode.nodeValue.substr(0, offset);\r\n const secondPart = textNode.nodeValue.substr(offset);\r\n const newNode = textNode.ownerDocument.createTextNode(returnFirstPart ? firstPart : secondPart);\r\n textNode.nodeValue = returnFirstPart ? secondPart : firstPart;\r\n textNode.parentNode.insertBefore(newNode, returnFirstPart ? textNode : textNode.nextSibling);\r\n return newNode;\r\n}\r\n","import applyTextStyle from '../utils/applyTextStyle';\r\nimport createRange from '../selection/createRange';\r\nimport Position from '../selection/Position';\r\nimport { BlockElement, InlineElement, NodePosition, PositionType } from 'roosterjs-editor-types';\r\nimport { getNextLeafSibling, getPreviousLeafSibling } from '../utils/getLeafSibling';\r\n\r\n/**\r\n * This is a special version of inline element that identifies a section of an inline element\r\n * We often have the need to cut an inline element in half and perform some operation only on half of an inline element\r\n * i.e. users select only some text of a text node and apply format, in that case, format has to happen on partial of an inline element\r\n * PartialInlineElement is implemented in a way that decorate another full inline element with its own override on methods like isAfter\r\n * It also offers some special methods that others don't have, i.e. nextInlineElement etc.\r\n */\r\nexport default class PartialInlineElement implements InlineElement {\r\n constructor(\r\n private inlineElement: InlineElement,\r\n private start?: NodePosition,\r\n private end?: NodePosition\r\n ) {}\r\n\r\n /**\r\n * Get the full inline element that this partial inline decorates\r\n */\r\n public getDecoratedInline(): InlineElement {\r\n return this.inlineElement;\r\n }\r\n\r\n /**\r\n * Gets the container node\r\n */\r\n public getContainerNode(): Node {\r\n return this.inlineElement.getContainerNode();\r\n }\r\n\r\n /**\r\n * Gets the parent block\r\n */\r\n public getParentBlock(): BlockElement {\r\n return this.inlineElement.getParentBlock();\r\n }\r\n\r\n /**\r\n * Gets the text content\r\n */\r\n public getTextContent(): string {\r\n let range = createRange(this.getStartPosition(), this.getEndPosition());\r\n\r\n return range.toString();\r\n }\r\n\r\n /**\r\n * Get start position of this inline element.\r\n */\r\n public getStartPosition(): NodePosition {\r\n return this.start || this.inlineElement.getStartPosition();\r\n }\r\n\r\n /**\r\n * Get end position of this inline element.\r\n */\r\n public getEndPosition(): NodePosition {\r\n return this.end || this.inlineElement.getEndPosition();\r\n }\r\n\r\n /**\r\n * Get next partial inline element if it is not at the end boundary yet\r\n */\r\n public get nextInlineElement(): PartialInlineElement {\r\n return this.end && new PartialInlineElement(this.inlineElement, this.end, null);\r\n }\r\n\r\n /**\r\n * Get previous partial inline element if it is not at the begin boundary yet\r\n */\r\n public get previousInlineElement(): PartialInlineElement {\r\n return this.start && new PartialInlineElement(this.inlineElement, null, this.start);\r\n }\r\n\r\n /**\r\n * Checks if it contains a position\r\n */\r\n public contains(pos: NodePosition): boolean {\r\n return pos && pos.isAfter(this.getStartPosition()) && this.getEndPosition().isAfter(pos);\r\n }\r\n\r\n /**\r\n * Checks if this inline element is a textual inline element\r\n */\r\n public isTextualInlineElement(): boolean {\r\n return this.inlineElement && this.inlineElement.isTextualInlineElement();\r\n }\r\n\r\n /**\r\n * Check if this inline element is after the other inline element\r\n */\r\n public isAfter(inlineElement: InlineElement): boolean {\r\n let thisStart = this.getStartPosition();\r\n let otherEnd = inlineElement && inlineElement.getEndPosition();\r\n return otherEnd && (thisStart.isAfter(otherEnd) || thisStart.equalTo(otherEnd));\r\n }\r\n\r\n /**\r\n * apply style\r\n */\r\n public applyStyle(styler: (element: HTMLElement, isInnerNode?: boolean) => any) {\r\n let from = this.getStartPosition().normalize();\r\n let to = this.getEndPosition().normalize();\r\n let container = this.getContainerNode();\r\n\r\n if (from.isAtEnd) {\r\n let nextNode = getNextLeafSibling(container, from.node);\r\n from = nextNode ? new Position(nextNode, PositionType.Begin) : null;\r\n }\r\n if (to.offset == 0) {\r\n let previousNode = getPreviousLeafSibling(container, to.node);\r\n to = previousNode ? new Position(previousNode, PositionType.End) : null;\r\n }\r\n\r\n applyTextStyle(container, styler, from, to);\r\n }\r\n}\r\n","/**\n * Replace all child nodes of the given target node to the child nodes of source node.\n * @param target Target node, all child nodes of this node will be removed if keepExistingChildren is not set to true\n * @param source (Optional) source node, all child nodes of this node will be move to target node\n * @param keepExistingChildren (Optional) When set to true, all existing child nodes of target will be kept\n */\nexport default function moveChildNodes(\n target: Node,\n source?: Node,\n keepExistingChildren?: boolean\n) {\n if (!target) {\n return;\n }\n\n while (!keepExistingChildren && target.firstChild) {\n target.removeChild(target.firstChild);\n }\n\n while (source?.firstChild) {\n target.appendChild(source.firstChild);\n }\n}\n","import getTagOfNode from './getTagOfNode';\r\nimport { NodeType } from 'roosterjs-editor-types';\r\n\r\nconst VISIBLE_ELEMENT_TAGS = ['IMG'];\r\nconst VISIBLE_CHILD_ELEMENT_SELECTOR = ['TABLE', 'IMG', 'LI'].join(',');\r\nconst ZERO_WIDTH_SPACE = /\\u200b/g;\r\n\r\n/**\r\n * Check if a given node has no visible content\r\n * @param node The node to check\r\n * @param trimContent Whether trim the text content so that spaces will be treated as empty.\r\n * Default value is false\r\n * @returns True if there isn't any visible element inside node, otherwise false\r\n */\r\nexport default function isNodeEmpty(node: Node, trimContent?: boolean) {\r\n if (!node) {\r\n return false;\r\n } else if (node.nodeType == NodeType.Text) {\r\n return trim(node.nodeValue, trimContent) == '';\r\n } else if (node.nodeType == NodeType.Element) {\r\n let element = node as Element;\r\n let textContent = trim(element.textContent, trimContent);\r\n if (\r\n textContent != '' ||\r\n VISIBLE_ELEMENT_TAGS.indexOf(getTagOfNode(element)) >= 0 ||\r\n element.querySelectorAll(VISIBLE_CHILD_ELEMENT_SELECTOR)[0]\r\n ) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n\r\nfunction trim(s: string, trim: boolean) {\r\n s = s.replace(ZERO_WIDTH_SPACE, '');\r\n return trim ? s.trim() : s;\r\n}\r\n","/**\r\n * Removes the node and keep all children in place, return the parentNode where the children are attached\r\n * @param node the node to remove\r\n */\r\nexport default function unwrap(node: Node): Node {\r\n // Unwrap requires a parentNode\r\n let parentNode = node ? node.parentNode : null;\r\n if (!parentNode) {\r\n return null;\r\n }\r\n\r\n while (node.firstChild) {\r\n parentNode.insertBefore(node.firstChild, node);\r\n }\r\n\r\n parentNode.removeChild(node);\r\n return parentNode;\r\n}\r\n","import checkEditInfoState, { ImageEditInfoState } from './checkEditInfoState';\nimport ImageEditInfo, { IMAGE_EDIT_INFO_NAME } from '../types/ImageEditInfo';\n\n/**\n * @internal\n * Get image edit info from an image. If the image doesn't have edit info, create one from this image.\n * When create new edit info, it will have width/height set to the image's current client width/height, and\n * natural width/height set to the image's natural width/height, src set to its current src, and all\n * other fields set to 0.\n * @param image The image to get edit info from\n */\nexport default function getEditInfoFromImage(image: HTMLImageElement): ImageEditInfo {\n const obj = safeParseJSON(image?.dataset[IMAGE_EDIT_INFO_NAME]) as ImageEditInfo;\n return checkEditInfoState(obj) == ImageEditInfoState.Invalid ? getInitialEditInfo(image) : obj;\n}\n\nfunction getInitialEditInfo(image: HTMLImageElement): ImageEditInfo {\n return {\n src: image.src,\n widthPx: image.clientWidth,\n heightPx: image.clientHeight,\n naturalWidth: image.naturalWidth,\n naturalHeight: image.naturalHeight,\n leftPercent: 0,\n rightPercent: 0,\n topPercent: 0,\n bottomPercent: 0,\n angleRad: 0,\n };\n}\n\nfunction safeParseJSON(json: string): any {\n try {\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n","import getTagOfNode from './getTagOfNode';\r\n\r\n/**\r\n * HTML void elements\r\n * Per https://www.w3.org/TR/html/syntax.html#syntax-elements, cannot have child nodes\r\n * This regex is used when we move focus to very begin of editor. We should avoid putting focus inside\r\n * void elements so users don't accidentally create child nodes in them\r\n */\r\nconst HTML_VOID_ELEMENTS = 'AREA,BASE,BR,COL,COMMAND,EMBED,HR,IMG,INPUT,KEYGEN,LINK,META,PARAM,SOURCE,TRACK,WBR'.split(\r\n ','\r\n);\r\n\r\n/**\r\n * Check if the given node is html void element. Void element cannot have children\r\n * @param node The node to check\r\n */\r\nexport default function isVoidHtmlElement(node: Node): boolean {\r\n return !!node && HTML_VOID_ELEMENTS.indexOf(getTagOfNode(node)) >= 0;\r\n}\r\n","import getTagOfNode from './getTagOfNode';\r\nimport Position from '../selection/Position';\r\nimport splitTextNode from './splitTextNode';\r\nimport wrap from './wrap';\r\nimport { getNextLeafSibling } from './getLeafSibling';\r\nimport { NodePosition, NodeType, PositionType } from 'roosterjs-editor-types';\r\nimport { splitBalancedNodeRange } from './splitParentNode';\r\n\r\nconst STYLET_AGS = 'SPAN,B,I,U,EM,STRONG,STRIKE,S,SMALL'.split(',');\r\n\r\n/**\r\n * Apply style using a styler function to the given container node in the given range\r\n * @param container The container node to apply style to\r\n * @param styler The styler function\r\n * @param from From position\r\n * @param to To position\r\n */\r\nexport default function applyTextStyle(\r\n container: Node,\r\n styler: (node: HTMLElement, isInnerNode?: boolean) => any,\r\n from: NodePosition = new Position(container, PositionType.Begin).normalize(),\r\n to: NodePosition = new Position(container, PositionType.End).normalize()\r\n) {\r\n let formatNodes: Node[] = [];\r\n\r\n while (from && to && to.isAfter(from)) {\r\n let formatNode = from.node;\r\n let parentTag = getTagOfNode(formatNode.parentNode);\r\n\r\n // The code below modifies DOM. Need to get the next sibling first otherwise you won't be able to reliably get a good next sibling node\r\n let nextNode = getNextLeafSibling(container, formatNode);\r\n\r\n if (formatNode.nodeType == NodeType.Text && ['TR', 'TABLE'].indexOf(parentTag) < 0) {\r\n if (formatNode == to.node && !to.isAtEnd) {\r\n formatNode = splitTextNode(formatNode, to.offset, true /*returnFirstPart*/);\r\n }\r\n\r\n if (from.offset > 0) {\r\n formatNode = splitTextNode(\r\n formatNode,\r\n from.offset,\r\n false /*returnFirstPart*/\r\n );\r\n }\r\n\r\n formatNodes.push(formatNode);\r\n }\r\n\r\n from = nextNode && new Position(nextNode, PositionType.Begin);\r\n }\r\n\r\n if (formatNodes.length > 0) {\r\n if (formatNodes.every(node => node.parentNode == formatNodes[0].parentNode)) {\r\n let newNode = formatNodes.shift();\r\n formatNodes.forEach(node => {\r\n newNode.nodeValue += node.nodeValue;\r\n node.parentNode.removeChild(node);\r\n });\r\n formatNodes = [newNode];\r\n }\r\n\r\n formatNodes.forEach(node => {\r\n // When apply style within style tags like B/I/U/..., we split the tag and apply outside them\r\n // So that the inner style tag such as U, STRIKE can inherit the style we added\r\n while (\r\n getTagOfNode(node) != 'SPAN' &&\r\n STYLET_AGS.indexOf(getTagOfNode(node.parentNode)) >= 0\r\n ) {\r\n callStylerWithInnerNode(node, styler);\r\n node = splitBalancedNodeRange(node);\r\n }\r\n\r\n if (getTagOfNode(node) != 'SPAN') {\r\n callStylerWithInnerNode(node, styler);\r\n node = wrap(node, 'SPAN');\r\n }\r\n styler(node);\r\n });\r\n }\r\n}\r\n\r\nfunction callStylerWithInnerNode(\r\n node: Node,\r\n styler: (node: HTMLElement, isInnerNode?: boolean) => any\r\n) {\r\n if (node && node.nodeType == NodeType.Element) {\r\n styler(node as HTMLElement, true /*isInnerNode*/);\r\n }\r\n}\r\n","import findClosestElementAncestor from './findClosestElementAncestor';\r\n\r\n/**\r\n * Get computed styles of a node\r\n * @param node The node to get computed styles from\r\n * @param styleNames Names of style to get, can be a single name or an array.\r\n * Default value is font-family, font-size, color, background-color\r\n * @returns An array of the computed styles\r\n */\r\nexport default function getComputedStyles(\r\n node: Node,\r\n styleNames: string | string[] = ['font-family', 'font-size', 'color', 'background-color']\r\n): string[] {\r\n let element = findClosestElementAncestor(node);\r\n let result: string[] = [];\r\n styleNames = Array.isArray(styleNames) ? styleNames : [styleNames];\r\n if (element) {\r\n let win = element.ownerDocument.defaultView || window;\r\n let styles = win.getComputedStyle(element);\r\n\r\n if (styles) {\r\n for (let style of styleNames) {\r\n let value = styles.getPropertyValue(style) || '';\r\n value = style != 'font-family' ? value.toLowerCase() : value;\r\n value = style == 'font-size' ? px2Pt(value) : value;\r\n result.push(value);\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * A shortcut for getComputedStyles() when only one style is to be retrieved\r\n * @param node The node to get style from\r\n * @param styleName The style name\r\n * @returns The style value\r\n */\r\nexport function getComputedStyle(node: Node, styleName: string): string {\r\n return getComputedStyles(node, styleName)[0] || '';\r\n}\r\n\r\nfunction px2Pt(px: string) {\r\n if (px && px.indexOf('px') == px.length - 2) {\r\n // Edge may not handle the floating computing well which causes the calculated value is a little less than actual value\r\n // So add 0.05 to fix it\r\n return Math.round(parseFloat(px) * 75 + 0.05) / 100 + 'pt';\r\n }\r\n return px;\r\n}\r\n","import NodeInlineElement from './NodeInlineElement';\r\nimport { BlockElement } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * This is inline element presenting an html hyperlink\r\n */\r\nexport default class LinkInlineElement extends NodeInlineElement {\r\n constructor(containerNode: Node, parentBlock: BlockElement) {\r\n super(containerNode, parentBlock);\r\n }\r\n}\r\n","import getInlineElementAtNode from './getInlineElementAtNode';\r\nimport PartialInlineElement from './PartialInlineElement';\r\nimport shouldSkipNode from '../utils/shouldSkipNode';\r\nimport { getLeafSibling } from '../utils/getLeafSibling';\r\nimport { InlineElement, NodePosition, NodeType } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * @internal\r\n * Get inline element before a position\r\n * This is mostly used when we want to get the inline element before selection/cursor\r\n * There is a possible that the cursor is in middle of an inline element (i.e. mid of a text node)\r\n * in this case, we only want to return what is before cursor (a partial of an inline) to indicate\r\n * that we're in middle.\r\n * @param root Root node of current scope, use for create InlineElement\r\n * @param position The position to get InlineElement before\r\n */\r\nexport function getInlineElementBefore(root: Node, position: NodePosition): InlineElement {\r\n return getInlineElementBeforeAfter(root, position, false /*isAfter*/);\r\n}\r\n\r\n/**\r\n * @internal\r\n * Get inline element after a position\r\n * This is mostly used when we want to get the inline element after selection/cursor\r\n * There is a possible that the cursor is in middle of an inline element (i.e. mid of a text node)\r\n * in this case, we only want to return what is before cursor (a partial of an inline) to indicate\r\n * that we're in middle.\r\n * @param root Root node of current scope, use for create InlineElement\r\n * @param position The position to get InlineElement after\r\n */\r\nexport function getInlineElementAfter(root: Node, position: NodePosition): InlineElement {\r\n return getInlineElementBeforeAfter(root, position, true /*isAfter*/);\r\n}\r\n\r\n/**\r\n * @internal\r\n */\r\nexport function getInlineElementBeforeAfter(root: Node, position: NodePosition, isAfter: boolean) {\r\n if (!root || !position || !position.node) {\r\n return null;\r\n }\r\n\r\n position = position.normalize();\r\n let { node, offset, isAtEnd } = position;\r\n let isPartial = false;\r\n\r\n if ((!isAfter && offset == 0 && !isAtEnd) || (isAfter && isAtEnd)) {\r\n node = getLeafSibling(root, node, isAfter);\r\n } else if (\r\n node.nodeType == NodeType.Text &&\r\n ((!isAfter && !isAtEnd) || (isAfter && offset > 0))\r\n ) {\r\n isPartial = true;\r\n }\r\n\r\n if (node && shouldSkipNode(node)) {\r\n node = getLeafSibling(root, node, isAfter);\r\n }\r\n\r\n let inlineElement = getInlineElementAtNode(root, node);\r\n\r\n if (inlineElement && (isPartial || inlineElement.contains(position))) {\r\n inlineElement = isAfter\r\n ? new PartialInlineElement(inlineElement, position, null)\r\n : new PartialInlineElement(inlineElement, null, position);\r\n }\r\n\r\n return inlineElement;\r\n}\r\n","/**\n * Read a file object and invoke a callback function with the data url of this file\n * @param file The file to read\n * @param callback the callback to invoke with data url of the file.\n * If fail to read, dataUrl will be null\n */\nexport default function readFile(file: File, callback: (dataUrl: string) => void) {\n if (file && callback) {\n const reader = new FileReader();\n reader.onload = () => {\n callback(reader.result as string);\n };\n reader.onerror = () => {\n callback(null);\n };\n reader.readAsDataURL(file);\n }\n}\n","/**\n * A type-safe wrapper for Array.prototype.push.apply()\n * @param mainArray The main array to push items into\n * @param itemsArray The items to push to main array\n */\nexport default function arrayPush(mainArray: T[], itemsArray: T[]) {\n Array.prototype.push.apply(mainArray, itemsArray);\n}\n","import { Rect } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * A ClientRect of all 0 is possible. i.e. chrome returns a ClientRect of 0 when the cursor is on an empty p\r\n * We validate that and only return a rect when the passed in ClientRect is valid\r\n */\r\nexport default function normalizeRect(clientRect: ClientRect): Rect {\r\n let { left, right, top, bottom } =\r\n clientRect || { left: 0, right: 0, top: 0, bottom: 0 };\r\n return left + right + top + bottom > 0\r\n ? {\r\n left: Math.round(left),\r\n right: Math.round(right),\r\n top: Math.round(top),\r\n bottom: Math.round(bottom),\r\n }\r\n : null;\r\n}\r\n","import changeElementTag from '../utils/changeElementTag';\nimport getListTypeFromNode, { isListElement } from './getListTypeFromNode';\nimport getTagOfNode from '../utils/getTagOfNode';\nimport isBlockElement from '../utils/isBlockElement';\nimport isNodeEmpty from '../utils/isNodeEmpty';\nimport Position from '../selection/Position';\nimport queryElements from '../utils/queryElements';\nimport safeInstanceOf from '../utils/safeInstanceOf';\nimport splitParentNode from '../utils/splitParentNode';\nimport toArray from '../utils/toArray';\nimport unwrap from '../utils/unwrap';\nimport VListItem from './VListItem';\nimport wrap from '../utils/wrap';\nimport {\n Indentation,\n ListType,\n NodePosition,\n PositionType,\n NodeType,\n} from 'roosterjs-editor-types';\n\n/**\n * Represent a bullet or a numbering list\n *\n * @example\n * A VList is a logical representation of list items, it contains an item array with node and list type stack.\n * e.g. We have a list like this\n * ```html\n *
    \n *
  1. item 1
  2. \n *
  3. item 2
  4. \n *
      \n *
    • item 2.1
    • \n *
    • item 2.2
    • \n *
        \n *
\n * ```\n *\n * A VList of this list will be like this:\n * ```javascript\n * {\n * rootList: (OL node),\n * items: [{\n * node: (LI node with 'item 1'),\n * listTypes: [null, OL],\n * }, {\n * node: (LI node with 'item 2'),\n * listTypes: [null, OL],\n * }, {\n * node: (LI node with 'item 2.1),\n * listTypes: [null, OL, UL],\n * }, {\n * node: (LI node with 'item 2.2'),\n * listTypes: [null, OL, UL],\n * }\n * ]\n * }\n * ```\n *\n * When we want to outdent item 2.1, we just need to remove the last \"UL\" from listTypes of item 2.1, then\n * the writeBack() function will handle everything related to DOM change\n */\nexport default class VList {\n public readonly items: VListItem[] = [];\n\n /**\n * Create a new instance of VList class\n * @param rootList The root list element, can be either OL or UL tag\n */\n constructor(private rootList: HTMLOListElement | HTMLUListElement) {\n if (!rootList) {\n throw new Error('rootList must not be null');\n }\n\n // Before populate items, we need to normalize the list to make sure it is in a correct format\n // otherwise further action may mass thing up.\n //\n // There are two kinds of normalization to perform.\n // 1. Move nodes directly under OL/UL into a LI node, unless it is an orphan node\n // Please see comment for VListItem.isOrphanItem() for more information about orphan node\n // e.g.:\n // ```HTML\n //
    \n //
  1. item 1
  2. \n //
    item 2
    \n //
\n // ```\n // After this step, it should become:\n // ```html\n //
    \n //
  1. item 1\n //
    item 2
    \n //
  2. \n //
\n // ```\n moveChildNodesToLi(this.rootList);\n queryElements(this.rootList, 'ol,ul', moveChildNodesToLi);\n\n // 2. Move LI node embedded into another LI node out to directly under OL/UL node\n // Ideally browser we do this for us automatically when out the HTML into DOM. However after\n // step 1, it is possible that we move some LI node into another one. e.g:\n // ```HTML\n //
    \n //
  1. item 1
  2. \n //
    \n // item 1.1\n //
  3. item 3
  4. \n //
    \n //
\n // ```\n // See that the second LI tag is not directly under OL, so after step 1, this will become:\n // ```html\n //
    \n //
  1. item 1\n //
    \n // item 1.1\n //
  2. item 2
  3. \n // \n //
  4. \n //
\n // ```\n // Now we have a LI tag embedded into another LI tag. So we need step 2 to move the inner LI tag out to be:\n // ```html\n //
    \n //
  1. item1\n //
    item 1.1
    \n //
  2. \n //
  3. item2
  4. \n //
\n // ```\n queryElements(this.rootList, 'li', moveLiToList);\n\n this.populateItems(this.rootList);\n }\n\n /**\n * Check if this list contains the given node\n * @param node The node to check\n */\n contains(node: Node) {\n // We don't check if the node is contained by this.rootList here, because after some operation,\n // it is possible a node is logically contained by this list but the container list item hasn't\n // been put under this.rootList in DOM tree yet.\n return this.items.some(item => item.contains(node));\n }\n\n /**\n * Get list number of the last item in this VList.\n * If there is no order list item, result will be undefined\n */\n getLastItemNumber(): number | undefined {\n const start = getStart(this.rootList);\n\n return start === undefined\n ? start\n : start -\n 1 +\n this.items.filter(\n item =>\n item.getListType() == ListType.Ordered &&\n item.getLevel() == 1 &&\n !item.isDummy()\n ).length;\n }\n\n /**\n * Write the result back into DOM tree\n * After that, this VList becomes unavailable because we set this.rootList to null\n */\n writeBack() {\n if (!this.rootList) {\n throw new Error('rootList must not be null');\n }\n\n const doc = this.rootList.ownerDocument;\n const listStack: Node[] = [doc.createDocumentFragment()];\n const placeholder = doc.createTextNode('');\n let start = getStart(this.rootList) || 1;\n let lastList: Node;\n\n // Use a placeholder to hold the position since the root list may be moved into document fragment later\n this.rootList.parentNode.replaceChild(placeholder, this.rootList);\n\n this.items.forEach(item => {\n if (item.getNewListStart() && item.getNewListStart() != start) {\n listStack.splice(1, listStack.length - 1);\n start = item.getNewListStart();\n }\n item.writeBack(listStack, this.rootList);\n const topList = listStack[1];\n\n if (safeInstanceOf(topList, 'HTMLOListElement')) {\n if (lastList != topList) {\n if (start == 1) {\n topList.removeAttribute('start');\n } else {\n topList.start = start;\n }\n }\n\n if (item.getLevel() == 1) {\n start++;\n }\n }\n\n lastList = topList;\n });\n\n // Restore the content to the position of placeholder\n placeholder.parentNode.replaceChild(listStack[0], placeholder);\n\n // Set rootList to null to avoid this to be called again for the same VList, because\n // after change the rootList may not be available any more (e.g. outdent all items).\n this.rootList = null;\n }\n\n /**\n * Sets the New List Start Property, that is going to be used to create a new List in the WriteBack function\n * @param separator The HTML element that indicates when to split the VList\n * @param startNumber The start number of the new List\n */\n split(separator: HTMLElement, startNumber: number) {\n if (!this.rootList) {\n throw new Error('rootList must not be null');\n }\n\n //Traverse the items of the VList, when the separator is found, set the New List Start Property\n for (let index = 0; index < this.items.length; index++) {\n if (this.items[index].getNode() == separator) {\n this.items[index].setNewListStart(startNumber);\n return;\n }\n }\n }\n\n /**\n * Set indentation of the given range of this list\n * @param start Start position to operate from\n * @param end End position to operate to\n * @param indentation Indent or outdent\n */\n setIndentation(start: NodePosition, end: NodePosition, indentation: Indentation): void;\n\n /**\n * Outdent the give range of this list\n * @param start Start position to operate from\n * @param end End position to operate to\n * @param indentation Specify to outdent\n * @param softOutdent (Optional) True to make the item to by dummy (no bullet or number) if the item is not dummy,\n * otherwise outdent the item\n */\n setIndentation(\n start: NodePosition,\n end: NodePosition,\n indentation: Indentation.Decrease,\n softOutdent?: boolean\n ): void;\n\n setIndentation(\n start: NodePosition,\n end: NodePosition,\n indentation: Indentation,\n softOutdent?: boolean\n ) {\n this.findListItems(start, end, item =>\n indentation == Indentation.Decrease\n ? softOutdent && !item.isDummy()\n ? item.setIsDummy(true /*isDummy*/)\n : item.outdent()\n : item.indent()\n );\n }\n\n /**\n * Change list type of the given range of this list.\n * If some of the items are not real list item yet, this will make them to be list item with given type\n * If all items in the given range are already in the type to change to, this becomes an outdent operation\n * @param start Start position to operate from\n * @param end End position to operate to\n * @param targetType Target list type\n */\n changeListType(start: NodePosition, end: NodePosition, targetType: ListType) {\n let needChangeType = false;\n\n this.findListItems(start, end, item => {\n needChangeType = needChangeType || item.getListType() != targetType;\n });\n this.findListItems(start, end, item =>\n needChangeType ? item.changeListType(targetType) : item.outdent()\n );\n }\n\n /**\n * Append a new item to this VList\n * @param node node of the item to append. If it is not wrapped with LI tag, it will be wrapped\n * @param type Type of this list item, can be ListType.None\n */\n appendItem(node: Node, type: ListType) {\n const nodeTag = getTagOfNode(node);\n\n // Change DIV tag to SPAN. Otherwise we cannot create new list item by Enter key in Safari\n if (nodeTag == 'DIV') {\n node = changeElementTag(node, 'LI');\n } else if (nodeTag != 'LI') {\n node = wrap(node, 'LI');\n }\n\n this.items.push(type == ListType.None ? new VListItem(node) : new VListItem(node, type));\n }\n\n /**\n * Merge the given VList into current VList.\n * - All list items will be removed from the given VList and added into this list.\n * - The root node of the given VList will be removed from DOM tree\n * - If there are orphan items in the given VList, they will be merged into the last item\n * of this list if any.\n * @param list The vList to merge from\n */\n mergeVList(list: VList) {\n if (list && list != this) {\n list.items.forEach(item => this.items.push(item));\n list.items.splice(0, list.items.length);\n list.rootList.parentNode?.removeChild(list.rootList);\n }\n }\n\n private findListItems(\n start: NodePosition,\n end: NodePosition,\n callback?: (item: VListItem) => any\n ): VListItem[] {\n if (this.items.length == 0) {\n return [];\n }\n\n const listStartPos = new Position(this.items[0].getNode(), PositionType.Begin);\n const listEndPos = new Position(\n this.items[this.items.length - 1].getNode(),\n PositionType.End\n );\n\n let startIndex = listStartPos.isAfter(start) ? 0 : -1;\n let endIndex = this.items.length - (end.isAfter(listEndPos) ? 1 : 0);\n\n this.items.forEach((item, index) => {\n startIndex = item.contains(start.node) ? index : startIndex;\n endIndex = item.contains(end.node) ? index : endIndex;\n });\n\n startIndex = endIndex < this.items.length ? Math.max(0, startIndex) : startIndex;\n endIndex = startIndex >= 0 ? Math.min(this.items.length - 1, endIndex) : endIndex;\n\n const result = startIndex <= endIndex ? this.items.slice(startIndex, endIndex + 1) : [];\n\n if (callback) {\n result.forEach(callback);\n }\n\n return result;\n }\n\n private populateItems(\n list: HTMLOListElement | HTMLUListElement,\n listTypes: (ListType.Ordered | ListType.Unordered)[] = []\n ) {\n const type = getListTypeFromNode(list);\n const items = toArray(list.childNodes);\n\n items.forEach(item => {\n const newListTypes = [...listTypes, type];\n\n if (isListElement(item)) {\n this.populateItems(item, newListTypes);\n } else if (item.nodeType != NodeType.Text || item.nodeValue.trim() != '') {\n this.items.push(new VListItem(item, ...newListTypes));\n }\n });\n }\n}\n\n//Normalization\n\n// Step 1: Move all non-LI direct children under list into LI\n// e.g.\n// From:
  • line 1
  • line 2
\n// To:
  • line 1
    line 2
\nfunction moveChildNodesToLi(list: HTMLOListElement | HTMLUListElement) {\n let currentItem: HTMLLIElement = null;\n\n toArray(list.childNodes).forEach(child => {\n if (getTagOfNode(child) == 'LI') {\n currentItem = child as HTMLLIElement;\n } else if (isListElement(child)) {\n currentItem = null;\n } else if (currentItem && !isNodeEmpty(child, true /*trimContent*/)) {\n currentItem.appendChild(isBlockElement(child) ? child : wrap(child));\n }\n });\n}\n\n// Step 2: Move nested LI up to under list directly\n// e.g.\n// From:
  • line 1
  • line 2
  • line 3
\n// To:
  • line 1
  • line 2
    line 3
\nfunction moveLiToList(li: HTMLLIElement) {\n while (!isListElement(li.parentNode)) {\n splitParentNode(li, true /*splitBefore*/);\n let furtherNodes: Node[] = toArray(li.parentNode.childNodes).slice(1);\n\n if (furtherNodes.length > 0) {\n if (!isBlockElement(furtherNodes[0])) {\n furtherNodes = [wrap(furtherNodes)];\n }\n furtherNodes.forEach(node => li.appendChild(node));\n }\n\n unwrap(li.parentNode);\n }\n}\n\nfunction getStart(list: HTMLOListElement | HTMLUListElement): number | undefined {\n return safeInstanceOf(list, 'HTMLOListElement') ? list.start : undefined;\n}\n","import getTagOfNode from '../utils/getTagOfNode';\nimport { ListType } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Get list type from a list element. The result will be either Ordered or Unordered ListType\n * @param listElement the element to get list type from\n */\nexport default function getListTypeFromNode(\n listElement: HTMLOListElement | HTMLUListElement\n): ListType.Ordered | ListType.Unordered;\n\n/**\n * Get list type from a DOM node. It is possible to return ListType.None\n * @param node the node to get list type from\n */\nexport default function getListTypeFromNode(node: Node): ListType;\n\nexport default function getListTypeFromNode(node: Node): ListType {\n switch (getTagOfNode(node)) {\n case 'OL':\n return ListType.Ordered;\n case 'UL':\n return ListType.Unordered;\n default:\n return ListType.None;\n }\n}\n\n/**\n * @internal\n * Check if the given DOM node is a list element (OL or UL)\n * @param node The node to check\n */\nexport function isListElement(node: Node): node is HTMLUListElement | HTMLOListElement {\n return getListTypeFromNode(node) != ListType.None;\n}\n","import createRange from '../selection/createRange';\nimport Position from '../selection/Position';\nimport { getNextLeafSibling, getPreviousLeafSibling } from '../utils/getLeafSibling';\nimport { Region, RegionBase } from 'roosterjs-editor-types';\n\n/**\n * Get the selection range in the given region.\n * The original range can cover multiple regions, this function will narrow the original selection\n * of a region into current region\n * @param regionBase The region to get range from\n */\nexport default function getSelectionRangeInRegion(regionBase: RegionBase): Range | null {\n if (!regionBase) {\n return null;\n }\n\n const { nodeBefore, nodeAfter, rootNode, skipTags } = regionBase;\n const startNode = nodeBefore\n ? getNextLeafSibling(regionBase.rootNode, nodeBefore, regionBase.skipTags)\n : rootNode.firstChild;\n const endNode = nodeAfter\n ? getPreviousLeafSibling(rootNode, nodeAfter, skipTags)\n : rootNode.lastChild;\n const regionRange = startNode && endNode && createRange(startNode, endNode);\n\n if (!isRegion(regionBase)) {\n return regionRange;\n } else if (regionRange) {\n const regionStart = Position.getStart(regionRange).normalize();\n const regionEnd = Position.getEnd(regionRange).normalize();\n const { fullSelectionEnd, fullSelectionStart } = regionBase;\n\n if (!fullSelectionStart.isAfter(regionEnd) && !regionStart.isAfter(fullSelectionEnd)) {\n const start = fullSelectionStart.isAfter(regionStart)\n ? fullSelectionStart\n : regionStart;\n const end = fullSelectionEnd.isAfter(regionEnd) ? regionEnd : fullSelectionEnd;\n\n return createRange(start, end);\n } else {\n return null;\n }\n }\n}\n\nfunction isRegion(regionBase: RegionBase): regionBase is Region {\n const region = regionBase as Region;\n return !!region.fullSelectionEnd && !!region.fullSelectionStart;\n}\n","/**\n * Get CSS styles of a given element in name-value pair format\n * @param element The element to get styles from\n */\nexport default function getStyles(element: HTMLElement): Record {\n const result: Record = {};\n const style = element?.getAttribute('style') || '';\n style.split(';').forEach(pair => {\n const valueIndex = pair.indexOf(':');\n const name = pair.slice(0, valueIndex);\n const value = pair.slice(valueIndex + 1);\n if (name && value) {\n result[name.trim()] = value.trim();\n }\n });\n return result;\n}\n","/**\n * Set styles to an HTML element. If styles are empty, remove 'style' attribute\n * @param element The element to set styles\n * @param styles The styles to set, in name-value pair format\n */\nexport default function setStyles(element: HTMLElement, styles: Record) {\n if (element) {\n const style = Object.keys(styles || {})\n .map(name => {\n let value = styles[name];\n name = name ? name.trim() : null;\n value = value ? value.trim() : null;\n return name && value ? `${name}:${value}` : null;\n })\n .filter(x => x)\n .join(';');\n if (style) {\n element.setAttribute('style', style);\n } else {\n element.removeAttribute('style');\n }\n }\n}\n","import { Snapshots } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Check whether can move current snapshot with the given step\r\n * @param snapshots The snapshots data structure to check\r\n * @param step The step to check, can be positive, negative or 0\r\n * @returns True if can move current snapshot with the given step, otherwise false\r\n */\r\nexport default function canMoveCurrentSnapshot(snapshots: Snapshots, step: number): boolean {\r\n let newIndex = snapshots.currentIndex + step;\r\n return newIndex >= 0 && newIndex < snapshots.snapshots.length;\r\n}\r\n","import { IMAGE_EDIT_INFO_NAME } from '../types/ImageEditInfo';\n\n/**\n * @internal\n * Delete edit info of an image if any\n * @param image The image to delete edit info from\n */\nexport default function deleteEditInfo(image: HTMLImageElement) {\n if (image) {\n delete image.dataset[IMAGE_EDIT_INFO_NAME];\n }\n}\n","export const IMAGE_EDIT_INFO_NAME = 'roosterEditInfo';\n\n/**\n * @internal\n * Edit info for inline image resize\n */\nexport interface ResizeInfo {\n /**\n * Width after resize, in px.\n * If image is cropped, this is the width of visible part\n * If image is rotated, this is the width before rotation\n * @default clientWidth of the image\n */\n widthPx: number;\n\n /**\n * Height after resize, in px.\n * If image is cropped, this is the height of visible part\n * If image is rotated, this is the height before rotation\n * @default clientHeight of the image\n */\n heightPx: number;\n}\n\n/**\n * @internal\n * Edit info for inline image crop\n */\nexport interface CropInfo {\n /**\n * Left cropped percentage. Rotation or resizing won't impact this percentage value\n * @default 0\n */\n leftPercent: number;\n\n /**\n * Right cropped percentage. Rotation or resizing won't impact this percentage value\n * @default 0\n */\n rightPercent: number;\n\n /**\n * Top cropped percentage. Rotation or resizing won't impact this percentage value\n * @default 0\n */\n topPercent: number;\n\n /**\n * Bottom cropped percentage. Rotation or resizing won't impact this percentage value\n * @default 0\n */\n bottomPercent: number;\n}\n\n/**\n * @internal\n * Edit info for inline image rotate\n */\nexport interface RotateInfo {\n /**\n * Rotated angle of inline image, in radian. Cropping or resizing won't impact this percentage value\n * @default 0\n */\n angleRad: number;\n}\n\n/**\n * @internal\n * Edit info for inline image editing\n */\nexport default interface ImageEditInfo extends ResizeInfo, CropInfo, RotateInfo {\n /**\n * Original src of the image. This value will not be changed when edit image. We can always use it\n * to get the original image so that all editing operation will be on top of the original image.\n */\n readonly src: string;\n\n /**\n * Natural width of the original image (specified by the src field, may not be the current edited image)\n */\n readonly naturalWidth: number;\n\n /**\n * Natural height of the original image (specified by the src field, may not be the current edited image)\n */\n readonly naturalHeight: number;\n}\n","import GeneratedImageSize from '../types/GeneratedImageSize';\nimport ImageEditInfo from '../types/ImageEditInfo';\n\n/**\n * @internal\n * Calculate the target size of an image.\n * For image that is not rotated, target size is the same with resizing/cropping size.\n * For image that is rotated, target size is calculated from resizing/cropping size and its rotate angle\n * Say an image is resized to 100w*100h, cropped 25% on each side, then rotated 45deg, so that cropped size\n * will be (both height and width) 100*(1-0.25-0,25) = 50px, then final image size will be 50*sqrt(2) = 71px\n * @param editInfo The edit info to calculate size from\n * @param beforeCrop True to calculate the full size of original image before crop, false to calculate the size\n * after crop\n * @returns A GeneratedImageSize object which contains original, visible and target target width and height of the image\n */\nexport default function getGeneratedImageSize(\n editInfo: ImageEditInfo,\n beforeCrop?: boolean\n): GeneratedImageSize {\n const {\n widthPx: width,\n heightPx: height,\n angleRad: angle,\n leftPercent: left,\n rightPercent: right,\n topPercent: top,\n bottomPercent: bottom,\n } = editInfo;\n\n // Original image size before crop and rotate\n const originalWidth = width / (1 - left - right);\n const originalHeight = height / (1 - top - bottom);\n\n // Visible size\n const visibleWidth = beforeCrop ? originalWidth : width;\n const visibleHeight = beforeCrop ? originalHeight : height;\n\n // Target size after crop and rotate\n const targetWidth =\n Math.abs(visibleWidth * Math.cos(angle)) + Math.abs(visibleHeight * Math.sin(angle));\n const targetHeight =\n Math.abs(visibleWidth * Math.sin(angle)) + Math.abs(visibleHeight * Math.cos(angle));\n\n return {\n targetWidth,\n targetHeight,\n originalWidth,\n originalHeight,\n visibleWidth,\n visibleHeight,\n };\n}\n","// Classes\r\nexport { default as Editor } from './editor/Editor';\r\n","import contains from '../utils/contains';\r\nimport isNodeAfter from '../utils/isNodeAfter';\r\nimport { BlockElement } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * @internal\r\n * This presents a content block that can be represented by a single html block type element.\r\n * In most cases, it corresponds to an HTML block level element, i.e. P, DIV, LI, TD etc.\r\n */\r\nexport default class NodeBlockElement implements BlockElement {\r\n constructor(private element: HTMLElement) {}\r\n\r\n /**\r\n * Collapse this element to a single DOM element.\r\n * If the content nodes are separated in different root nodes, wrap them to a single node\r\n * If the content nodes are included in root node with other nodes, split root node\r\n */\r\n public collapseToSingleElement(): HTMLElement {\r\n return this.element;\r\n }\r\n\r\n /**\r\n * Get the start node of the block\r\n * For NodeBlockElement, start and end essentially refers to same node\r\n */\r\n public getStartNode(): Node {\r\n return this.element;\r\n }\r\n\r\n /**\r\n * Get the end node of the block\r\n * For NodeBlockElement, start and end essentially refers to same node\r\n */\r\n public getEndNode(): Node {\r\n return this.element;\r\n }\r\n\r\n /**\r\n * Checks if it refers to same block\r\n */\r\n public equals(blockElement: BlockElement): boolean {\r\n // Ideally there is only one unique way to generate a block so we only need to compare the startNode\r\n return this.element == blockElement.getStartNode();\r\n }\r\n\r\n /**\r\n * Checks if a block is after the current block\r\n */\r\n public isAfter(blockElement: BlockElement): boolean {\r\n // if the block's startNode is after current node endEnd, we say it is after\r\n return isNodeAfter(this.element, blockElement.getEndNode());\r\n }\r\n\r\n /**\r\n * Checks if a certain html node is within the block\r\n */\r\n public contains(node: Node): boolean {\r\n return contains(this.element, node, true /*treatSameNodeAsContain*/);\r\n }\r\n\r\n /**\r\n * Get the text content of this block element\r\n */\r\n public getTextContent(): string {\r\n return this.element ? this.element.textContent : '';\r\n }\r\n}\r\n","import safeInstanceOf from './safeInstanceOf';\n\n/**\n * Type definition of HTMLElement interface for IE\n */\ninterface HTMLElementForIE extends HTMLElement {\n /**\n * IE implementation of Element.matches() function\n *\n */\n msMatchesSelector: (selector: string) => boolean;\n}\n\n/**\n * A wrapper function of Element.matches\n * @param element The element to match\n * @param selector The selector to match\n */\nexport default function matchesSelector(element: Node, selector: string): boolean {\n return (\n safeInstanceOf(element, 'HTMLElement') &&\n (element.matches || (element).msMatchesSelector).call(element, selector)\n );\n}\n","import toArray from './toArray';\r\n\r\n/**\r\n * @deprecated\r\n * Creates an HTML node array from html\r\n * @param html the html string to create HTML elements from\r\n * @param ownerDocument Owner document of the result HTML elements\r\n * @returns An HTML node array to represent the given html string\r\n */\r\nexport default function fromHtml(html: string, ownerDocument: HTMLDocument): Node[] {\r\n let element = ownerDocument.createElement('DIV');\r\n element.innerHTML = html;\r\n\r\n return toArray(element.childNodes);\r\n}\r\n","import getBlockElementAtNode from './getBlockElementAtNode';\r\nimport { BlockElement } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Get the first/last BlockElement of under the root node.\r\n * If no suitable BlockElement found, returns null\r\n * @param rootNode The root node to get BlockElement from\r\n * @param isFirst True to get first BlockElement, false to get last BlockElement\r\n */\r\nexport default function getFirstLastBlockElement(rootNode: Node, isFirst: boolean): BlockElement {\r\n let node = rootNode;\r\n do {\r\n node = node && (isFirst ? node.firstChild : node.lastChild);\r\n } while (node && node.firstChild);\r\n return node && getBlockElementAtNode(rootNode, node);\r\n}\r\n","import NodeInlineElement from './NodeInlineElement';\r\nimport { BlockElement } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * This is an inline element representing an Html image\r\n */\r\nexport default class ImageInlineElement extends NodeInlineElement {\r\n constructor(containerNode: Node, parentBlock: BlockElement) {\r\n super(containerNode, parentBlock);\r\n }\r\n}\r\n","import getInlineElementAtNode from './getInlineElementAtNode';\r\nimport { getFirstLeafNode, getLastLeafNode } from '../utils/getLeafNode';\r\nimport { InlineElement } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * @internal\r\n * Get the first inline element inside the given node\r\n */\r\nexport function getFirstInlineElement(rootNode: Node): InlineElement {\r\n // getFirstLeafNode can return null for empty container\r\n // do check null before passing on to get inline from the node\r\n let node = getFirstLeafNode(rootNode);\r\n return node ? getInlineElementAtNode(rootNode, node) : null;\r\n}\r\n\r\n/**\r\n * @internal\r\n * Get the last inline element inside the given node\r\n */\r\nexport function getLastInlineElement(rootNode: Node): InlineElement {\r\n // getLastLeafNode can return null for empty container\r\n // do check null before passing on to get inline from the node\r\n let node = getLastLeafNode(rootNode);\r\n return node ? getInlineElementAtNode(rootNode, node) : null;\r\n}\r\n","import shouldSkipNode from './shouldSkipNode';\r\nimport { getLeafSibling } from './getLeafSibling';\r\n\r\n/**\r\n * Get first/last leaf node of the given root node.\r\n * @param rootNode Root node to get leaf node from\r\n * @param isFirst True to get first leaf node, false to get last leaf node\r\n */\r\nfunction getLeafNode(rootNode: Node, isFirst: boolean): Node {\r\n let getChild = (node: Node): Node => (isFirst ? node.firstChild : node.lastChild);\r\n let result = getChild(rootNode);\r\n while (result && getChild(result)) {\r\n result = getChild(result);\r\n }\r\n\r\n if (result && shouldSkipNode(result)) {\r\n result = getLeafSibling(rootNode, result, isFirst);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Get the first meaningful leaf node\r\n * @param rootNode Root node to get leaf node from\r\n */\r\nexport function getFirstLeafNode(rootNode: Node): Node {\r\n return getLeafNode(rootNode, true /*isFirst*/);\r\n}\r\n\r\n/**\r\n * Get the last meaningful leaf node\r\n * @param rootNode Root node to get leaf node from\r\n */\r\nexport function getLastLeafNode(rootNode: Node): Node {\r\n return getLeafNode(rootNode, false /*isFirst*/);\r\n}\r\n","import { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * @internal\r\n * Represents an empty InlineElement.\r\n * This is used for ContentTraverser internally only.\r\n * An empty InlineElement means current position is at the end of a tag so nothing is included inside this element\r\n */\r\nexport default class EmptyInlineElement implements InlineElement {\r\n constructor(private position: NodePosition, private parentBlock: BlockElement) {}\r\n\r\n /**\r\n * Get the text content of this inline element\r\n */\r\n getTextContent(): string {\r\n return '';\r\n }\r\n\r\n /**\r\n * Get the container node of this inline element\r\n */\r\n getContainerNode(): Node {\r\n return this.position.node;\r\n }\r\n\r\n /**\r\n * Get the parent block element of this inline element\r\n */\r\n getParentBlock(): BlockElement {\r\n return this.parentBlock;\r\n }\r\n\r\n /**\r\n * Get the start position of this inline element\r\n */\r\n getStartPosition(): NodePosition {\r\n return this.position;\r\n }\r\n\r\n /**\r\n * Get the end position of this inline element\r\n */\r\n getEndPosition(): NodePosition {\r\n return this.position;\r\n }\r\n\r\n /**\r\n * Checks if the given inline element is after this inline element\r\n */\r\n isAfter(inlineElement: InlineElement): boolean {\r\n return inlineElement && this.position.isAfter(inlineElement.getEndPosition());\r\n }\r\n\r\n /**\r\n * Checks if this inline element is a textual inline element\r\n */\r\n isTextualInlineElement(): boolean {\r\n return false;\r\n }\r\n\r\n /**\r\n * Checks if the given editor position is contained in this inline element\r\n */\r\n contains(position: NodePosition): boolean {\r\n return false;\r\n }\r\n\r\n /**\r\n * Apply inline style to a region of an inline element.\r\n */\r\n applyStyle(styler: (element: HTMLElement) => any): void {}\r\n}\r\n","import ContentTraverser from './ContentTraverser';\r\nimport createRange from '../selection/createRange';\r\nimport {\r\n IContentTraverser,\r\n InlineElement,\r\n IPositionContentSearcher,\r\n NodePosition,\r\n} from 'roosterjs-editor-types';\r\n\r\n// White space matching regex. It matches following chars:\r\n// \\s: white space\r\n// \\u00A0: no-breaking white space\r\n// \\u200B: zero width space\r\n// \\u3000: full width space (which can come from JPN IME)\r\nconst WHITESPACE_REGEX = /[\\s\\u00A0\\u200B\\u3000]+([^\\s\\u00A0\\u200B\\u3000]*)$/i;\r\n\r\n/**\r\n * The class that helps search content around a position\r\n */\r\nexport default class PositionContentSearcher implements IPositionContentSearcher {\r\n // The cached text before position that has been read so far\r\n private text = '';\r\n\r\n // The cached word before position\r\n private word: string;\r\n\r\n // The inline element before position\r\n private inlineBefore: InlineElement;\r\n\r\n // The inline element after position\r\n private inlineAfter: InlineElement;\r\n\r\n // The content traverser used to traverse backwards\r\n private traverser: IContentTraverser;\r\n\r\n // Backward parsing has completed\r\n private traversingComplete: boolean;\r\n\r\n // All inline elements before position that have been read so far\r\n private inlineElements: InlineElement[] = [];\r\n\r\n // First non-text inline before position\r\n private nearestNonTextInlineElement: InlineElement;\r\n\r\n /**\r\n * Create a new CursorData instance\r\n * @param rootNode Root node of the whole scope\r\n * @param position Start position\r\n */\r\n constructor(private rootNode: Node, private position: NodePosition) {}\r\n\r\n /**\r\n * Get the word before position. The word is determined by scanning backwards till the first white space, the portion\r\n * between position and the white space is the word before position\r\n * @returns The word before position\r\n */\r\n public getWordBefore(): string {\r\n if (!this.word) {\r\n this.traverse(() => this.word);\r\n }\r\n\r\n return this.word;\r\n }\r\n\r\n /**\r\n * Get the inline element before position\r\n * @returns The inlineElement before position\r\n */\r\n public getInlineElementBefore(): InlineElement {\r\n if (!this.inlineBefore) {\r\n this.traverse(null);\r\n }\r\n\r\n return this.inlineBefore;\r\n }\r\n\r\n /**\r\n * Get the inline element after position\r\n * @returns The inline element after position\r\n */\r\n public getInlineElementAfter(): InlineElement {\r\n if (!this.inlineAfter) {\r\n this.inlineAfter = ContentTraverser.createBlockTraverser(\r\n this.rootNode,\r\n this.position\r\n ).currentInlineElement;\r\n }\r\n\r\n return this.inlineAfter;\r\n }\r\n\r\n /**\r\n * Get X number of chars before position\r\n * The actual returned chars may be less than what is requested.\r\n * @param length The length of string user want to get, the string always ends at the position,\r\n * so this length determines the start position of the string\r\n * @returns The actual string we get as a sub string, or the whole string before position when\r\n * there is not enough chars in the string\r\n */\r\n public getSubStringBefore(length: number): string {\r\n if (this.text.length < length) {\r\n this.traverse(() => this.text.length >= length);\r\n }\r\n\r\n return this.text.substr(Math.max(0, this.text.length - length));\r\n }\r\n\r\n /**\r\n * Try to get a range matches the given text before the position\r\n * @param text The text to match against\r\n * @param exactMatch Whether it is an exact match\r\n * @returns The range for the matched text, null if unable to find a match\r\n */\r\n public getRangeFromText(text: string, exactMatch: boolean): Range {\r\n if (!text) {\r\n return null;\r\n }\r\n\r\n let startPosition: NodePosition;\r\n let endPosition: NodePosition;\r\n let textIndex = text.length - 1;\r\n\r\n this.forEachTextInlineElement(textInline => {\r\n let nodeContent = textInline.getTextContent() || '';\r\n let nodeIndex = nodeContent.length - 1;\r\n for (; nodeIndex >= 0 && textIndex >= 0; nodeIndex--) {\r\n if (text.charCodeAt(textIndex) == nodeContent.charCodeAt(nodeIndex)) {\r\n textIndex--;\r\n\r\n // on first time when end is matched, set the end of range\r\n if (!endPosition) {\r\n endPosition = textInline.getStartPosition().move(nodeIndex + 1);\r\n }\r\n } else if (exactMatch || endPosition) {\r\n // Mismatch found when exact match or end already match, so return since matching failed\r\n return true;\r\n }\r\n }\r\n\r\n // when textIndex == -1, we have a successful complete match\r\n if (textIndex == -1) {\r\n startPosition = textInline.getStartPosition().move(nodeIndex + 1);\r\n return true;\r\n }\r\n\r\n return false;\r\n });\r\n\r\n return startPosition && endPosition && createRange(startPosition, endPosition);\r\n }\r\n\r\n /**\r\n * Get text section before position till stop condition is met.\r\n * This offers consumers to retrieve text section by section\r\n * The section essentially is just an inline element which has Container element\r\n * so that the consumer can remember it for anchoring popup or verification purpose\r\n * when position moves out of context etc.\r\n * @param stopFunc The callback stop function\r\n */\r\n public forEachTextInlineElement(callback: (textInlineElement: InlineElement) => any) {\r\n // We cache all text sections read so far\r\n // Every time when you ask for textSection, we start with the cached first\r\n // and resort to further reading once we exhausted with the cache\r\n if (!this.inlineElements.some(callback)) {\r\n this.traverse(callback);\r\n }\r\n }\r\n\r\n /**\r\n * Get first non textual inline element before position\r\n * @returns First non textual inline element before position or null if no such element exists\r\n */\r\n public getNearestNonTextInlineElement(): InlineElement {\r\n if (!this.nearestNonTextInlineElement) {\r\n this.traverse(() => this.nearestNonTextInlineElement);\r\n }\r\n\r\n return this.nearestNonTextInlineElement;\r\n }\r\n\r\n /**\r\n * Continue traversing backward till stop condition is met or begin of block is reached\r\n */\r\n private traverse(callback: (inlineElement: InlineElement) => any) {\r\n this.traverser =\r\n this.traverser || ContentTraverser.createBlockTraverser(this.rootNode, this.position);\r\n\r\n if (!this.traverser || this.traversingComplete) {\r\n return;\r\n }\r\n\r\n let previousInline = this.traverser.getPreviousInlineElement();\r\n while (!this.traversingComplete) {\r\n this.inlineBefore = this.inlineBefore || previousInline;\r\n\r\n if (previousInline && previousInline.isTextualInlineElement()) {\r\n let textContent = previousInline.getTextContent();\r\n\r\n // build the word before position if it is not built yet\r\n if (!this.word) {\r\n // Match on the white space, the portion after space is on the index of 1 of the matched result\r\n // (index at 0 is whole match result, index at 1 is the word)\r\n let matches = WHITESPACE_REGEX.exec(textContent);\r\n if (matches && matches.length == 2) {\r\n this.word = matches[1] + this.text;\r\n }\r\n }\r\n\r\n this.text = textContent + this.text;\r\n this.inlineElements.push(previousInline);\r\n\r\n // Check if stop condition is met\r\n if (callback && callback(previousInline)) {\r\n break;\r\n }\r\n } else {\r\n this.nearestNonTextInlineElement = previousInline;\r\n this.traversingComplete = true;\r\n if (!this.word) {\r\n // if parsing is done, whatever we get so far in this.cachedText should also be in this.cachedWordBeforeCursor\r\n this.word = this.text;\r\n }\r\n\r\n // When a non-textual inline element, or null is seen, we consider parsing complete\r\n // TODO: we may need to change this if there is a future need to parse beyond text, i.e.\r\n // we have aaa @someone bbb, and we want to read the text before @someone\r\n break;\r\n }\r\n\r\n previousInline = this.traverser.getPreviousInlineElement();\r\n }\r\n }\r\n}\r\n","import readFile from '../utils/readFile';\nimport { Browser } from '../utils/Browser';\nimport {\n ClipboardData,\n ContentType,\n ContentTypePrefix,\n EdgeLinkPreview,\n ExtractClipboardItemsOption,\n} from 'roosterjs-editor-types';\n\n// HTML header to indicate where is the HTML content started from.\n// Sample header:\n// Version:0.9\n// StartHTML:71\n// EndHTML:170\n// StartFragment:140\n// EndFragment:160\n// StartSelection:140\n// EndSelection:160\nconst CLIPBOARD_HTML_HEADER_REGEX = /^Version:[0-9\\.]+\\s+StartHTML:\\s*([0-9]+)\\s+EndHTML:\\s*([0-9]+)\\s+/i;\nconst OTHER_TEXT_TYPE = ContentTypePrefix.Text + '*';\nconst EDGE_LINK_PREVIEW = 'link-preview';\nconst ContentHandlers: {\n [contentType: string]: (data: ClipboardData, value: string, type: string) => void;\n} = {\n [ContentType.HTML]: (data, value) =>\n (data.rawHtml = Browser.isEdge ? workaroundForEdge(value) : value),\n [ContentType.PlainText]: (data, value) => (data.text = value),\n [OTHER_TEXT_TYPE]: (data, value, type) => (data.customValues[type] = value),\n};\n\n/**\n * Extract clipboard items to be a ClipboardData object for IE\n * @param items The clipboard items retrieve from a DataTransfer object\n * @param callback Callback function when data is ready\n * @returns An object with the following properties:\n * types: Available types from the clipboard event\n * text: Plain text from the clipboard event\n * image: Image file from the clipboard event\n * html: Html string from the clipboard event. When set to null, it means there's no HTML found from the event.\n * When set to undefined, it means can't retrieve HTML string, there may be HTML string but direct retrieving is\n * not supported by browser.\n */\nexport default function extractClipboardItems(\n items: DataTransferItem[],\n options?: ExtractClipboardItemsOption\n): Promise {\n const data: ClipboardData = {\n types: [],\n text: '',\n image: null,\n rawHtml: null,\n customValues: {},\n };\n\n const contentHandlers = { ...ContentHandlers };\n\n if (options?.allowLinkPreview) {\n contentHandlers[ContentTypePrefix.Text + EDGE_LINK_PREVIEW] = tryParseLinkPreview;\n }\n\n return Promise.all(\n (items || []).map(item => {\n const type = item.type;\n\n if (type.indexOf(ContentTypePrefix.Image) == 0 && !data.image) {\n data.types.push(type);\n data.image = item.getAsFile();\n return new Promise(resolve => {\n readFile(data.image, dataUrl => {\n data.imageDataUri = dataUrl;\n resolve();\n });\n });\n } else {\n const customType = getAllowedCustomType(type, options?.allowedCustomPasteType);\n const handler =\n contentHandlers[type] || (customType ? contentHandlers[OTHER_TEXT_TYPE] : null);\n return new Promise(resolve =>\n handler\n ? item.getAsString(value => {\n data.types.push(type);\n handler(data, value, customType);\n resolve();\n })\n : resolve()\n );\n }\n })\n ).then(() => data);\n}\n\n/**\n * Edge sometimes doesn't remove the headers, which cause we paste more things then expected.\n * So we need to remove it in our code\n * @param html The HTML string got from clipboard\n */\nfunction workaroundForEdge(html: string) {\n const headerValues = CLIPBOARD_HTML_HEADER_REGEX.exec(html);\n\n if (headerValues?.length == 3) {\n const start = parseInt(headerValues[1]);\n const end = parseInt(headerValues[2]);\n if (start > 0 && end > start) {\n html = html.substring(start, end);\n }\n }\n\n return html;\n}\n\nfunction tryParseLinkPreview(data: ClipboardData, value: string) {\n try {\n data.customValues[EDGE_LINK_PREVIEW] = value;\n data.linkPreview = JSON.parse(value) as EdgeLinkPreview;\n } catch {}\n}\n\nfunction getAllowedCustomType(type: string, allowedCustomPasteType: string[]) {\n let textType =\n type.indexOf(ContentTypePrefix.Text) == 0\n ? type.substr(ContentTypePrefix.Text.length)\n : null;\n return textType && allowedCustomPasteType?.indexOf(textType) >= 0 ? textType : null;\n}\n","import readFile from '../utils/readFile';\nimport toArray from '../utils/toArray';\nimport {\n ClipboardData,\n ContentTypePrefix,\n ExtractClipboardItemsForIEOptions,\n} from 'roosterjs-editor-types';\n\n/**\n * Extract clipboard items to be a ClipboardData object for IE\n * @param dataTransfer The clipboard items retrieve from a DataTransfer object\n * @param callback Callback function when data is ready\n * @returns An object with the following properties:\n * types: Available types from the clipboard event\n * text: Plain text from the clipboard event\n * image: Image file from the clipboard event\n * html: Html string from the clipboard event. When set to null, it means there's no HTML found from the event.\n * When set to undefined, it means can't retrieve HTML string, there may be HTML string but direct retrieving is\n * not supported by browser.\n */\nexport default function extractClipboardItemsForIE(\n dataTransfer: DataTransfer,\n callback: (data: ClipboardData) => void,\n options: ExtractClipboardItemsForIEOptions\n) {\n const clipboardData: ClipboardData = {\n types: dataTransfer.types ? toArray(dataTransfer.types) : [],\n text: dataTransfer.getData('text'),\n image: null,\n rawHtml: null,\n customValues: {},\n };\n\n for (let i = 0; i < (dataTransfer.files ? dataTransfer.files.length : 0); i++) {\n let file = dataTransfer.files.item(i);\n if (file.type?.indexOf(ContentTypePrefix.Image) == 0) {\n clipboardData.image = file;\n break;\n }\n }\n\n const nextStep = () => {\n if (clipboardData.image) {\n readFile(clipboardData.image, dataUrl => {\n clipboardData.imageDataUri = dataUrl;\n callback(clipboardData);\n });\n } else {\n callback(clipboardData);\n }\n };\n\n if (options?.getTempDiv && options?.removeTempDiv) {\n const div = options.getTempDiv();\n div.contentEditable = 'true';\n div.innerHTML = '';\n div.focus();\n div.ownerDocument.defaultView.setTimeout(() => {\n clipboardData.rawHtml = div.innerHTML;\n options.removeTempDiv(div);\n nextStep();\n }, 0);\n } else {\n clipboardData.rawHtml = undefined;\n nextStep();\n }\n}\n","import { DarkModeDatasetNames, ModeIndependentColor } from 'roosterjs-editor-types';\n\nexport default function setColor(\n element: HTMLElement,\n color: string | ModeIndependentColor,\n isBackgroundColor: boolean,\n isDarkMode?: boolean\n) {\n const colorString = typeof color === 'string' ? color.trim() : '';\n const modeIndependentColor = typeof color === 'string' ? null : color;\n\n if (colorString || modeIndependentColor) {\n element.style.setProperty(\n isBackgroundColor ? 'background-color' : 'color',\n (isDarkMode\n ? modeIndependentColor?.darkModeColor\n : modeIndependentColor?.lightModeColor) || colorString\n );\n\n if (element.dataset) {\n const dataSetName = isBackgroundColor\n ? DarkModeDatasetNames.OriginalStyleBackgroundColor\n : DarkModeDatasetNames.OriginalStyleColor;\n if (!isDarkMode) {\n delete element.dataset[dataSetName];\n } else if (modeIndependentColor) {\n element.dataset[dataSetName] = modeIndependentColor.lightModeColor;\n }\n }\n }\n}\n","import safeInstanceOf from '../utils/safeInstanceOf';\n\n/**\n * Get innerHTML of the given node\n * @param node The DOM node to get innerHTML from\n */\nexport default function getInnerHTML(node: HTMLElement | DocumentFragment) {\n if (safeInstanceOf(node, 'HTMLElement')) {\n return node.innerHTML;\n } else if (node) {\n const tempNode = node.ownerDocument.createElement('span');\n tempNode.appendChild(node.cloneNode(true /*deep*/));\n return tempNode.innerHTML;\n } else {\n return '';\n }\n}\n","import contains from '../utils/contains';\r\nimport getTagOfNode from '../utils/getTagOfNode';\r\nimport isNodeEmpty from '../utils/isNodeEmpty';\r\nimport { NodePosition } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Check if this position is at beginning of the given node.\r\n * This will return true if all nodes between the beginning of target node and the position are empty.\r\n * @param position The position to check\r\n * @param targetNode The node to check\r\n * @returns True if position is at beginning of the node, otherwise false\r\n */\r\nexport default function isPositionAtBeginningOf(position: NodePosition, targetNode: Node) {\r\n if (position) {\r\n let { node, offset } = position.normalize();\r\n if (offset == 0) {\r\n while (contains(targetNode, node) && areAllPreviousNodesEmpty(node)) {\r\n node = node.parentNode;\r\n }\r\n\r\n return node == targetNode;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\nfunction areAllPreviousNodesEmpty(node: Node): boolean {\r\n while (node.previousSibling) {\r\n node = node.previousSibling;\r\n if (getTagOfNode(node) == 'BR' || !isNodeEmpty(node)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n","import moveChildNodes from '../utils/moveChildNodes';\r\nimport normalizeRect from '../utils/normalizeRect';\r\nimport safeInstanceOf from '../utils/safeInstanceOf';\r\nimport toArray from '../utils/toArray';\r\nimport { TableFormat, TableOperation, VCell } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * A virtual table class, represent an HTML table, by expand all merged cells to each separated cells\r\n */\r\nexport default class VTable {\r\n /**\r\n * The HTML table object\r\n */\r\n table: HTMLTableElement;\r\n\r\n /**\r\n * Virtual cells\r\n */\r\n cells: VCell[][];\r\n\r\n /**\r\n * Current row index\r\n */\r\n row: number;\r\n\r\n /**\r\n * Current column index\r\n */\r\n col: number;\r\n\r\n private trs: HTMLTableRowElement[] = [];\r\n\r\n /**\r\n * Create a new instance of VTable object using HTML TABLE or TD node\r\n * @param node The HTML Table or TD node\r\n */\r\n constructor(node: HTMLTableElement | HTMLTableCellElement, normalizeSize?: boolean) {\r\n this.table = safeInstanceOf(node, 'HTMLTableElement') ? node : getTableFromTd(node);\r\n if (this.table) {\r\n let currentTd = safeInstanceOf(node, 'HTMLTableElement') ? null : node;\r\n let trs = toArray(this.table.rows);\r\n this.cells = trs.map(row => []);\r\n trs.forEach((tr, rowIndex) => {\r\n this.trs[rowIndex % 2] = tr;\r\n for (let sourceCol = 0, targetCol = 0; sourceCol < tr.cells.length; sourceCol++) {\r\n // Skip the cells which already initialized\r\n for (; this.cells[rowIndex][targetCol]; targetCol++) {}\r\n\r\n let td = tr.cells[sourceCol];\r\n if (td == currentTd) {\r\n this.col = targetCol;\r\n this.row = rowIndex;\r\n }\r\n\r\n for (let colSpan = 0; colSpan < td.colSpan; colSpan++, targetCol++) {\r\n for (let rowSpan = 0; rowSpan < td.rowSpan; rowSpan++) {\r\n const hasTd: boolean = colSpan + rowSpan == 0;\r\n const rect = td.getBoundingClientRect();\r\n this.cells[rowIndex + rowSpan][targetCol] = {\r\n td: hasTd ? td : null,\r\n spanLeft: colSpan > 0,\r\n spanAbove: rowSpan > 0,\r\n width: hasTd ? rect.width : undefined,\r\n height: hasTd ? rect.height : undefined,\r\n };\r\n }\r\n }\r\n }\r\n });\r\n\r\n if (normalizeSize) {\r\n this.normalizeSize();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Write the virtual table back to DOM tree to represent the change of VTable\r\n */\r\n writeBack() {\r\n if (this.cells) {\r\n moveChildNodes(this.table);\r\n this.cells.forEach((row, r) => {\r\n let tr = cloneNode(this.trs[r % 2] || this.trs[0]);\r\n this.table.appendChild(tr);\r\n row.forEach((cell, c) => {\r\n if (cell.td) {\r\n this.recalculateSpans(r, c);\r\n tr.appendChild(cell.td);\r\n }\r\n });\r\n });\r\n } else if (this.table) {\r\n this.table.parentNode.removeChild(this.table);\r\n }\r\n }\r\n\r\n /**\r\n * Apply the given table format to this virtual table\r\n * @param format Table format to apply\r\n */\r\n applyFormat(format: Partial) {\r\n if (!format || !this.table) {\r\n return;\r\n }\r\n this.table.style.borderCollapse = 'collapse';\r\n this.trs[0].style.backgroundColor = format.bgColorOdd || 'transparent';\r\n if (this.trs[1]) {\r\n this.trs[1].style.backgroundColor = format.bgColorEven || 'transparent';\r\n }\r\n this.cells.forEach(row =>\r\n row\r\n .filter(cell => cell.td)\r\n .forEach(cell => {\r\n cell.td.style.borderTop = getBorderStyle(format.topBorderColor);\r\n cell.td.style.borderBottom = getBorderStyle(format.bottomBorderColor);\r\n cell.td.style.borderLeft = getBorderStyle(format.verticalBorderColor);\r\n cell.td.style.borderRight = getBorderStyle(format.verticalBorderColor);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * Edit table with given operation.\r\n * @param operation Table operation\r\n */\r\n edit(operation: TableOperation) {\r\n if (!this.table) {\r\n return;\r\n }\r\n\r\n let currentRow = this.cells[this.row];\r\n let currentCell = currentRow[this.col];\r\n switch (operation) {\r\n case TableOperation.InsertAbove:\r\n this.cells.splice(this.row, 0, currentRow.map(cloneCell));\r\n break;\r\n case TableOperation.InsertBelow:\r\n let newRow = this.row + this.countSpanAbove(this.row, this.col);\r\n this.cells.splice(\r\n newRow,\r\n 0,\r\n this.cells[newRow - 1].map((cell, colIndex) => {\r\n let nextCell = this.getCell(newRow, colIndex);\r\n if (nextCell.spanAbove) {\r\n return cloneCell(nextCell);\r\n } else if (cell.spanLeft) {\r\n let newCell = cloneCell(cell);\r\n newCell.spanAbove = false;\r\n return newCell;\r\n } else {\r\n return {\r\n td: cloneNode(this.getTd(this.row, colIndex)),\r\n };\r\n }\r\n })\r\n );\r\n break;\r\n\r\n case TableOperation.InsertLeft:\r\n this.forEachCellOfCurrentColumn((cell, row) => {\r\n row.splice(this.col, 0, cloneCell(cell));\r\n });\r\n break;\r\n case TableOperation.InsertRight:\r\n let newCol = this.col + this.countSpanLeft(this.row, this.col);\r\n this.forEachCellOfColumn(newCol - 1, (cell, row, i) => {\r\n let nextCell = this.getCell(i, newCol);\r\n let newCell: VCell;\r\n if (nextCell.spanLeft) {\r\n newCell = cloneCell(nextCell);\r\n } else if (cell.spanAbove) {\r\n newCell = cloneCell(cell);\r\n newCell.spanLeft = false;\r\n } else {\r\n newCell = {\r\n td: cloneNode(this.getTd(i, this.col)),\r\n };\r\n }\r\n\r\n row.splice(newCol, 0, newCell);\r\n });\r\n break;\r\n\r\n case TableOperation.DeleteRow:\r\n this.forEachCellOfCurrentRow((cell, i) => {\r\n let nextCell = this.getCell(this.row + 1, i);\r\n if (cell.td && cell.td.rowSpan > 1 && nextCell.spanAbove) {\r\n nextCell.td = cell.td;\r\n }\r\n });\r\n this.cells.splice(this.row, 1);\r\n break;\r\n\r\n case TableOperation.DeleteColumn:\r\n this.forEachCellOfCurrentColumn((cell, row, i) => {\r\n let nextCell = this.getCell(i, this.col + 1);\r\n if (cell.td && cell.td.colSpan > 1 && nextCell.spanLeft) {\r\n nextCell.td = cell.td;\r\n }\r\n row.splice(this.col, 1);\r\n });\r\n break;\r\n\r\n case TableOperation.MergeAbove:\r\n case TableOperation.MergeBelow:\r\n let rowStep = operation == TableOperation.MergeAbove ? -1 : 1;\r\n for (\r\n let rowIndex = this.row + rowStep;\r\n rowIndex >= 0 && rowIndex < this.cells.length;\r\n rowIndex += rowStep\r\n ) {\r\n let cell = this.getCell(rowIndex, this.col);\r\n if (cell.td && !cell.spanAbove) {\r\n let aboveCell = rowIndex < this.row ? cell : currentCell;\r\n let belowCell = rowIndex < this.row ? currentCell : cell;\r\n if (aboveCell.td.colSpan == belowCell.td.colSpan) {\r\n moveChildNodes(\r\n aboveCell.td,\r\n belowCell.td,\r\n true /*keepExistingChildren*/\r\n );\r\n belowCell.td = null;\r\n belowCell.spanAbove = true;\r\n }\r\n break;\r\n }\r\n }\r\n break;\r\n\r\n case TableOperation.MergeLeft:\r\n case TableOperation.MergeRight:\r\n let colStep = operation == TableOperation.MergeLeft ? -1 : 1;\r\n for (\r\n let colIndex = this.col + colStep;\r\n colIndex >= 0 && colIndex < this.cells[this.row].length;\r\n colIndex += colStep\r\n ) {\r\n let cell = this.getCell(this.row, colIndex);\r\n if (cell.td && !cell.spanLeft) {\r\n let leftCell = colIndex < this.col ? cell : currentCell;\r\n let rightCell = colIndex < this.col ? currentCell : cell;\r\n if (leftCell.td.rowSpan == rightCell.td.rowSpan) {\r\n moveChildNodes(\r\n leftCell.td,\r\n rightCell.td,\r\n true /*keepExistingChildren*/\r\n );\r\n rightCell.td = null;\r\n rightCell.spanLeft = true;\r\n }\r\n break;\r\n }\r\n }\r\n break;\r\n\r\n case TableOperation.DeleteTable:\r\n this.cells = null;\r\n break;\r\n\r\n case TableOperation.SplitVertically:\r\n if (currentCell.td.rowSpan > 1) {\r\n this.getCell(this.row + 1, this.col).td = cloneNode(currentCell.td);\r\n } else {\r\n let splitRow = currentRow.map(cell => {\r\n return {\r\n td: cell == currentCell ? cloneNode(cell.td) : null,\r\n spanAbove: cell != currentCell,\r\n spanLeft: cell.spanLeft,\r\n };\r\n });\r\n this.cells.splice(this.row + 1, 0, splitRow);\r\n }\r\n break;\r\n\r\n case TableOperation.SplitHorizontally:\r\n if (currentCell.td.colSpan > 1) {\r\n this.getCell(this.row, this.col + 1).td = cloneNode(currentCell.td);\r\n } else {\r\n this.forEachCellOfCurrentColumn((cell, row) => {\r\n row.splice(this.col + 1, 0, {\r\n td: row == currentRow ? cloneNode(cell.td) : null,\r\n spanAbove: cell.spanAbove,\r\n spanLeft: row != currentRow,\r\n });\r\n });\r\n }\r\n break;\r\n\r\n case TableOperation.AlignCenter:\r\n this.table.setAttribute('align', 'center');\r\n break;\r\n case TableOperation.AlignLeft:\r\n this.table.setAttribute('align', 'left');\r\n break;\r\n case TableOperation.AlignRight:\r\n this.table.setAttribute('align', 'right');\r\n break;\r\n }\r\n }\r\n\r\n /**\r\n * Loop each cell of current column and invoke a callback function\r\n * @param callback The callback function to invoke\r\n */\r\n forEachCellOfCurrentColumn(callback: (cell: VCell, row: VCell[], i: number) => any) {\r\n this.forEachCellOfColumn(this.col, callback);\r\n }\r\n\r\n /**\r\n * Loop each table cell and get all the cells that share the same border from one side\r\n * The result is an array of table cell elements\r\n * @param borderPos The position of the border\r\n * @param getLeftCells Get left-hand-side or right-hand-side cells of the border\r\n *\r\n * Example, consider having a 3 by 4 table as below with merged and split cells\r\n *\r\n * | 1 | 4 | 7 | 8 |\r\n * | 5 | 9 |\r\n * | 3 | 10 |\r\n *\r\n * input => borderPos: the 3rd border, getLeftCells: true\r\n * output => [4, 5, 3]\r\n *\r\n * input => borderPos: the 3rd border, getLeftCells: false\r\n * output => [7, 9, 10]\r\n *\r\n * input => borderPos: the 2nd border, getLeftCells: true\r\n * output => [1]\r\n *\r\n * input => borderPos: the 2nd border, getLeftCells: false\r\n * output => [4]\r\n */\r\n getCellsWithBorder(borderPos: number, getLeftCells: boolean): HTMLTableCellElement[] {\r\n const cells: HTMLTableCellElement[] = [];\r\n for (let i = 0; i < this.cells.length; i++) {\r\n for (let j = 0; j < this.cells[i].length; j++) {\r\n const cell = this.getCell(i, j);\r\n if (cell.td) {\r\n const cellRect = normalizeRect(cell.td.getBoundingClientRect());\r\n let found: boolean = false;\r\n if (getLeftCells) {\r\n if (cellRect.right == borderPos) {\r\n found = true;\r\n cells.push(cell.td);\r\n } else if (found) {\r\n break;\r\n }\r\n } else {\r\n if (cellRect.left == borderPos) {\r\n found = true;\r\n cells.push(cell.td);\r\n } else if (found) {\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n return cells;\r\n }\r\n\r\n /**\r\n * Loop each cell of current row and invoke a callback function\r\n * @param callback The callback function to invoke\r\n */\r\n forEachCellOfCurrentRow(callback: (cell: VCell, i: number) => any) {\r\n this.forEachCellOfRow(this.row, callback);\r\n }\r\n\r\n /**\r\n * Get a table cell using its row and column index. This function will always return an object\r\n * even if the given indexes don't exist in table.\r\n * @param row The row index\r\n * @param col The column index\r\n */\r\n getCell(row: number, col: number): VCell {\r\n return (this.cells && this.cells[row] && this.cells[row][col]) || {};\r\n }\r\n\r\n /**\r\n * Get current HTML table cell object. If the current table cell is a virtual expanded cell, return its root cell\r\n */\r\n getCurrentTd(): HTMLTableCellElement {\r\n return this.getTd(this.row, this.col);\r\n }\r\n\r\n private getTd(row: number, col: number) {\r\n if (this.cells) {\r\n row = Math.min(this.cells.length - 1, row);\r\n col = this.cells[row] ? Math.min(this.cells[row].length - 1, col) : col;\r\n if (!isNaN(row) && !isNaN(col)) {\r\n while (row >= 0 && col >= 0) {\r\n let cell = this.getCell(row, col);\r\n if (cell.td) {\r\n return cell.td;\r\n } else if (cell.spanLeft) {\r\n col--;\r\n } else if (cell.spanAbove) {\r\n row--;\r\n } else {\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n return null;\r\n }\r\n\r\n private forEachCellOfColumn(\r\n col: number,\r\n callback: (cell: VCell, row: VCell[], i: number) => any\r\n ) {\r\n for (let i = 0; i < this.cells.length; i++) {\r\n callback(this.getCell(i, col), this.cells[i], i);\r\n }\r\n }\r\n\r\n private forEachCellOfRow(row: number, callback: (cell: VCell, i: number) => any) {\r\n for (let i = 0; i < this.cells[row].length; i++) {\r\n callback(this.getCell(row, i), i);\r\n }\r\n }\r\n\r\n private recalculateSpans(row: number, col: number) {\r\n let td = this.getCell(row, col).td;\r\n if (td) {\r\n td.colSpan = this.countSpanLeft(row, col);\r\n td.rowSpan = this.countSpanAbove(row, col);\r\n if (td.colSpan == 1) {\r\n td.removeAttribute('colSpan');\r\n }\r\n if (td.rowSpan == 1) {\r\n td.removeAttribute('rowSpan');\r\n }\r\n }\r\n }\r\n\r\n private countSpanLeft(row: number, col: number) {\r\n let result = 1;\r\n for (let i = col + 1; i < this.cells[row].length; i++) {\r\n let cell = this.getCell(row, i);\r\n if (cell.td || !cell.spanLeft) {\r\n break;\r\n }\r\n result++;\r\n }\r\n return result;\r\n }\r\n\r\n private countSpanAbove(row: number, col: number) {\r\n let result = 1;\r\n for (let i = row + 1; i < this.cells.length; i++) {\r\n let cell = this.getCell(i, col);\r\n if (cell.td || !cell.spanAbove) {\r\n break;\r\n }\r\n result++;\r\n }\r\n return result;\r\n }\r\n\r\n private normalizeEmptyTableCells() {\r\n for (let i = 0, row; (row = this.table.rows[i]); i++) {\r\n for (let j = 0, cell; (cell = row.cells[j]); j++) {\r\n if (cell) {\r\n if (!cell.innerHTML || !cell.innerHTML.trim()) {\r\n cell.appendChild(document.createElement('br'));\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n /* normalize width/height for each cell in the table */\r\n public normalizeTableCellSize() {\r\n // remove width/height for each row\r\n for (let i = 0, row; (row = this.table.rows[i]); i++) {\r\n row.removeAttribute('width');\r\n row.style.width = null;\r\n row.removeAttribute('height');\r\n row.style.height = null;\r\n }\r\n\r\n // set width/height for each cell\r\n for (let i = 0; i < this.cells.length; i++) {\r\n for (let j = 0; j < this.cells[i].length; j++) {\r\n const cell = this.cells[i][j];\r\n if (cell) {\r\n setHTMLElementSizeInPx(cell.td, cell.width, cell.height);\r\n }\r\n }\r\n }\r\n }\r\n\r\n private normalizeSize() {\r\n this.normalizeEmptyTableCells();\r\n this.normalizeTableCellSize();\r\n setHTMLElementSizeInPx(this.table); // Make sure table width/height is fixed to avoid shifting effect\r\n }\r\n}\r\n\r\nfunction setHTMLElementSizeInPx(element: HTMLElement, newWidth?: number, newHeight?: number) {\r\n if (!!element) {\r\n element.removeAttribute('width');\r\n element.removeAttribute('height');\r\n element.style.boxSizing = 'border-box';\r\n const rect = element.getBoundingClientRect();\r\n element.style.width = `${newWidth !== undefined ? newWidth : rect.width}px`;\r\n element.style.height = `${newHeight !== undefined ? newHeight : rect.height}px`;\r\n }\r\n}\r\n\r\nfunction getTableFromTd(td: HTMLTableCellElement) {\r\n let result = td;\r\n for (; result && result.tagName != 'TABLE'; result = result.parentElement) {}\r\n return result;\r\n}\r\n\r\nfunction getBorderStyle(style: string): string {\r\n return 'solid 1px ' + (style || 'transparent');\r\n}\r\n\r\n/**\r\n * Clone a table cell\r\n * @param cell The cell to clone\r\n */\r\nfunction cloneCell(cell: VCell): VCell {\r\n return {\r\n td: cloneNode(cell.td),\r\n spanAbove: cell.spanAbove,\r\n spanLeft: cell.spanLeft,\r\n };\r\n}\r\n\r\n/**\r\n * Clone a node without its children.\r\n * @param node The node to clone\r\n */\r\nfunction cloneNode(node: T): T {\r\n let newNode = node ? node.cloneNode(false /*deep*/) : null;\r\n if (safeInstanceOf(newNode, 'HTMLTableCellElement')) {\r\n newNode.removeAttribute('id');\r\n if (!newNode.firstChild) {\r\n newNode.appendChild(node.ownerDocument.createElement('br'));\r\n }\r\n }\r\n return newNode;\r\n}\r\n","import contains from '../utils/contains';\nimport getListTypeFromNode from './getListTypeFromNode';\nimport getTagOfNode from '../utils/getTagOfNode';\nimport isBlockElement from '../utils/isBlockElement';\nimport moveChildNodes from '../utils/moveChildNodes';\nimport safeInstanceOf from '../utils/safeInstanceOf';\nimport toArray from '../utils/toArray';\nimport unwrap from '../utils/unwrap';\nimport wrap from '../utils/wrap';\nimport { KnownCreateElementDataIndex, ListType } from 'roosterjs-editor-types';\n\nconst orderListStyles = [null, 'lower-alpha', 'lower-roman'];\n\n/**\n * @internal\n * !!! Never directly create instance of this class. It should be created within VList class !!!\n *\n * Represent a list item.\n *\n * A list item is normally wrapped using a LI tag. But this class is only a logical item,\n * it can be a LI tag, or another other type of node which means it is actually not a list item.\n * That can happen after we do \"outdent\" on a 1-level list item, then it becomes not a list item.\n * @internal\n */\nexport default class VListItem {\n private listTypes: ListType[];\n private node: HTMLLIElement;\n private dummy: boolean;\n private newListStart: number = undefined;\n\n /**\n * Construct a new instance of VListItem class\n * @param node The DOM node for this item\n * @param listTypes An array represents list types of all parent and current level.\n * Skip this parameter for a non-list item.\n */\n constructor(node: Node, ...listTypes: (ListType.Ordered | ListType.Unordered)[]) {\n if (!node) {\n throw new Error('node must not be null');\n }\n\n this.node = safeInstanceOf(node, 'HTMLLIElement')\n ? node\n : (wrap(node, KnownCreateElementDataIndex.BlockListItem) as HTMLLIElement);\n const display = this.node.style.display;\n\n this.dummy = display != 'list-item' && display != '';\n\n // Always add a None list type in front of all other types to represent non-list scenario.\n this.listTypes = [ListType.None, ...listTypes];\n }\n\n /**\n * Get type of current list item\n */\n getListType(): ListType {\n return this.listTypes[this.listTypes.length - 1];\n }\n\n /**\n * Get the levels of this list item.\n */\n getLevel(): number {\n return this.listTypes.length - 1;\n }\n\n /**\n * Get DOM node of this list item\n */\n getNode(): HTMLLIElement {\n return this.node;\n }\n\n /**\n * Get the Start Number of the new List\n */\n getNewListStart(): number | undefined {\n return this.newListStart;\n }\n\n /**\n * Check if a given node is contained by this list item\n * @param node The node to check\n */\n contains(node: Node): boolean {\n return contains(this.node, node, true /*treatSameNodeAsContain*/);\n }\n\n /**\n * Check if this item is a dummy item.\n * A dummy item is also represented by LI tag, but it won't render a bullet (for Unordered list) or a number (for Ordered list)\n * normally it has CSS style display set to a value other than \"list-item\"\n */\n isDummy() {\n return this.dummy;\n }\n\n /**\n * @deprecated Always return false\n */\n isOrphanItem(): boolean {\n return false;\n }\n\n /**\n * @deprecated\n */\n canMerge(item: VListItem): boolean {\n if (!item?.isOrphanItem() || this.listTypes.length != item.listTypes.length) {\n return false;\n }\n\n return this.listTypes.every((type, index) => item.listTypes[index] == type);\n }\n\n /**\n * @deprecated\n */\n mergeItems(items: VListItem[]) {\n const nodesToWrap = items?.map(item => item.node) || [];\n const targetNodes = wrapIfNotBlockNode(\n nodesToWrap,\n true /*checkFirst*/,\n false /*checkLast*/\n );\n targetNodes.forEach(node => this.node.appendChild(node));\n }\n\n /**\n * Indent this item\n * If this is not an list item, it will be no op\n */\n indent() {\n const listType = this.getListType();\n if (listType != ListType.None) {\n this.listTypes.push(listType);\n }\n }\n\n /**\n * Outdent this item\n * If this item is already not an list item, it will be no op\n */\n outdent() {\n if (this.listTypes.length > 1) {\n this.listTypes.pop();\n }\n }\n\n /**\n * Change list type of this item\n * @param targetType The target list type to change to\n */\n changeListType(targetType: ListType) {\n if (targetType == ListType.None) {\n this.listTypes = [targetType];\n } else {\n this.outdent();\n this.listTypes.push(targetType);\n }\n }\n\n /**\n * Set whether the item is a dummy item\n * @param isDummy Whether the item is a dummy item\n */\n setIsDummy(isDummy: boolean) {\n this.dummy = isDummy;\n }\n\n /**\n * Set the start Number of the new list\n * @param isDummy Whether the item is a dummy item\n */\n setNewListStart(startNumber: number) {\n this.newListStart = startNumber;\n }\n\n /**\n * Write the change result back into DOM\n * @param listStack current stack of list elements\n * @param originalRoot Original list root element. It will be reused when write back if possible\n */\n writeBack(listStack: Node[], originalRoot?: HTMLOListElement | HTMLUListElement) {\n let nextLevel = 1;\n\n // 1. Determine list elements that we can reuse\n // e.g.:\n // passed in listStack: Fragment > OL > UL > OL\n // local listTypes: null > OL > UL > UL > OL\n // then Fragment > OL > UL can be reused\n for (; nextLevel < listStack.length; nextLevel++) {\n if (getListTypeFromNode(listStack[nextLevel]) !== this.listTypes[nextLevel]) {\n listStack.splice(nextLevel);\n break;\n }\n }\n\n // 2. Add new list elements\n // e.g.:\n // passed in listStack: Fragment > OL > UL\n // local listTypes: null > OL > UL > UL > OL\n // then we need to create a UL and a OL tag\n for (; nextLevel < this.listTypes.length; nextLevel++) {\n const newList = createListElement(\n listStack[0],\n this.listTypes[nextLevel],\n nextLevel,\n originalRoot\n );\n\n listStack[listStack.length - 1].appendChild(newList);\n listStack.push(newList);\n }\n\n // 3. Add current node into deepest list element\n listStack[listStack.length - 1].appendChild(this.node);\n this.node.style.display = this.dummy ? 'block' : null;\n\n // 4. If this is not a list item now, need to unwrap the LI node and do proper handling\n if (this.listTypes.length <= 1) {\n wrapIfNotBlockNode(\n getTagOfNode(this.node) == 'LI' ? getChildrenAndUnwrap(this.node) : [this.node],\n true /*checkFirst*/,\n true /*checkLast*/\n );\n }\n }\n}\n\nfunction createListElement(\n newRoot: Node,\n listType: ListType,\n nextLevel: number,\n originalRoot?: HTMLOListElement | HTMLUListElement\n): HTMLOListElement | HTMLUListElement {\n const doc = newRoot.ownerDocument;\n let result: HTMLOListElement | HTMLUListElement;\n\n // Try to reuse the existing root element\n // It can be reused when\n // 1. Current list item is level 1 (top level), AND\n // 2. Original root exists, AND\n // 3. They have the same list type AND\n // 4. The original root is not used yet\n if (nextLevel == 1 && originalRoot && listType == getListTypeFromNode(originalRoot)) {\n if (contains(newRoot, originalRoot)) {\n // If it is already used, let's clone one and remove ID to avoid duplicating ID\n result = originalRoot.cloneNode(false /*deep*/) as HTMLOListElement | HTMLUListElement;\n (result).removeAttribute('id');\n } else {\n // Remove all child nodes, they will be added back later when write back other items\n moveChildNodes(originalRoot);\n result = originalRoot;\n }\n } else {\n // Can't be reused, can't clone, let's create a new one\n result = doc.createElement(listType == ListType.Ordered ? 'ol' : 'ul');\n }\n\n if (listType == ListType.Ordered && nextLevel > 1) {\n result.style.listStyleType = orderListStyles[(nextLevel - 1) % orderListStyles.length];\n }\n\n return result;\n}\n\nfunction wrapIfNotBlockNode(nodes: Node[], checkFirst: boolean, checkLast: boolean): Node[] {\n if (\n nodes.length > 0 &&\n (!checkFirst || !isBlockElement(nodes[0])) &&\n (!checkLast || !isBlockElement(nodes[nodes.length]))\n ) {\n nodes = [wrap(nodes)];\n }\n\n return nodes;\n}\n\nfunction getChildrenAndUnwrap(node: Node): Node[] {\n const result = toArray(node.childNodes);\n unwrap(node);\n return result;\n}\n","import findClosestElementAncestor from '../utils/findClosestElementAncestor';\nimport { RegionBase } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * A type map from selector string to HTML element type\n */\nexport interface SelectorToTypeMap {\n ol: HTMLOListElement;\n ul: HTMLUListElement;\n 'ol,ul': HTMLOListElement | HTMLUListElement;\n}\n\n/**\n * @internal\n * Get Root list node from the given node within the given region\n * @param region Region to scope the search into\n * @param selector The selector to search\n * @param node The start node\n */\nexport default function getRootListNode(\n region: RegionBase,\n selector: TSelector,\n node: Node\n): SelectorToTypeMap[TSelector] {\n let list =\n region &&\n (findClosestElementAncestor(\n node,\n region.rootNode,\n selector\n ) as SelectorToTypeMap[TSelector]);\n\n if (list) {\n let ancestor: SelectorToTypeMap[TSelector];\n while (\n (ancestor = findClosestElementAncestor(\n list.parentNode,\n region.rootNode,\n selector\n ) as SelectorToTypeMap[TSelector])\n ) {\n list = ancestor;\n }\n }\n\n return list;\n}\n","import ContentTraverser from '../contentTraverser/ContentTraverser';\nimport createElement from '../utils/createElement';\nimport getBlockElementAtNode from '../blockElements/getBlockElementAtNode';\nimport getSelectionRangeInRegion from './getSelectionRangeInRegion';\nimport shouldSkipNode from '../utils/shouldSkipNode';\nimport { BlockElement, KnownCreateElementDataIndex, RegionBase } from 'roosterjs-editor-types';\n\n/**\n * Get all block elements covered by the selection under this region\n * @param regionBase The region to get block elements from\n * @param createBlockIfEmpty When set to true, a new empty block element will be created if there is not\n * any blocks in the region. Default value is false\n */\nexport default function getSelectedBlockElementsInRegion(\n regionBase: RegionBase,\n createBlockIfEmpty?: boolean\n): BlockElement[] {\n const range = getSelectionRangeInRegion(regionBase);\n let blocks: BlockElement[] = [];\n\n if (range) {\n const { rootNode, skipTags } = regionBase;\n const traverser = ContentTraverser.createSelectionTraverser(rootNode, range, skipTags);\n\n for (\n let block = traverser?.currentBlockElement;\n !!block;\n block = traverser.getNextBlockElement()\n ) {\n blocks.push(block);\n }\n\n // Remove meaningless nodes\n blocks = blocks.filter(block => {\n const startNode = block.getStartNode();\n const endNode = block.getEndNode();\n\n if (startNode == endNode && shouldSkipNode(startNode, true /*ignoreSpace*/)) {\n startNode.parentNode?.removeChild(startNode);\n return false;\n } else {\n return true;\n }\n });\n }\n\n if (blocks.length == 0 && regionBase && !regionBase.rootNode.firstChild && createBlockIfEmpty) {\n const newNode = createElement(\n KnownCreateElementDataIndex.EmptyLine,\n regionBase.rootNode.ownerDocument\n );\n regionBase.rootNode.appendChild(newNode);\n blocks.push(getBlockElementAtNode(regionBase.rootNode, newNode));\n }\n\n return blocks;\n}\n","import contains from '../utils/contains';\nimport findClosestElementAncestor from '../utils/findClosestElementAncestor';\nimport Position from '../selection/Position';\nimport queryElements from '../utils/queryElements';\nimport regionTypeData from './regionTypeData';\nimport { getNextLeafSibling, getPreviousLeafSibling } from '../utils/getLeafSibling';\nimport { QueryScope, Region, RegionType } from 'roosterjs-editor-types';\n\n/**\n * Get regions impacted by the given range under the root node\n * @param root Root node to get regions from\n * @param range A selection range. Regions will be created according to this range. Each region will be\n * fully or partially covered by this range.\n * @param type Type of region. Currently we only support TABLE region.\n */\nexport default function getRegionsFromRange(\n root: HTMLElement,\n range: Range,\n type: RegionType\n): Region[] {\n let regions: Region[] = [];\n if (root && range) {\n const { innerSelector, skipTags } = regionTypeData[type];\n const boundaryTree = buildBoundaryTree(root, range, type);\n const start = findClosestElementAncestor(range.startContainer, root, innerSelector) || root;\n const end = findClosestElementAncestor(range.endContainer, root, innerSelector) || root;\n const creator = getRegionCreator(range, skipTags);\n [regions] = iterateNodes(creator, boundaryTree, start, end);\n }\n\n return regions.filter(r => !!r);\n}\n\n/**\n * @internal export for test only\n */\nexport function getRegionCreator(\n fullRange: Range,\n skipTags: string[]\n): (rootNode: HTMLElement, nodeBefore?: Node, nodeAfter?: Node) => Region {\n const fullSelectionStart = Position.getStart(fullRange).normalize();\n const fullSelectionEnd = Position.getEnd(fullRange).normalize();\n return (rootNode: HTMLElement, nodeBefore?: Node, nodeAfter?: Node) => {\n return areNodesValid(rootNode, nodeBefore, nodeAfter, skipTags)\n ? {\n rootNode,\n nodeBefore,\n nodeAfter,\n skipTags,\n fullSelectionStart,\n fullSelectionEnd,\n }\n : null;\n };\n}\n\n/**\n * This is a internal data structure used for build regions.\n * We firstly split the selection by some boundaries, then we can build region from these boundaries.\n */\ninterface Boundary {\n /**\n * inner node of this boundary\n */\n innerNode: HTMLElement;\n\n /**\n * Children of this boundary\n */\n children: {\n /**\n * Outer node of a boundary child\n */\n outerNode: Node;\n\n /**\n * Child boundaries\n */\n boundaries: Boundary[];\n }[];\n}\n\n/**\n * Step 1: Build boundary tree\n * @param root Root node of the whole scope, normally this will be the root of editable scope\n * @param range Existing selected full range\n * @param type Type of region to create\n */\nfunction buildBoundaryTree(root: HTMLElement, range: Range, type: RegionType): Boundary {\n const allBoundaries: Boundary[] = [{ innerNode: root, children: [] }];\n const { outerSelector, innerSelector } = regionTypeData[type];\n const inSelectionOuterNode = queryElements(\n root,\n outerSelector,\n null /*callback*/,\n QueryScope.InSelection,\n range\n );\n\n // According to https://www.w3.org/TR/selectors-api/#queryselectorall, the result of querySelectorAll\n // is in document order, which is what we expect. So we don't need to sort the result here.\n queryElements(\n root,\n innerSelector,\n thisInnerNode => {\n const thisOuterNode = findClosestElementAncestor(thisInnerNode, root, outerSelector);\n if (thisOuterNode && inSelectionOuterNode.indexOf(thisOuterNode) < 0) {\n const boundary: Boundary = { innerNode: thisInnerNode, children: [] };\n\n for (let i = allBoundaries.length - 1; i >= 0; i--) {\n const { innerNode, children } = allBoundaries[i];\n if (contains(innerNode, thisOuterNode)) {\n let child = children.filter(c => c.outerNode == thisOuterNode)[0];\n\n if (!child) {\n child = { outerNode: thisOuterNode, boundaries: [] };\n children.push(child);\n }\n\n child.boundaries.push(boundary);\n break;\n }\n }\n allBoundaries.push(boundary);\n }\n },\n QueryScope.OnSelection,\n range\n );\n\n return allBoundaries[0];\n}\n\n/**\n * Step 2: Recursively iterate all boundaries and create regions\n * @param creator A region creator function to help create region\n * @param boundary Current root boundary\n * @param start A node where full range start from. This may not be the direct node container of range.startContainer.\n * It is the nearest ancestor which satisfies the InnerSelector of the given region type\n * @param end A node where full range end from. This may not be the direct node container of range.endContainer.\n * It is the nearest ancestor which satisfies the InnerSelector of the given region type\n * @param started Whether we have already hit the start node\n */\nfunction iterateNodes(\n creator: (rootNode: HTMLElement, nodeBefore?: Node, nodeAfter?: Node) => Region,\n boundary: Boundary,\n start: Node,\n end: Node,\n started?: boolean\n): [Region[], boolean, boolean] {\n started = started || boundary.innerNode == start;\n let ended = false;\n const { children, innerNode } = boundary;\n let regions: Region[] = [];\n\n if (children.length == 0) {\n regions.push(creator(innerNode));\n } else {\n // Need to run one more time to add region after all children\n for (let i = 0; i <= children.length && !ended; i++) {\n const { outerNode, boundaries } = children[i] || {};\n const previousOuterNode = children[i - 1]?.outerNode;\n if (started) {\n regions.push(creator(innerNode, previousOuterNode, outerNode));\n }\n\n boundaries?.forEach(child => {\n let newRegions: Region[];\n [newRegions, started, ended] = iterateNodes(creator, child, start, end, started);\n regions = regions.concat(newRegions);\n });\n }\n }\n\n return [regions, started, ended || innerNode == end];\n}\n\n/**\n * Check if the given nodes combination is valid to create a region.\n * A combination is valid when:\n * 1. Root node is not null and is not empty. And\n * 2. For nodeBefore and nodeAfter, each of them should be either null or contained by root node. And\n * 3. If none of nodeBefore and nodeAfter is null, the should not contain each other, and there should be\n * node between them.\n * @param root Root node of region\n * @param nodeBefore The boundary node before the region under root\n * @param nodeAfter The boundary node after the region under root\n * @param skipTags Tags to skip\n */\nfunction areNodesValid(root: Node, nodeBefore: Node, nodeAfter: Node, skipTags: string[]) {\n if (!root) {\n return false;\n } else {\n const firstNodeOfRegion = nodeBefore && getNextLeafSibling(root, nodeBefore, skipTags);\n const lastNodeOfRegion = nodeAfter && getPreviousLeafSibling(root, nodeAfter, skipTags);\n const firstNodeValid =\n !nodeBefore || (contains(root, nodeBefore) && contains(root, firstNodeOfRegion));\n const lastNodeValid =\n !nodeAfter || (contains(root, nodeAfter) && contains(root, lastNodeOfRegion));\n const bothValid =\n !nodeBefore ||\n !nodeAfter ||\n (!contains(nodeBefore, nodeAfter, true /*treatSameAsContain*/) &&\n !contains(nodeBefore, lastNodeOfRegion, true /*treatSameAsContain*/) &&\n !contains(nodeAfter, nodeBefore, true /*treatSameAsContain*/) &&\n !contains(nodeAfter, firstNodeOfRegion, true /*treatSameAsContain*/));\n return firstNodeValid && lastNodeValid && bothValid;\n }\n}\n","import collapseNode from '../utils/collapseNodes';\nimport isNodeInRegion from './isNodeInRegion';\nimport safeInstanceOf from '../utils/safeInstanceOf';\nimport { BlockElement, RegionBase } from 'roosterjs-editor-types';\n\n/**\n * Collapse nodes within this region to their common ancestor node under this region\n * @param region The region to collapse nodes in.\n * @param nodesOrBlockElements Nodes or block elements to collapse. When take BlockElement[] as input,\n * start node of the first BlockElement and end node of the last BlockElement will be used as the nodes.\n * All nodes not contained by the given region will be ignored.\n */\nexport default function collapseNodesInRegion(\n region: RegionBase,\n nodesOrBlockElements: Node[] | BlockElement[]\n): Node[] {\n if (!nodesOrBlockElements || nodesOrBlockElements.length == 0) {\n return [];\n }\n\n let nodes = safeInstanceOf(nodesOrBlockElements[0], 'Node')\n ? nodesOrBlockElements\n : [\n nodesOrBlockElements[0].getStartNode(),\n (nodesOrBlockElements[nodesOrBlockElements.length - 1]).getEndNode(),\n ];\n\n nodes = nodes && nodes.filter(node => isNodeInRegion(region, node));\n\n const firstNode = nodes[0];\n const lastNode = nodes[nodes.length - 1];\n\n if (isNodeInRegion(region, firstNode) && isNodeInRegion(region, lastNode)) {\n return collapseNode(region.rootNode, firstNode, lastNode, true /*canSplitParent*/);\n } else {\n return [];\n }\n}\n","import changeElementTag from '../utils/changeElementTag';\nimport contains from '../utils/contains';\nimport getBlockElementAtNode from '../blockElements/getBlockElementAtNode';\nimport getPredefinedCssForElement from '../htmlSanitizer/getPredefinedCssForElement';\nimport getStyles from '../style/getStyles';\nimport isNodeInRegion from '../region/isNodeInRegion';\nimport safeInstanceOf from '../utils/safeInstanceOf';\nimport setStyles from '../style/setStyles';\nimport { BlockElement, RegionBase } from 'roosterjs-editor-types';\nimport { collapse } from '../utils/collapseNodes';\n\n/**\n * Merge a BlockElement of given node after another node\n * @param region Region to operate in\n * @param refNode The node to merge after\n * @param targetNode The node of target block element\n */\nexport default function mergeBlocksInRegion(region: RegionBase, refNode: Node, targetNode: Node) {\n let block: BlockElement;\n\n if (\n !isNodeInRegion(region, refNode) ||\n !isNodeInRegion(region, targetNode) ||\n !(block = getBlockElementAtNode(region.rootNode, targetNode)) ||\n block.contains(refNode)\n ) {\n return;\n }\n\n const blockRoot = block.collapseToSingleElement();\n const commonContainer = collapse(\n region.rootNode,\n blockRoot,\n refNode,\n false /*isStart*/,\n true /*canSplitParent*/\n );\n\n // Copy styles of parent nodes into blockRoot\n for (let node: Node = blockRoot; contains(commonContainer, node); ) {\n const parent = node.parentNode;\n if (safeInstanceOf(parent, 'HTMLElement')) {\n const styles = {\n ...(getPredefinedCssForElement(parent) || {}),\n ...getStyles(parent),\n ...getStyles(blockRoot),\n };\n setStyles(blockRoot, styles);\n }\n node = parent;\n }\n\n let nodeToRemove: Node = null;\n let nodeToMerge =\n blockRoot.childNodes.length == 1 && blockRoot.attributes.length == 0\n ? blockRoot.firstChild\n : changeElementTag(blockRoot, 'SPAN');\n\n // Remove empty node\n for (\n let node: Node = nodeToMerge;\n contains(commonContainer, node) && node.parentNode.childNodes.length == 1;\n node = node.parentNode\n ) {\n // If the only child is the one which is about to be removed, this node should also be removed\n nodeToRemove = node.parentNode;\n }\n\n // Finally, merge blocks, and remove empty nodes\n refNode.parentNode?.insertBefore(nodeToMerge, refNode.nextSibling);\n nodeToRemove?.parentNode?.removeChild(nodeToRemove);\n}\n","import getTagOfNode from '../utils/getTagOfNode';\nimport { PredefinedCssMap, StringMap } from 'roosterjs-editor-types';\n\nconst PREDEFINED_CSS_FOR_ELEMENT: PredefinedCssMap = {\n B: {\n 'font-weight': 'bold',\n },\n EM: {\n 'font-style': 'italic',\n },\n I: {\n 'font-style': 'italic',\n },\n U: {\n 'text-decoration': 'underline',\n },\n P: {\n 'margin-top': '1em',\n 'margin-bottom': '1em',\n },\n PRE: {\n 'white-space': 'pre',\n },\n S: {\n 'text-decoration': 'line-through',\n },\n STRIKE: {\n 'text-decoration': 'line-through',\n },\n SUB: {\n 'vertical-align': 'sub',\n 'font-size': 'smaller',\n },\n SUP: {\n 'vertical-align': 'super',\n 'font-size': 'smaller',\n },\n};\n\n/**\n * @internal\n * Get a map for browser built-in CSS definations of elements\n */\nexport default function getPredefinedCssForElement(\n element: HTMLElement,\n additionalPredefinedCssForElement?: PredefinedCssMap\n): StringMap {\n const tag = getTagOfNode(element);\n return PREDEFINED_CSS_FOR_ELEMENT[tag] || (additionalPredefinedCssForElement || {})[tag];\n}\n","import contains from '../utils/contains';\r\nimport Position from './Position';\r\nimport { NodePosition, NodeType, SelectionPath } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Get path of the given selection range related to the given rootNode\r\n * @param rootNode The root node where the path start from\r\n * @param range The range of selection\r\n */\r\nexport default function getSelectionPath(rootNode: Node, range: Range): SelectionPath {\r\n if (!range) {\r\n return null;\r\n }\r\n\r\n let selectionPath: SelectionPath = {\r\n start: getPositionPath(Position.getStart(range), rootNode),\r\n end: getPositionPath(Position.getEnd(range), rootNode),\r\n };\r\n\r\n return selectionPath;\r\n}\r\n\r\n/**\r\n * Get the path of the node relative to rootNode.\r\n * The path of the node is an array of integer indices into the childNodes of the given node.\r\n *\r\n * The node path will be what the node path will be on a _normalized_ dom\r\n * (e.g. empty text nodes will be ignored and adjacent text nodes will be concatenated)\r\n *\r\n * @param rootNode the node the path will be relative to\r\n * @param position the position to get indexes from. Follows the same semantics\r\n * as selectionRange (if node is of type Text, it is an offset into the text of that node.\r\n * If node is of type Element, it is the index of a child in that Element node.)\r\n */\r\nfunction getPositionPath(position: NodePosition, rootNode: Node): number[] {\r\n if (!position || !rootNode) {\r\n return [];\r\n }\r\n\r\n let { node, offset } = position;\r\n let result: number[] = [];\r\n let parent: Node;\r\n\r\n if (!contains(rootNode, node, true)) {\r\n return [];\r\n }\r\n\r\n if (node.nodeType == NodeType.Text) {\r\n parent = node.parentNode;\r\n while (node.previousSibling && node.previousSibling.nodeType == NodeType.Text) {\r\n offset += node.previousSibling.nodeValue.length;\r\n node = node.previousSibling;\r\n }\r\n result.unshift(offset);\r\n } else {\r\n parent = node;\r\n node = node.childNodes[offset];\r\n }\r\n\r\n do {\r\n offset = 0;\r\n let isPreviousText = false;\r\n\r\n for (let c: Node = parent.firstChild; c && c != node; c = c.nextSibling) {\r\n if (c.nodeType == NodeType.Text) {\r\n if (c.nodeValue.length == 0 || isPreviousText) {\r\n continue;\r\n }\r\n\r\n isPreviousText = true;\r\n } else {\r\n isPreviousText = false;\r\n }\r\n\r\n offset++;\r\n }\r\n\r\n result.unshift(offset);\r\n node = parent;\r\n parent = parent.parentNode;\r\n } while (node && node != rootNode);\r\n\r\n return result;\r\n}\r\n","import canMoveCurrentSnapshot from './canMoveCurrentSnapshot';\r\nimport { Snapshots } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Clear all snapshots after the current one\r\n * @param snapshots The snapshots data structure to clear\r\n */\r\nexport default function clearProceedingSnapshots(snapshots: Snapshots) {\r\n if (canMoveCurrentSnapshot(snapshots, 1)) {\r\n let removedSize = 0;\r\n for (let i = snapshots.currentIndex + 1; i < snapshots.snapshots.length; i++) {\r\n removedSize += snapshots.snapshots[i].length;\r\n }\r\n snapshots.snapshots.splice(snapshots.currentIndex + 1);\r\n snapshots.totalSize -= removedSize;\r\n snapshots.autoCompleteIndex = -1;\r\n }\r\n}\r\n","import { StringMap } from 'roosterjs-editor-types';\n\n// Inheritable CSS properties\n// Ref: https://www.w3.org/TR/CSS21/propidx.html\nconst INHERITABLE_PROPERTIES = (\n 'border-spacing,caption-side,color,' +\n 'cursor,direction,empty-cells,font-family,font-size,font-style,font-variant,font-weight,' +\n 'font,letter-spacing,line-height,list-style-image,list-style-position,list-style-type,' +\n 'list-style,orphans,quotes,text-align,text-indent,text-transform,visibility,white-space,' +\n 'widows,word-spacing'\n).split(',');\n\n/**\n * Get inheritable CSS style values from the given element\n * @param element The element to get style from\n */\nexport default function getInheritableStyles(element: HTMLElement): StringMap {\n let win = element && element.ownerDocument && element.ownerDocument.defaultView;\n let styles = win && win.getComputedStyle(element);\n let result: StringMap = {};\n INHERITABLE_PROPERTIES.forEach(\n name => (result[name] = (styles && styles.getPropertyValue(name)) || '')\n );\n return result;\n}\n","function nativeClone(\n source: Record,\n existingObj?: Record\n): Record {\n return Object.assign(existingObj || {}, source);\n}\n\nfunction customClone(\n source: Record,\n existingObj?: Record\n): Record {\n let result: Record = existingObj || {};\n if (source) {\n for (let key of Object.keys(source)) {\n result[key] = source[key];\n }\n }\n return result;\n}\n\n/**\n * @internal\n */\nexport const cloneObject = Object.assign ? nativeClone : customClone;\n","const CTRL_CHAR_CODE = 'Control';\nconst ALT_CHAR_CODE = 'Alt';\nconst META_CHAR_CODE = 'Meta';\n\n/**\n * Returns true when the event was fired from a modifier key, otherwise false\n * @param event The keyboard event object\n */\nexport default function isModifierKey(event: KeyboardEvent): boolean {\n const isCtrlKey = event.ctrlKey || event.key === CTRL_CHAR_CODE;\n const isAltKey = event.altKey || event.key === ALT_CHAR_CODE;\n const isMetaKey = event.metaKey || event.key === META_CHAR_CODE;\n\n return isCtrlKey || isAltKey || isMetaKey;\n}\n","import { contains } from 'roosterjs-editor-dom';\nimport { EditorCore, HasFocus } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Check if the editor has focus now\n * @param core The EditorCore object\n * @returns True if the editor has focus, otherwise false\n */\nexport const hasFocus: HasFocus = (core: EditorCore) => {\n let activeElement = core.contentDiv.ownerDocument.activeElement;\n return (\n activeElement && contains(core.contentDiv, activeElement, true /*treatSameNodeAsContain*/)\n );\n};\n","export { default as getDarkColor } from './utils/getDarkColor';\n","'use strict'\r\n\r\nmodule.exports = {\r\n\t\"aliceblue\": [240, 248, 255],\r\n\t\"antiquewhite\": [250, 235, 215],\r\n\t\"aqua\": [0, 255, 255],\r\n\t\"aquamarine\": [127, 255, 212],\r\n\t\"azure\": [240, 255, 255],\r\n\t\"beige\": [245, 245, 220],\r\n\t\"bisque\": [255, 228, 196],\r\n\t\"black\": [0, 0, 0],\r\n\t\"blanchedalmond\": [255, 235, 205],\r\n\t\"blue\": [0, 0, 255],\r\n\t\"blueviolet\": [138, 43, 226],\r\n\t\"brown\": [165, 42, 42],\r\n\t\"burlywood\": [222, 184, 135],\r\n\t\"cadetblue\": [95, 158, 160],\r\n\t\"chartreuse\": [127, 255, 0],\r\n\t\"chocolate\": [210, 105, 30],\r\n\t\"coral\": [255, 127, 80],\r\n\t\"cornflowerblue\": [100, 149, 237],\r\n\t\"cornsilk\": [255, 248, 220],\r\n\t\"crimson\": [220, 20, 60],\r\n\t\"cyan\": [0, 255, 255],\r\n\t\"darkblue\": [0, 0, 139],\r\n\t\"darkcyan\": [0, 139, 139],\r\n\t\"darkgoldenrod\": [184, 134, 11],\r\n\t\"darkgray\": [169, 169, 169],\r\n\t\"darkgreen\": [0, 100, 0],\r\n\t\"darkgrey\": [169, 169, 169],\r\n\t\"darkkhaki\": [189, 183, 107],\r\n\t\"darkmagenta\": [139, 0, 139],\r\n\t\"darkolivegreen\": [85, 107, 47],\r\n\t\"darkorange\": [255, 140, 0],\r\n\t\"darkorchid\": [153, 50, 204],\r\n\t\"darkred\": [139, 0, 0],\r\n\t\"darksalmon\": [233, 150, 122],\r\n\t\"darkseagreen\": [143, 188, 143],\r\n\t\"darkslateblue\": [72, 61, 139],\r\n\t\"darkslategray\": [47, 79, 79],\r\n\t\"darkslategrey\": [47, 79, 79],\r\n\t\"darkturquoise\": [0, 206, 209],\r\n\t\"darkviolet\": [148, 0, 211],\r\n\t\"deeppink\": [255, 20, 147],\r\n\t\"deepskyblue\": [0, 191, 255],\r\n\t\"dimgray\": [105, 105, 105],\r\n\t\"dimgrey\": [105, 105, 105],\r\n\t\"dodgerblue\": [30, 144, 255],\r\n\t\"firebrick\": [178, 34, 34],\r\n\t\"floralwhite\": [255, 250, 240],\r\n\t\"forestgreen\": [34, 139, 34],\r\n\t\"fuchsia\": [255, 0, 255],\r\n\t\"gainsboro\": [220, 220, 220],\r\n\t\"ghostwhite\": [248, 248, 255],\r\n\t\"gold\": [255, 215, 0],\r\n\t\"goldenrod\": [218, 165, 32],\r\n\t\"gray\": [128, 128, 128],\r\n\t\"green\": [0, 128, 0],\r\n\t\"greenyellow\": [173, 255, 47],\r\n\t\"grey\": [128, 128, 128],\r\n\t\"honeydew\": [240, 255, 240],\r\n\t\"hotpink\": [255, 105, 180],\r\n\t\"indianred\": [205, 92, 92],\r\n\t\"indigo\": [75, 0, 130],\r\n\t\"ivory\": [255, 255, 240],\r\n\t\"khaki\": [240, 230, 140],\r\n\t\"lavender\": [230, 230, 250],\r\n\t\"lavenderblush\": [255, 240, 245],\r\n\t\"lawngreen\": [124, 252, 0],\r\n\t\"lemonchiffon\": [255, 250, 205],\r\n\t\"lightblue\": [173, 216, 230],\r\n\t\"lightcoral\": [240, 128, 128],\r\n\t\"lightcyan\": [224, 255, 255],\r\n\t\"lightgoldenrodyellow\": [250, 250, 210],\r\n\t\"lightgray\": [211, 211, 211],\r\n\t\"lightgreen\": [144, 238, 144],\r\n\t\"lightgrey\": [211, 211, 211],\r\n\t\"lightpink\": [255, 182, 193],\r\n\t\"lightsalmon\": [255, 160, 122],\r\n\t\"lightseagreen\": [32, 178, 170],\r\n\t\"lightskyblue\": [135, 206, 250],\r\n\t\"lightslategray\": [119, 136, 153],\r\n\t\"lightslategrey\": [119, 136, 153],\r\n\t\"lightsteelblue\": [176, 196, 222],\r\n\t\"lightyellow\": [255, 255, 224],\r\n\t\"lime\": [0, 255, 0],\r\n\t\"limegreen\": [50, 205, 50],\r\n\t\"linen\": [250, 240, 230],\r\n\t\"magenta\": [255, 0, 255],\r\n\t\"maroon\": [128, 0, 0],\r\n\t\"mediumaquamarine\": [102, 205, 170],\r\n\t\"mediumblue\": [0, 0, 205],\r\n\t\"mediumorchid\": [186, 85, 211],\r\n\t\"mediumpurple\": [147, 112, 219],\r\n\t\"mediumseagreen\": [60, 179, 113],\r\n\t\"mediumslateblue\": [123, 104, 238],\r\n\t\"mediumspringgreen\": [0, 250, 154],\r\n\t\"mediumturquoise\": [72, 209, 204],\r\n\t\"mediumvioletred\": [199, 21, 133],\r\n\t\"midnightblue\": [25, 25, 112],\r\n\t\"mintcream\": [245, 255, 250],\r\n\t\"mistyrose\": [255, 228, 225],\r\n\t\"moccasin\": [255, 228, 181],\r\n\t\"navajowhite\": [255, 222, 173],\r\n\t\"navy\": [0, 0, 128],\r\n\t\"oldlace\": [253, 245, 230],\r\n\t\"olive\": [128, 128, 0],\r\n\t\"olivedrab\": [107, 142, 35],\r\n\t\"orange\": [255, 165, 0],\r\n\t\"orangered\": [255, 69, 0],\r\n\t\"orchid\": [218, 112, 214],\r\n\t\"palegoldenrod\": [238, 232, 170],\r\n\t\"palegreen\": [152, 251, 152],\r\n\t\"paleturquoise\": [175, 238, 238],\r\n\t\"palevioletred\": [219, 112, 147],\r\n\t\"papayawhip\": [255, 239, 213],\r\n\t\"peachpuff\": [255, 218, 185],\r\n\t\"peru\": [205, 133, 63],\r\n\t\"pink\": [255, 192, 203],\r\n\t\"plum\": [221, 160, 221],\r\n\t\"powderblue\": [176, 224, 230],\r\n\t\"purple\": [128, 0, 128],\r\n\t\"rebeccapurple\": [102, 51, 153],\r\n\t\"red\": [255, 0, 0],\r\n\t\"rosybrown\": [188, 143, 143],\r\n\t\"royalblue\": [65, 105, 225],\r\n\t\"saddlebrown\": [139, 69, 19],\r\n\t\"salmon\": [250, 128, 114],\r\n\t\"sandybrown\": [244, 164, 96],\r\n\t\"seagreen\": [46, 139, 87],\r\n\t\"seashell\": [255, 245, 238],\r\n\t\"sienna\": [160, 82, 45],\r\n\t\"silver\": [192, 192, 192],\r\n\t\"skyblue\": [135, 206, 235],\r\n\t\"slateblue\": [106, 90, 205],\r\n\t\"slategray\": [112, 128, 144],\r\n\t\"slategrey\": [112, 128, 144],\r\n\t\"snow\": [255, 250, 250],\r\n\t\"springgreen\": [0, 255, 127],\r\n\t\"steelblue\": [70, 130, 180],\r\n\t\"tan\": [210, 180, 140],\r\n\t\"teal\": [0, 128, 128],\r\n\t\"thistle\": [216, 191, 216],\r\n\t\"tomato\": [255, 99, 71],\r\n\t\"turquoise\": [64, 224, 208],\r\n\t\"violet\": [238, 130, 238],\r\n\t\"wheat\": [245, 222, 179],\r\n\t\"white\": [255, 255, 255],\r\n\t\"whitesmoke\": [245, 245, 245],\r\n\t\"yellow\": [255, 255, 0],\r\n\t\"yellowgreen\": [154, 205, 50]\r\n};\r\n","/* MIT license */\nvar cssKeywords = require('color-name');\n\n// NOTE: conversions should only return primitive values (i.e. arrays, or\n// values that give correct `typeof` results).\n// do not use box values types (i.e. Number(), String(), etc.)\n\nvar reverseKeywords = {};\nfor (var key in cssKeywords) {\n\tif (cssKeywords.hasOwnProperty(key)) {\n\t\treverseKeywords[cssKeywords[key]] = key;\n\t}\n}\n\nvar convert = module.exports = {\n\trgb: {channels: 3, labels: 'rgb'},\n\thsl: {channels: 3, labels: 'hsl'},\n\thsv: {channels: 3, labels: 'hsv'},\n\thwb: {channels: 3, labels: 'hwb'},\n\tcmyk: {channels: 4, labels: 'cmyk'},\n\txyz: {channels: 3, labels: 'xyz'},\n\tlab: {channels: 3, labels: 'lab'},\n\tlch: {channels: 3, labels: 'lch'},\n\thex: {channels: 1, labels: ['hex']},\n\tkeyword: {channels: 1, labels: ['keyword']},\n\tansi16: {channels: 1, labels: ['ansi16']},\n\tansi256: {channels: 1, labels: ['ansi256']},\n\thcg: {channels: 3, labels: ['h', 'c', 'g']},\n\tapple: {channels: 3, labels: ['r16', 'g16', 'b16']},\n\tgray: {channels: 1, labels: ['gray']}\n};\n\n// hide .channels and .labels properties\nfor (var model in convert) {\n\tif (convert.hasOwnProperty(model)) {\n\t\tif (!('channels' in convert[model])) {\n\t\t\tthrow new Error('missing channels property: ' + model);\n\t\t}\n\n\t\tif (!('labels' in convert[model])) {\n\t\t\tthrow new Error('missing channel labels property: ' + model);\n\t\t}\n\n\t\tif (convert[model].labels.length !== convert[model].channels) {\n\t\t\tthrow new Error('channel and label counts mismatch: ' + model);\n\t\t}\n\n\t\tvar channels = convert[model].channels;\n\t\tvar labels = convert[model].labels;\n\t\tdelete convert[model].channels;\n\t\tdelete convert[model].labels;\n\t\tObject.defineProperty(convert[model], 'channels', {value: channels});\n\t\tObject.defineProperty(convert[model], 'labels', {value: labels});\n\t}\n}\n\nconvert.rgb.hsl = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar min = Math.min(r, g, b);\n\tvar max = Math.max(r, g, b);\n\tvar delta = max - min;\n\tvar h;\n\tvar s;\n\tvar l;\n\n\tif (max === min) {\n\t\th = 0;\n\t} else if (r === max) {\n\t\th = (g - b) / delta;\n\t} else if (g === max) {\n\t\th = 2 + (b - r) / delta;\n\t} else if (b === max) {\n\t\th = 4 + (r - g) / delta;\n\t}\n\n\th = Math.min(h * 60, 360);\n\n\tif (h < 0) {\n\t\th += 360;\n\t}\n\n\tl = (min + max) / 2;\n\n\tif (max === min) {\n\t\ts = 0;\n\t} else if (l <= 0.5) {\n\t\ts = delta / (max + min);\n\t} else {\n\t\ts = delta / (2 - max - min);\n\t}\n\n\treturn [h, s * 100, l * 100];\n};\n\nconvert.rgb.hsv = function (rgb) {\n\tvar rdif;\n\tvar gdif;\n\tvar bdif;\n\tvar h;\n\tvar s;\n\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar v = Math.max(r, g, b);\n\tvar diff = v - Math.min(r, g, b);\n\tvar diffc = function (c) {\n\t\treturn (v - c) / 6 / diff + 1 / 2;\n\t};\n\n\tif (diff === 0) {\n\t\th = s = 0;\n\t} else {\n\t\ts = diff / v;\n\t\trdif = diffc(r);\n\t\tgdif = diffc(g);\n\t\tbdif = diffc(b);\n\n\t\tif (r === v) {\n\t\t\th = bdif - gdif;\n\t\t} else if (g === v) {\n\t\t\th = (1 / 3) + rdif - bdif;\n\t\t} else if (b === v) {\n\t\t\th = (2 / 3) + gdif - rdif;\n\t\t}\n\t\tif (h < 0) {\n\t\t\th += 1;\n\t\t} else if (h > 1) {\n\t\t\th -= 1;\n\t\t}\n\t}\n\n\treturn [\n\t\th * 360,\n\t\ts * 100,\n\t\tv * 100\n\t];\n};\n\nconvert.rgb.hwb = function (rgb) {\n\tvar r = rgb[0];\n\tvar g = rgb[1];\n\tvar b = rgb[2];\n\tvar h = convert.rgb.hsl(rgb)[0];\n\tvar w = 1 / 255 * Math.min(r, Math.min(g, b));\n\n\tb = 1 - 1 / 255 * Math.max(r, Math.max(g, b));\n\n\treturn [h, w * 100, b * 100];\n};\n\nconvert.rgb.cmyk = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar c;\n\tvar m;\n\tvar y;\n\tvar k;\n\n\tk = Math.min(1 - r, 1 - g, 1 - b);\n\tc = (1 - r - k) / (1 - k) || 0;\n\tm = (1 - g - k) / (1 - k) || 0;\n\ty = (1 - b - k) / (1 - k) || 0;\n\n\treturn [c * 100, m * 100, y * 100, k * 100];\n};\n\n/**\n * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance\n * */\nfunction comparativeDistance(x, y) {\n\treturn (\n\t\tMath.pow(x[0] - y[0], 2) +\n\t\tMath.pow(x[1] - y[1], 2) +\n\t\tMath.pow(x[2] - y[2], 2)\n\t);\n}\n\nconvert.rgb.keyword = function (rgb) {\n\tvar reversed = reverseKeywords[rgb];\n\tif (reversed) {\n\t\treturn reversed;\n\t}\n\n\tvar currentClosestDistance = Infinity;\n\tvar currentClosestKeyword;\n\n\tfor (var keyword in cssKeywords) {\n\t\tif (cssKeywords.hasOwnProperty(keyword)) {\n\t\t\tvar value = cssKeywords[keyword];\n\n\t\t\t// Compute comparative distance\n\t\t\tvar distance = comparativeDistance(rgb, value);\n\n\t\t\t// Check if its less, if so set as closest\n\t\t\tif (distance < currentClosestDistance) {\n\t\t\t\tcurrentClosestDistance = distance;\n\t\t\t\tcurrentClosestKeyword = keyword;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn currentClosestKeyword;\n};\n\nconvert.keyword.rgb = function (keyword) {\n\treturn cssKeywords[keyword];\n};\n\nconvert.rgb.xyz = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\n\t// assume sRGB\n\tr = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);\n\tg = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);\n\tb = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);\n\n\tvar x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);\n\tvar y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);\n\tvar z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);\n\n\treturn [x * 100, y * 100, z * 100];\n};\n\nconvert.rgb.lab = function (rgb) {\n\tvar xyz = convert.rgb.xyz(rgb);\n\tvar x = xyz[0];\n\tvar y = xyz[1];\n\tvar z = xyz[2];\n\tvar l;\n\tvar a;\n\tvar b;\n\n\tx /= 95.047;\n\ty /= 100;\n\tz /= 108.883;\n\n\tx = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);\n\ty = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);\n\tz = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);\n\n\tl = (116 * y) - 16;\n\ta = 500 * (x - y);\n\tb = 200 * (y - z);\n\n\treturn [l, a, b];\n};\n\nconvert.hsl.rgb = function (hsl) {\n\tvar h = hsl[0] / 360;\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar t1;\n\tvar t2;\n\tvar t3;\n\tvar rgb;\n\tvar val;\n\n\tif (s === 0) {\n\t\tval = l * 255;\n\t\treturn [val, val, val];\n\t}\n\n\tif (l < 0.5) {\n\t\tt2 = l * (1 + s);\n\t} else {\n\t\tt2 = l + s - l * s;\n\t}\n\n\tt1 = 2 * l - t2;\n\n\trgb = [0, 0, 0];\n\tfor (var i = 0; i < 3; i++) {\n\t\tt3 = h + 1 / 3 * -(i - 1);\n\t\tif (t3 < 0) {\n\t\t\tt3++;\n\t\t}\n\t\tif (t3 > 1) {\n\t\t\tt3--;\n\t\t}\n\n\t\tif (6 * t3 < 1) {\n\t\t\tval = t1 + (t2 - t1) * 6 * t3;\n\t\t} else if (2 * t3 < 1) {\n\t\t\tval = t2;\n\t\t} else if (3 * t3 < 2) {\n\t\t\tval = t1 + (t2 - t1) * (2 / 3 - t3) * 6;\n\t\t} else {\n\t\t\tval = t1;\n\t\t}\n\n\t\trgb[i] = val * 255;\n\t}\n\n\treturn rgb;\n};\n\nconvert.hsl.hsv = function (hsl) {\n\tvar h = hsl[0];\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar smin = s;\n\tvar lmin = Math.max(l, 0.01);\n\tvar sv;\n\tvar v;\n\n\tl *= 2;\n\ts *= (l <= 1) ? l : 2 - l;\n\tsmin *= lmin <= 1 ? lmin : 2 - lmin;\n\tv = (l + s) / 2;\n\tsv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);\n\n\treturn [h, sv * 100, v * 100];\n};\n\nconvert.hsv.rgb = function (hsv) {\n\tvar h = hsv[0] / 60;\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\tvar hi = Math.floor(h) % 6;\n\n\tvar f = h - Math.floor(h);\n\tvar p = 255 * v * (1 - s);\n\tvar q = 255 * v * (1 - (s * f));\n\tvar t = 255 * v * (1 - (s * (1 - f)));\n\tv *= 255;\n\n\tswitch (hi) {\n\t\tcase 0:\n\t\t\treturn [v, t, p];\n\t\tcase 1:\n\t\t\treturn [q, v, p];\n\t\tcase 2:\n\t\t\treturn [p, v, t];\n\t\tcase 3:\n\t\t\treturn [p, q, v];\n\t\tcase 4:\n\t\t\treturn [t, p, v];\n\t\tcase 5:\n\t\t\treturn [v, p, q];\n\t}\n};\n\nconvert.hsv.hsl = function (hsv) {\n\tvar h = hsv[0];\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\tvar vmin = Math.max(v, 0.01);\n\tvar lmin;\n\tvar sl;\n\tvar l;\n\n\tl = (2 - s) * v;\n\tlmin = (2 - s) * vmin;\n\tsl = s * vmin;\n\tsl /= (lmin <= 1) ? lmin : 2 - lmin;\n\tsl = sl || 0;\n\tl /= 2;\n\n\treturn [h, sl * 100, l * 100];\n};\n\n// http://dev.w3.org/csswg/css-color/#hwb-to-rgb\nconvert.hwb.rgb = function (hwb) {\n\tvar h = hwb[0] / 360;\n\tvar wh = hwb[1] / 100;\n\tvar bl = hwb[2] / 100;\n\tvar ratio = wh + bl;\n\tvar i;\n\tvar v;\n\tvar f;\n\tvar n;\n\n\t// wh + bl cant be > 1\n\tif (ratio > 1) {\n\t\twh /= ratio;\n\t\tbl /= ratio;\n\t}\n\n\ti = Math.floor(6 * h);\n\tv = 1 - bl;\n\tf = 6 * h - i;\n\n\tif ((i & 0x01) !== 0) {\n\t\tf = 1 - f;\n\t}\n\n\tn = wh + f * (v - wh); // linear interpolation\n\n\tvar r;\n\tvar g;\n\tvar b;\n\tswitch (i) {\n\t\tdefault:\n\t\tcase 6:\n\t\tcase 0: r = v; g = n; b = wh; break;\n\t\tcase 1: r = n; g = v; b = wh; break;\n\t\tcase 2: r = wh; g = v; b = n; break;\n\t\tcase 3: r = wh; g = n; b = v; break;\n\t\tcase 4: r = n; g = wh; b = v; break;\n\t\tcase 5: r = v; g = wh; b = n; break;\n\t}\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.cmyk.rgb = function (cmyk) {\n\tvar c = cmyk[0] / 100;\n\tvar m = cmyk[1] / 100;\n\tvar y = cmyk[2] / 100;\n\tvar k = cmyk[3] / 100;\n\tvar r;\n\tvar g;\n\tvar b;\n\n\tr = 1 - Math.min(1, c * (1 - k) + k);\n\tg = 1 - Math.min(1, m * (1 - k) + k);\n\tb = 1 - Math.min(1, y * (1 - k) + k);\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.xyz.rgb = function (xyz) {\n\tvar x = xyz[0] / 100;\n\tvar y = xyz[1] / 100;\n\tvar z = xyz[2] / 100;\n\tvar r;\n\tvar g;\n\tvar b;\n\n\tr = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);\n\tg = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);\n\tb = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);\n\n\t// assume sRGB\n\tr = r > 0.0031308\n\t\t? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)\n\t\t: r * 12.92;\n\n\tg = g > 0.0031308\n\t\t? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)\n\t\t: g * 12.92;\n\n\tb = b > 0.0031308\n\t\t? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)\n\t\t: b * 12.92;\n\n\tr = Math.min(Math.max(0, r), 1);\n\tg = Math.min(Math.max(0, g), 1);\n\tb = Math.min(Math.max(0, b), 1);\n\n\treturn [r * 255, g * 255, b * 255];\n};\n\nconvert.xyz.lab = function (xyz) {\n\tvar x = xyz[0];\n\tvar y = xyz[1];\n\tvar z = xyz[2];\n\tvar l;\n\tvar a;\n\tvar b;\n\n\tx /= 95.047;\n\ty /= 100;\n\tz /= 108.883;\n\n\tx = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);\n\ty = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);\n\tz = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);\n\n\tl = (116 * y) - 16;\n\ta = 500 * (x - y);\n\tb = 200 * (y - z);\n\n\treturn [l, a, b];\n};\n\nconvert.lab.xyz = function (lab) {\n\tvar l = lab[0];\n\tvar a = lab[1];\n\tvar b = lab[2];\n\tvar x;\n\tvar y;\n\tvar z;\n\n\ty = (l + 16) / 116;\n\tx = a / 500 + y;\n\tz = y - b / 200;\n\n\tvar y2 = Math.pow(y, 3);\n\tvar x2 = Math.pow(x, 3);\n\tvar z2 = Math.pow(z, 3);\n\ty = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;\n\tx = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;\n\tz = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;\n\n\tx *= 95.047;\n\ty *= 100;\n\tz *= 108.883;\n\n\treturn [x, y, z];\n};\n\nconvert.lab.lch = function (lab) {\n\tvar l = lab[0];\n\tvar a = lab[1];\n\tvar b = lab[2];\n\tvar hr;\n\tvar h;\n\tvar c;\n\n\thr = Math.atan2(b, a);\n\th = hr * 360 / 2 / Math.PI;\n\n\tif (h < 0) {\n\t\th += 360;\n\t}\n\n\tc = Math.sqrt(a * a + b * b);\n\n\treturn [l, c, h];\n};\n\nconvert.lch.lab = function (lch) {\n\tvar l = lch[0];\n\tvar c = lch[1];\n\tvar h = lch[2];\n\tvar a;\n\tvar b;\n\tvar hr;\n\n\thr = h / 360 * 2 * Math.PI;\n\ta = c * Math.cos(hr);\n\tb = c * Math.sin(hr);\n\n\treturn [l, a, b];\n};\n\nconvert.rgb.ansi16 = function (args) {\n\tvar r = args[0];\n\tvar g = args[1];\n\tvar b = args[2];\n\tvar value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization\n\n\tvalue = Math.round(value / 50);\n\n\tif (value === 0) {\n\t\treturn 30;\n\t}\n\n\tvar ansi = 30\n\t\t+ ((Math.round(b / 255) << 2)\n\t\t| (Math.round(g / 255) << 1)\n\t\t| Math.round(r / 255));\n\n\tif (value === 2) {\n\t\tansi += 60;\n\t}\n\n\treturn ansi;\n};\n\nconvert.hsv.ansi16 = function (args) {\n\t// optimization here; we already know the value and don't need to get\n\t// it converted for us.\n\treturn convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);\n};\n\nconvert.rgb.ansi256 = function (args) {\n\tvar r = args[0];\n\tvar g = args[1];\n\tvar b = args[2];\n\n\t// we use the extended greyscale palette here, with the exception of\n\t// black and white. normal palette only has 4 greyscale shades.\n\tif (r === g && g === b) {\n\t\tif (r < 8) {\n\t\t\treturn 16;\n\t\t}\n\n\t\tif (r > 248) {\n\t\t\treturn 231;\n\t\t}\n\n\t\treturn Math.round(((r - 8) / 247) * 24) + 232;\n\t}\n\n\tvar ansi = 16\n\t\t+ (36 * Math.round(r / 255 * 5))\n\t\t+ (6 * Math.round(g / 255 * 5))\n\t\t+ Math.round(b / 255 * 5);\n\n\treturn ansi;\n};\n\nconvert.ansi16.rgb = function (args) {\n\tvar color = args % 10;\n\n\t// handle greyscale\n\tif (color === 0 || color === 7) {\n\t\tif (args > 50) {\n\t\t\tcolor += 3.5;\n\t\t}\n\n\t\tcolor = color / 10.5 * 255;\n\n\t\treturn [color, color, color];\n\t}\n\n\tvar mult = (~~(args > 50) + 1) * 0.5;\n\tvar r = ((color & 1) * mult) * 255;\n\tvar g = (((color >> 1) & 1) * mult) * 255;\n\tvar b = (((color >> 2) & 1) * mult) * 255;\n\n\treturn [r, g, b];\n};\n\nconvert.ansi256.rgb = function (args) {\n\t// handle greyscale\n\tif (args >= 232) {\n\t\tvar c = (args - 232) * 10 + 8;\n\t\treturn [c, c, c];\n\t}\n\n\targs -= 16;\n\n\tvar rem;\n\tvar r = Math.floor(args / 36) / 5 * 255;\n\tvar g = Math.floor((rem = args % 36) / 6) / 5 * 255;\n\tvar b = (rem % 6) / 5 * 255;\n\n\treturn [r, g, b];\n};\n\nconvert.rgb.hex = function (args) {\n\tvar integer = ((Math.round(args[0]) & 0xFF) << 16)\n\t\t+ ((Math.round(args[1]) & 0xFF) << 8)\n\t\t+ (Math.round(args[2]) & 0xFF);\n\n\tvar string = integer.toString(16).toUpperCase();\n\treturn '000000'.substring(string.length) + string;\n};\n\nconvert.hex.rgb = function (args) {\n\tvar match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);\n\tif (!match) {\n\t\treturn [0, 0, 0];\n\t}\n\n\tvar colorString = match[0];\n\n\tif (match[0].length === 3) {\n\t\tcolorString = colorString.split('').map(function (char) {\n\t\t\treturn char + char;\n\t\t}).join('');\n\t}\n\n\tvar integer = parseInt(colorString, 16);\n\tvar r = (integer >> 16) & 0xFF;\n\tvar g = (integer >> 8) & 0xFF;\n\tvar b = integer & 0xFF;\n\n\treturn [r, g, b];\n};\n\nconvert.rgb.hcg = function (rgb) {\n\tvar r = rgb[0] / 255;\n\tvar g = rgb[1] / 255;\n\tvar b = rgb[2] / 255;\n\tvar max = Math.max(Math.max(r, g), b);\n\tvar min = Math.min(Math.min(r, g), b);\n\tvar chroma = (max - min);\n\tvar grayscale;\n\tvar hue;\n\n\tif (chroma < 1) {\n\t\tgrayscale = min / (1 - chroma);\n\t} else {\n\t\tgrayscale = 0;\n\t}\n\n\tif (chroma <= 0) {\n\t\thue = 0;\n\t} else\n\tif (max === r) {\n\t\thue = ((g - b) / chroma) % 6;\n\t} else\n\tif (max === g) {\n\t\thue = 2 + (b - r) / chroma;\n\t} else {\n\t\thue = 4 + (r - g) / chroma + 4;\n\t}\n\n\thue /= 6;\n\thue %= 1;\n\n\treturn [hue * 360, chroma * 100, grayscale * 100];\n};\n\nconvert.hsl.hcg = function (hsl) {\n\tvar s = hsl[1] / 100;\n\tvar l = hsl[2] / 100;\n\tvar c = 1;\n\tvar f = 0;\n\n\tif (l < 0.5) {\n\t\tc = 2.0 * s * l;\n\t} else {\n\t\tc = 2.0 * s * (1.0 - l);\n\t}\n\n\tif (c < 1.0) {\n\t\tf = (l - 0.5 * c) / (1.0 - c);\n\t}\n\n\treturn [hsl[0], c * 100, f * 100];\n};\n\nconvert.hsv.hcg = function (hsv) {\n\tvar s = hsv[1] / 100;\n\tvar v = hsv[2] / 100;\n\n\tvar c = s * v;\n\tvar f = 0;\n\n\tif (c < 1.0) {\n\t\tf = (v - c) / (1 - c);\n\t}\n\n\treturn [hsv[0], c * 100, f * 100];\n};\n\nconvert.hcg.rgb = function (hcg) {\n\tvar h = hcg[0] / 360;\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tif (c === 0.0) {\n\t\treturn [g * 255, g * 255, g * 255];\n\t}\n\n\tvar pure = [0, 0, 0];\n\tvar hi = (h % 1) * 6;\n\tvar v = hi % 1;\n\tvar w = 1 - v;\n\tvar mg = 0;\n\n\tswitch (Math.floor(hi)) {\n\t\tcase 0:\n\t\t\tpure[0] = 1; pure[1] = v; pure[2] = 0; break;\n\t\tcase 1:\n\t\t\tpure[0] = w; pure[1] = 1; pure[2] = 0; break;\n\t\tcase 2:\n\t\t\tpure[0] = 0; pure[1] = 1; pure[2] = v; break;\n\t\tcase 3:\n\t\t\tpure[0] = 0; pure[1] = w; pure[2] = 1; break;\n\t\tcase 4:\n\t\t\tpure[0] = v; pure[1] = 0; pure[2] = 1; break;\n\t\tdefault:\n\t\t\tpure[0] = 1; pure[1] = 0; pure[2] = w;\n\t}\n\n\tmg = (1.0 - c) * g;\n\n\treturn [\n\t\t(c * pure[0] + mg) * 255,\n\t\t(c * pure[1] + mg) * 255,\n\t\t(c * pure[2] + mg) * 255\n\t];\n};\n\nconvert.hcg.hsv = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tvar v = c + g * (1.0 - c);\n\tvar f = 0;\n\n\tif (v > 0.0) {\n\t\tf = c / v;\n\t}\n\n\treturn [hcg[0], f * 100, v * 100];\n};\n\nconvert.hcg.hsl = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\n\tvar l = g * (1.0 - c) + 0.5 * c;\n\tvar s = 0;\n\n\tif (l > 0.0 && l < 0.5) {\n\t\ts = c / (2 * l);\n\t} else\n\tif (l >= 0.5 && l < 1.0) {\n\t\ts = c / (2 * (1 - l));\n\t}\n\n\treturn [hcg[0], s * 100, l * 100];\n};\n\nconvert.hcg.hwb = function (hcg) {\n\tvar c = hcg[1] / 100;\n\tvar g = hcg[2] / 100;\n\tvar v = c + g * (1.0 - c);\n\treturn [hcg[0], (v - c) * 100, (1 - v) * 100];\n};\n\nconvert.hwb.hcg = function (hwb) {\n\tvar w = hwb[1] / 100;\n\tvar b = hwb[2] / 100;\n\tvar v = 1 - b;\n\tvar c = v - w;\n\tvar g = 0;\n\n\tif (c < 1) {\n\t\tg = (v - c) / (1 - c);\n\t}\n\n\treturn [hwb[0], c * 100, g * 100];\n};\n\nconvert.apple.rgb = function (apple) {\n\treturn [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];\n};\n\nconvert.rgb.apple = function (rgb) {\n\treturn [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];\n};\n\nconvert.gray.rgb = function (args) {\n\treturn [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];\n};\n\nconvert.gray.hsl = convert.gray.hsv = function (args) {\n\treturn [0, 0, args[0]];\n};\n\nconvert.gray.hwb = function (gray) {\n\treturn [0, 100, gray[0]];\n};\n\nconvert.gray.cmyk = function (gray) {\n\treturn [0, 0, 0, gray[0]];\n};\n\nconvert.gray.lab = function (gray) {\n\treturn [gray[0], 0, 0];\n};\n\nconvert.gray.hex = function (gray) {\n\tvar val = Math.round(gray[0] / 100 * 255) & 0xFF;\n\tvar integer = (val << 16) + (val << 8) + val;\n\n\tvar string = integer.toString(16).toUpperCase();\n\treturn '000000'.substring(string.length) + string;\n};\n\nconvert.rgb.gray = function (rgb) {\n\tvar val = (rgb[0] + rgb[1] + rgb[2]) / 3;\n\treturn [val / 255 * 100];\n};\n","export * from './ContentEdit';\r\nexport * from './ContextMenu';\r\nexport * from './CustomReplace';\r\nexport * from './CutPasteListChain';\r\nexport * from './HyperLink';\r\nexport * from './ImageEdit';\r\nexport * from './ImageResize';\r\nexport * from './Paste';\r\nexport * from './Picker';\r\nexport * from './TableResize';\r\nexport * from './Watermark';\r\n","import { AutoLinkFeatures } from './features/autoLinkFeatures';\nimport { CursorFeatures } from './features/cursorFeatures';\nimport { EntityFeatures } from './features/entityFeatures';\nimport { ListFeatures } from './features/listFeatures';\nimport { MarkdownFeatures } from './features/markdownFeatures';\nimport { QuoteFeatures } from './features/quoteFeatures';\nimport { ShortcutFeatures } from './features/shortcutFeatures';\nimport { StructuredNodeFeatures } from './features/structuredNodeFeatures';\nimport { TableFeatures } from './features/tableFeatures';\nimport {\n BuildInEditFeature,\n ContentEditFeatureSettings,\n PluginEvent,\n} from 'roosterjs-editor-types';\n\nconst allFeatures = {\n ...ListFeatures,\n ...QuoteFeatures,\n ...TableFeatures,\n ...StructuredNodeFeatures,\n ...AutoLinkFeatures,\n ...ShortcutFeatures,\n ...CursorFeatures,\n ...MarkdownFeatures,\n ...EntityFeatures,\n};\n\n/**\n * Get all content edit features provided by roosterjs\n */\nexport default function getAllFeatures(): Record<\n keyof ContentEditFeatureSettings,\n BuildInEditFeature\n> {\n return allFeatures;\n}\n","import blockFormat from '../utils/blockFormat';\r\nimport execCommand from '../utils/execCommand';\r\nimport setBackgroundColor from './setBackgroundColor';\r\nimport setFontName from './setFontName';\r\nimport setFontSize from './setFontSize';\r\nimport setTextColor from './setTextColor';\r\nimport toggleBold from './toggleBold';\r\nimport toggleItalic from './toggleItalic';\r\nimport toggleUnderline from './toggleUnderline';\r\nimport {\r\n ChangeSource,\r\n ClearFormatMode,\r\n DocumentCommand,\r\n IEditor,\r\n QueryScope,\r\n} from 'roosterjs-editor-types';\r\nimport {\r\n collapseNodesInRegion,\r\n getSelectedBlockElementsInRegion,\r\n getStyles,\r\n getTagOfNode,\r\n isBlockElement,\r\n isNodeInRegion,\r\n isVoidHtmlElement,\r\n PartialInlineElement,\r\n safeInstanceOf,\r\n setStyles,\r\n splitBalancedNodeRange,\r\n toArray,\r\n unwrap,\r\n wrap,\r\n} from 'roosterjs-editor-dom';\r\n\r\nconst STYLES_TO_REMOVE = ['font', 'text-decoration', 'color', 'background'];\r\nconst TAGS_TO_UNWRAP = 'B,I,U,STRONG,EM,SUB,SUP,STRIKE,FONT,CENTER,H1,H2,H3,H4,H5,H6,UL,OL,LI,SPAN,P,BLOCKQUOTE,CODE,S,PRE'.split(\r\n ','\r\n);\r\nconst ATTRIBUTES_TO_PRESERVE = ['href', 'src'];\r\nconst TAGS_TO_STOP_UNWRAP = ['TD', 'TH', 'TR', 'TABLE', 'TBODY', 'THEAD'];\r\n\r\n/**\r\n * @param editor The editor instance\r\n * @returns if the current selection is composed of two or more block elements\r\n */\r\nfunction isMultiBlockSelection(editor: IEditor): boolean {\r\n let transverser = editor.getSelectionTraverser();\r\n let blockElement = transverser.currentBlockElement;\r\n if (!blockElement) {\r\n return false;\r\n }\r\n\r\n let nextBlockElement = transverser.getNextBlockElement();\r\n\r\n //At least two blocks are selected\r\n return !!nextBlockElement;\r\n}\r\n\r\nfunction clearNodeFormat(node: Node): boolean {\r\n // 1. Recursively clear format of all its child nodes\r\n const areBlockElements = toArray(node.childNodes).map(clearNodeFormat);\r\n let areAllChildrenBlock = areBlockElements.every(b => b);\r\n let returnBlockElement = isBlockElement(node);\r\n\r\n // 2. Unwrap the tag if necessary\r\n const tag = getTagOfNode(node);\r\n if (tag) {\r\n if (\r\n TAGS_TO_UNWRAP.indexOf(tag) >= 0 ||\r\n (areAllChildrenBlock &&\r\n !isVoidHtmlElement(node) &&\r\n TAGS_TO_STOP_UNWRAP.indexOf(tag) < 0)\r\n ) {\r\n if (returnBlockElement && !areAllChildrenBlock) {\r\n wrap(node);\r\n }\r\n unwrap(node);\r\n } else {\r\n // 3. Otherwise, remove all attributes\r\n clearAttribute(node as HTMLElement);\r\n }\r\n }\r\n\r\n return returnBlockElement;\r\n}\r\n\r\nfunction clearAttribute(element: HTMLElement) {\r\n const isTableCell = safeInstanceOf(element, 'HTMLTableCellElement');\r\n\r\n for (let attr of toArray(element.attributes)) {\r\n if (isTableCell && attr.name == 'style') {\r\n removeNonBorderStyles(element);\r\n } else if (\r\n ATTRIBUTES_TO_PRESERVE.indexOf(attr.name.toLowerCase()) < 0 &&\r\n attr.name.indexOf('data-') != 0\r\n ) {\r\n element.removeAttribute(attr.name);\r\n }\r\n }\r\n}\r\n\r\nfunction removeNonBorderStyles(element: HTMLElement): Record {\r\n const styles = getStyles(element);\r\n const result: Record = {};\r\n\r\n Object.keys(styles).forEach(name => {\r\n if (name.indexOf('border') < 0) {\r\n result[name] = styles[name];\r\n delete styles[name];\r\n }\r\n });\r\n\r\n setStyles(element, styles);\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Clear the format of the selected text or list of blocks\r\n * If the current selection is compose of multiple block elements then remove the text and struture format for all the selected blocks\r\n * If the current selection is compose of a partial inline element then only the text format is removed from the current selection\r\n * @param editor The editor instance\r\n */\r\nfunction clearAutoDetectFormat(editor: IEditor) {\r\n const isMultiBlock = isMultiBlockSelection(editor);\r\n if (!isMultiBlock) {\r\n const transverser = editor.getSelectionTraverser();\r\n const inlineElement = transverser.currentInlineElement;\r\n const isPartial = inlineElement instanceof PartialInlineElement;\r\n if (isPartial) {\r\n clearFormat(editor);\r\n return;\r\n }\r\n }\r\n clearBlockFormat(editor);\r\n}\r\n\r\n/**\r\n * Clear all formats of selected blocks.\r\n * When selection is collapsed, only clear format of current block.\r\n * @param editor The editor instance\r\n */\r\nfunction clearBlockFormat(editor: IEditor) {\r\n blockFormat(editor, region => {\r\n const blocks = getSelectedBlockElementsInRegion(region);\r\n let nodes = collapseNodesInRegion(region, blocks);\r\n\r\n if (editor.contains(region.rootNode)) {\r\n // If there are styles on table cell, wrap all its children and move down all non-border styles.\r\n // So that we can preserve styles for unselected blocks as well as border styles for table\r\n const nonborderStyles = removeNonBorderStyles(region.rootNode);\r\n if (Object.keys(nonborderStyles).length > 0) {\r\n const wrapper = wrap(toArray(region.rootNode.childNodes));\r\n setStyles(wrapper, nonborderStyles);\r\n }\r\n }\r\n\r\n while (nodes.length > 0 && isNodeInRegion(region, nodes[0].parentNode)) {\r\n nodes = [splitBalancedNodeRange(nodes)];\r\n }\r\n\r\n nodes.forEach(clearNodeFormat);\r\n });\r\n}\r\n\r\nfunction clearInlineFormat(editor: IEditor) {\r\n editor.focus();\r\n editor.addUndoSnapshot(() => {\r\n execCommand(editor, DocumentCommand.RemoveFormat);\r\n\r\n editor.queryElements('[class]', QueryScope.OnSelection, node =>\r\n node.removeAttribute('class')\r\n );\r\n\r\n const defaultFormat = editor.getDefaultFormat();\r\n const isDefaultFormatEmpty = Object.keys(defaultFormat).length === 0;\r\n editor.queryElements('[style]', QueryScope.InSelection, node => {\r\n STYLES_TO_REMOVE.forEach(style => node.style.removeProperty(style));\r\n\r\n // when default format is empty, keep the HTML minimum by removing style attribute if there's no style\r\n // (note: because default format is empty, we're not adding style back in)\r\n if (isDefaultFormatEmpty && node.getAttribute('style') === '') {\r\n node.removeAttribute('style');\r\n }\r\n });\r\n\r\n if (!isDefaultFormatEmpty) {\r\n if (defaultFormat.fontFamily) {\r\n setFontName(editor, defaultFormat.fontFamily);\r\n }\r\n if (defaultFormat.fontSize) {\r\n setFontSize(editor, defaultFormat.fontSize);\r\n }\r\n if (defaultFormat.textColor) {\r\n if (defaultFormat.textColors) {\r\n setTextColor(editor, defaultFormat.textColors);\r\n } else {\r\n setTextColor(editor, defaultFormat.textColor);\r\n }\r\n }\r\n if (defaultFormat.backgroundColor) {\r\n if (defaultFormat.backgroundColors) {\r\n setBackgroundColor(editor, defaultFormat.backgroundColors);\r\n } else {\r\n setBackgroundColor(editor, defaultFormat.backgroundColor);\r\n }\r\n }\r\n if (defaultFormat.bold) {\r\n toggleBold(editor);\r\n }\r\n if (defaultFormat.italic) {\r\n toggleItalic(editor);\r\n }\r\n if (defaultFormat.underline) {\r\n toggleUnderline(editor);\r\n }\r\n }\r\n }, ChangeSource.Format);\r\n}\r\n\r\n/**\r\n * Clear the format in current selection, after cleaning, the format will be\r\n * changed to default format. The format that get cleaned include B/I/U/font name/\r\n * font size/text color/background color/align left/align right/align center/superscript/subscript\r\n * @param editor The editor instance\r\n * @param formatType type of format to apply\r\n */\r\nexport default function clearFormat(\r\n editor: IEditor,\r\n formatType: ClearFormatMode = ClearFormatMode.Inline\r\n) {\r\n switch (formatType) {\r\n case ClearFormatMode.Inline:\r\n clearInlineFormat(editor);\r\n break;\r\n case ClearFormatMode.Block:\r\n clearBlockFormat(editor);\r\n break;\r\n default:\r\n clearAutoDetectFormat(editor);\r\n }\r\n}\r\n","import { IEditor } from 'roosterjs-editor-types';\nimport { Position, VListChain } from 'roosterjs-editor-dom';\n\n/**\n * Commit changes of all list changes when experiment features are allowed\n * @param editor The Editor object\n * @param chains List chains to commit\n */\nexport default function experimentCommitListChains(editor: IEditor, chains: VListChain[]) {\n if (chains?.length > 0) {\n const range = editor.getSelectionRange();\n const start = range && Position.getStart(range);\n const end = range && Position.getEnd(range);\n chains.forEach(chain => chain.commit());\n editor.select(start, end);\n }\n}\n","import applyInlineStyle from '../utils/applyInlineStyle';\r\nimport { IEditor, ModeIndependentColor } from 'roosterjs-editor-types';\r\nimport { setColor } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Set background color at current selection\r\n * @param editor The editor instance\r\n * @param color One of two options:\r\n * The color string, can be any of the predefined color names (e.g, 'red')\r\n * or hexadecimal color string (e.g, '#FF0000') or rgb value (e.g, 'rgb(255, 0, 0)') supported by browser.\r\n * Currently there's no validation to the string, if the passed string is invalid, it won't take effect\r\n * Alternatively, you can pass a @typedef ModeIndependentColor. If in light mode, the lightModeColor property will be used.\r\n * If in dark mode, the darkModeColor will be used and the lightModeColor will be used when converting back to light mode.\r\n **/\r\nexport default function setBackgroundColor(editor: IEditor, color: string | ModeIndependentColor) {\r\n applyInlineStyle(editor, (element, isInnerNode) => {\r\n setColor(element, isInnerNode ? '' : color, true /*isBackground*/, editor.isDarkMode());\r\n });\r\n}\r\n","import applyInlineStyle from '../utils/applyInlineStyle';\r\nimport { IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Set font name at selection\r\n * @param editor The editor instance\r\n * @param fontName The fontName string, should be a valid CSS font-family style.\r\n * Currently there's no validation to the string, if the passed string is invalid, it won't take affect\r\n */\r\nexport default function setFontName(editor: IEditor, fontName: string) {\r\n fontName = fontName.trim();\r\n // The browser provided execCommand creates a HTML tag with face attribute. is not HTML5 standard\r\n // (http://www.w3schools.com/tags/tag_font.asp). Use applyInlineStyle which gives flexibility on applying inline style\r\n // for here, we use CSS font-family style\r\n applyInlineStyle(editor, (element, isInnerNode) => {\r\n element.style.fontFamily = isInnerNode ? '' : fontName;\r\n });\r\n}\r\n","import applyInlineStyle from '../utils/applyInlineStyle';\r\nimport { getComputedStyle } from 'roosterjs-editor-dom';\r\nimport { IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Set font size at selection\r\n * @param editor The editor instance\r\n * @param fontSize The fontSize string, should be a valid CSS font-size style.\r\n * Currently there's no validation to the string, if the passed string is invalid, it won't take affect\r\n */\r\nexport default function setFontSize(editor: IEditor, fontSize: string) {\r\n fontSize = fontSize.trim();\r\n // The browser provided execCommand only accepts 1-7 point value. In addition, it uses HTML tag with size attribute.\r\n // is not HTML5 standard (http://www.w3schools.com/tags/tag_font.asp). Use applyInlineStyle which gives flexibility on applying inline style\r\n // for here, we use CSS font-size style\r\n applyInlineStyle(editor, (element, isInnerNode) => {\r\n element.style.fontSize = isInnerNode ? '' : fontSize;\r\n let lineHeight = getComputedStyle(element, 'line-height');\r\n if (lineHeight != 'normal') {\r\n element.style.lineHeight = 'normal';\r\n }\r\n });\r\n}\r\n","import applyInlineStyle from '../utils/applyInlineStyle';\r\nimport { IEditor, ModeIndependentColor } from 'roosterjs-editor-types';\r\nimport { setColor } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Set text color at selection\r\n * @param editor The editor instance\r\n * @param color One of two options:\r\n * The color string, can be any of the predefined color names (e.g, 'red')\r\n * or hexadecimal color string (e.g, '#FF0000') or rgb value (e.g, 'rgb(255, 0, 0)') supported by browser.\r\n * Currently there's no validation to the string, if the passed string is invalid, it won't take affect\r\n * Alternatively, you can pass a @typedef ModeIndependentColor. If in light mode, the lightModeColor property will be used.\r\n * If in dark mode, the darkModeColor will be used and the lightModeColor will be used when converting back to light mode.\r\n */\r\nexport default function setTextColor(editor: IEditor, color: string | ModeIndependentColor) {\r\n applyInlineStyle(editor, (element, isInnerNode) => {\r\n setColor(element, isInnerNode ? '' : color, false /*isBackground*/, editor.isDarkMode());\r\n });\r\n}\r\n","import execCommand from '../utils/execCommand';\r\nimport { DocumentCommand, IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Toggle bold at selection\r\n * If selection is collapsed, it will only affect the following input after caret\r\n * If selection contains only bold text, the bold style will be removed\r\n * If selection contains only normal text, bold style will be added to the whole selected text\r\n * If selection contains both bold and normal text, bold style will be added to the whole selected text\r\n * @param editor The editor instance\r\n */\r\nexport default function toggleBold(editor: IEditor) {\r\n execCommand(editor, DocumentCommand.Bold);\r\n}\r\n","import execCommand from '../utils/execCommand';\r\nimport { DocumentCommand, IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Toggle italic at selection\r\n * If selection is collapsed, it will only affect the input after caret\r\n * If selection contains only italic text, the italic style will be removed\r\n * If selection contains only normal text, italic style will be added to the whole selected text\r\n * If selection contains both italic and normal text, italic style will be added to the whole selected text\r\n * @param editor The editor instance\r\n */\r\nexport default function toggleItalic(editor: IEditor) {\r\n execCommand(editor, DocumentCommand.Italic);\r\n}\r\n","import execCommand from '../utils/execCommand';\r\nimport { DocumentCommand, IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Toggle underline at selection\r\n * If selection is collapsed, it will only affect the input after caret\r\n * If selection contains only underlined text, the underline style will be removed\r\n * If selection contains only normal text, underline style will be added to the whole selected text\r\n * If selection contains both underlined and normal text, the underline style will be added to the whole selected text\r\n * @param editor The editor instance\r\n */\r\nexport default function toggleUnderline(editor: IEditor) {\r\n execCommand(editor, DocumentCommand.Underline);\r\n}\r\n","import blockFormat from '../utils/blockFormat';\nimport { createVListFromRegion, getBlockElementAtNode } from 'roosterjs-editor-dom';\nimport { IEditor, ListType } from 'roosterjs-editor-types';\n\n/**\n * @internal\n */\nexport default function toggleListType(editor: IEditor, listType: ListType): void;\nexport default function toggleListType(\n editor: IEditor,\n listType: ListType.Ordered,\n startNumber: number\n): void;\n\nexport default function toggleListType(editor: IEditor, listType: ListType, startNumber?: number) {\n blockFormat(editor, (region, start, end, chains) => {\n const chain =\n startNumber > 0 && chains.filter(chain => chain.canAppendAtCursor(startNumber))[0];\n const vList =\n chain && start.equalTo(end)\n ? chain.createVListAtBlock(\n getBlockElementAtNode(region.rootNode, start.node)?.collapseToSingleElement(),\n startNumber\n )\n : createVListFromRegion(region, true /*includeSiblingLists*/);\n\n if (vList) {\n vList.changeListType(start, end, listType);\n vList.writeBack();\n }\n });\n}\n","import blockFormat from './blockFormat';\nimport { IEditor } from 'roosterjs-editor-types';\nimport {\n collapseNodesInRegion,\n getSelectedBlockElementsInRegion,\n getTagOfNode,\n isNodeInRegion,\n splitBalancedNodeRange,\n toArray,\n wrap,\n} from 'roosterjs-editor-dom';\n\n/**\n * @internal\n * Toggle a tag at selection, if selection already contains elements of such tag,\n * the elements will be untagged and other elements will take no effect\n * @param editor The editor instance\n * @param wrapFunction The wrap function\n * @param beforeRunCallback A callback function to run before looping all regions. If it returns false,\n * the loop for regions will be skipped\n */\nexport default function blockWrap(\n editor: IEditor,\n wrapFunction: (nodes: Node[]) => void,\n beforeRunCallback: () => boolean\n): void {\n blockFormat(\n editor,\n region => {\n const blocks = getSelectedBlockElementsInRegion(region, true /*createBlockIfEmpty*/);\n let nodes = collapseNodesInRegion(region, blocks);\n if (nodes.length > 0) {\n if (nodes.length == 1) {\n const NodeTag = getTagOfNode(nodes[0]);\n if (NodeTag == 'BR') {\n nodes = [wrap(nodes[0])];\n } else if (NodeTag == 'LI' || NodeTag == 'TD') {\n nodes = toArray(nodes[0].childNodes);\n }\n }\n\n while (\n nodes[0] &&\n isNodeInRegion(region, nodes[0].parentNode) &&\n nodes.some(node => getTagOfNode(node) == 'LI')\n ) {\n nodes = [splitBalancedNodeRange(nodes)];\n }\n\n wrapFunction(nodes);\n }\n },\n beforeRunCallback\n );\n}\n","export * from './plugins/ImageEdit/index';\n","import checkEditInfoState, { ImageEditInfoState } from './checkEditInfoState';\nimport deleteEditInfo from './deleteEditInfo';\nimport generateDataURL from './generateDataURL';\nimport getEditInfoFromImage from './getEditInfoFromImage';\nimport getGeneratedImageSize from './getGeneratedImageSize';\nimport ImageEditInfo from '../types/ImageEditInfo';\nimport saveEditInfo from './saveEditInfo';\nimport { IEditor, PluginEventType } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Apply changes from the edit info of an image, write result to the image\n * @param editor The editor object that contains the image\n * @param image The image to apply the change\n * @param editInfo Edit info that contains the changed information of the image\n * @param previousSrc Last src value of the image before the change was made\n * @returns True if the image is changed, otherwise false\n */\nexport default function applyChange(\n editor: IEditor,\n image: HTMLImageElement,\n editInfo: ImageEditInfo,\n previousSrc: string\n): boolean {\n let newSrc = '';\n\n const initEditInfo = getEditInfoFromImage(image);\n const state = checkEditInfoState(editInfo, initEditInfo);\n\n switch (state) {\n case ImageEditInfoState.ResizeOnly:\n // For resize only case, no need to generate a new image, just reuse the original one\n newSrc = editInfo.src;\n break;\n case ImageEditInfoState.SameWithLast:\n // For SameWithLast case, image may be resized but the content is still the same with last one,\n // so no need to create a new image, but just reuse last one\n newSrc = previousSrc;\n break;\n case ImageEditInfoState.FullyChanged:\n // For other cases (cropped, rotated, ...) we need to create a new image to reflect the change\n newSrc = generateDataURL(image, editInfo);\n break;\n }\n\n const srcChanged = newSrc != previousSrc;\n\n if (srcChanged) {\n // If the src is changed, fire an EditImage event so that plugins knows that a new image is used, and can\n // replace the new src with some other string and it will be used and set to the image\n const event = editor.triggerPluginEvent(PluginEventType.EditImage, {\n image: image,\n originalSrc: editInfo.src,\n previousSrc,\n newSrc,\n });\n newSrc = event.newSrc;\n }\n\n if (newSrc == editInfo.src) {\n // If newSrc is the same with original one, it means there is only size change, but no rotation, no cropping,\n // so we don't need to keep edit info, we can delete it\n deleteEditInfo(image);\n } else {\n // Otherwise, save the new edit info to the image so that next time when we edit the same image, we know\n // the edit info\n saveEditInfo(image, editInfo);\n }\n\n // Write back the change to image, and set its new size\n const { targetWidth, targetHeight } = getGeneratedImageSize(editInfo);\n image.src = newSrc;\n image.style.width = targetWidth + 'px';\n image.style.height = targetHeight + 'px';\n image.width = targetWidth;\n image.height = targetHeight;\n\n return (\n srcChanged ||\n editInfo.widthPx != initEditInfo.widthPx ||\n editInfo.heightPx != initEditInfo.heightPx\n );\n}\n","import ImageEditInfo, { CropInfo, ResizeInfo, RotateInfo } from '../types/ImageEditInfo';\n\nconst RESIZE_KEYS: (keyof ResizeInfo)[] = ['widthPx', 'heightPx'];\nconst ROTATE_KEYS: (keyof RotateInfo)[] = ['angleRad'];\nconst CROP_KEYS: (keyof CropInfo)[] = [\n 'leftPercent',\n 'rightPercent',\n 'topPercent',\n 'bottomPercent',\n];\nconst ROTATE_CROP_KEYS: (keyof RotateInfo | keyof CropInfo)[] = [...ROTATE_KEYS, ...CROP_KEYS];\nconst ALL_KEYS = [...ROTATE_CROP_KEYS, ...RESIZE_KEYS];\n\n/**\n * State of an edit info object for image editing.\n * It is returned by checkEditInfoState() function\n */\nexport const enum ImageEditInfoState {\n /**\n * Invalid edit info. It means the given edit info object is either null,\n * or not all its member are of correct type\n */\n Invalid,\n\n /**\n * The edit info shows that it is only potentially edited by resizing action.\n * Image is not rotated or cropped, or event not changed at all.\n */\n ResizeOnly,\n\n /**\n * When compare with another edit info, this value can be returned when both current\n * edit info and the other one are not been rotated, and they have same cropping\n * percentages. So that they can share the same image src, only width and height\n * need to be adjusted.\n */\n SameWithLast,\n\n /**\n * When this value is returned, it means the image is edited by either cropping or\n * rotation, or both. Image source can't be reused, need to generate a new image src\n * data uri.\n */\n FullyChanged,\n}\n\n/**\n * @internal\n * Check the state of an edit info\n * @param editInfo The edit info to check\n * @param compareTo An optional edit info to compare to\n * @returns If the source edit info is not valid (wrong type, missing field, ...), returns Invalid.\n * If the source edit info doesn't contain any rotation or cropping, returns ResizeOnly\n * If the compare edit info exists, and both of them don't contain rotation, and the have same cropping values,\n * returns SameWithLast. Otherwise, returns FullyChanged\n */\nexport default function checkEditInfoState(\n editInfo: ImageEditInfo,\n compareTo?: ImageEditInfo\n): ImageEditInfoState {\n if (!editInfo || !editInfo.src || ALL_KEYS.some(key => !isNumber(editInfo[key]))) {\n return ImageEditInfoState.Invalid;\n } else if (ROTATE_CROP_KEYS.every(key => areSameNumber(editInfo[key], 0))) {\n return ImageEditInfoState.ResizeOnly;\n } else if (\n compareTo &&\n ROTATE_KEYS.every(key => areSameNumber(editInfo[key], 0)) &&\n ROTATE_KEYS.every(key => areSameNumber(compareTo[key], 0)) &&\n CROP_KEYS.every(key => areSameNumber(editInfo[key], compareTo[key]))\n ) {\n return ImageEditInfoState.SameWithLast;\n } else {\n return ImageEditInfoState.FullyChanged;\n }\n}\n\nfunction isNumber(o: any): o is number {\n return typeof o === 'number';\n}\n\nfunction areSameNumber(n1: number, n2: number) {\n return Math.abs(n1 - n2) < 1e-3;\n}\n","/**\n * Check if we can regenerate edited image from the source image.\n * An image can't regenerate result when there is CORS issue of the source content.\n * @param img The image element to test\n * @returns True when we can regenerate the edited image, otherwise false\n */\nexport default function canRegenerateImage(img: HTMLImageElement): boolean {\n if (!img) {\n return false;\n }\n\n try {\n const canvas = img.ownerDocument.createElement('canvas');\n canvas.width = 10;\n canvas.height = 10;\n const context = canvas.getContext('2d');\n context.drawImage(img, 0, 0);\n context.getImageData(0, 0, 1, 1);\n return true;\n } catch {\n return false;\n }\n}\n","import DragAndDropContext, { X, Y } from '../types/DragAndDropContext';\nimport DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler';\nimport ImageEditInfo, { ResizeInfo } from '../types/ImageEditInfo';\nimport ImageHtmlOptions from '../types/ImageHtmlOptions';\nimport { CreateElementData } from 'roosterjs-editor-types';\nimport { ImageEditElementClass } from '../types/ImageEditElementClass';\n\nconst RESIZE_HANDLE_SIZE = 7;\nconst RESIZE_HANDLE_MARGIN = 3;\nconst Xs: X[] = ['w', '', 'e'];\nconst Ys: Y[] = ['s', '', 'n'];\n\n/**\n * The resize drag and drop handler\n */\nconst Resizer: DragAndDropHandler = {\n onDragStart: ({ editInfo }) => ({ ...editInfo }),\n onDragging: ({ x, y, editInfo, options }, e, base, deltaX, deltaY) => {\n const ratio =\n base.widthPx > 0 && base.heightPx > 0 ? (base.widthPx * 1.0) / base.heightPx : 0;\n\n [deltaX, deltaY] = rotateCoordinate(deltaX, deltaY, editInfo.angleRad);\n\n const horizontalOnly = x == '';\n const verticalOnly = y == '';\n const shouldPreserveRatio =\n !(horizontalOnly || verticalOnly) && (options.preserveRatio || e.shiftKey);\n let newWidth = horizontalOnly\n ? base.widthPx\n : Math.max(base.widthPx + deltaX * (x == 'w' ? -1 : 1), options.minWidth);\n let newHeight = verticalOnly\n ? base.heightPx\n : Math.max(base.heightPx + deltaY * (y == 'n' ? -1 : 1), options.minHeight);\n\n if (shouldPreserveRatio && ratio > 0) {\n newHeight = Math.min(newHeight, newWidth / ratio);\n newWidth = Math.min(newWidth, newHeight * ratio);\n\n if (newWidth < newHeight * ratio) {\n newWidth = newHeight * ratio;\n } else {\n newHeight = newWidth / ratio;\n }\n }\n\n editInfo.widthPx = newWidth;\n editInfo.heightPx = newHeight;\n\n return true;\n },\n};\n\n/**\n * @internal\n */\nexport default Resizer;\n\n/**\n * @internal Calculate the rotated x and y distance for mouse moving\n * @param x Original x distance\n * @param y Original y distance\n * @param angle Rotated angle, in radian\n * @returns rotated x and y distances\n */\nexport function rotateCoordinate(x: number, y: number, angle: number): [number, number] {\n if (x == 0 && y == 0) {\n return [0, 0];\n }\n const hypotenuse = Math.sqrt(x * x + y * y);\n angle = Math.atan2(y, x) - angle;\n return [hypotenuse * Math.cos(angle), hypotenuse * Math.sin(angle)];\n}\n\n/**\n * @internal\n * Double check if the changed size can satisfy current width of container.\n * When resize an image and preserve ratio, its size can be limited by the size of container.\n * So we need to check the actual size and calculate the size again\n * @param editInfo Edit info of the image\n * @param preserveRatio Whether w/h ratio need to be preserved\n * @param actualWidth Actual width of the image after resize\n * @param actualHeight Actual height of the image after resize\n */\nexport function doubleCheckResize(\n editInfo: ImageEditInfo,\n preserveRatio: boolean,\n actualWidth: number,\n actualHeight: number\n) {\n let { widthPx, heightPx } = editInfo;\n const ratio = heightPx > 0 ? widthPx / heightPx : 0;\n\n actualWidth = Math.floor(actualWidth);\n actualHeight = Math.floor(actualHeight);\n widthPx = Math.floor(widthPx);\n heightPx = Math.floor(heightPx);\n\n editInfo.widthPx = actualWidth;\n editInfo.heightPx = actualHeight;\n\n if (preserveRatio && ratio > 0 && (widthPx !== actualWidth || heightPx !== actualHeight)) {\n if (actualWidth < widthPx) {\n editInfo.heightPx = actualWidth / ratio;\n } else {\n editInfo.widthPx = actualHeight * ratio;\n }\n }\n}\n\n/**\n * @internal\n * Get HTML for resize handles at the corners\n */\nexport function getCornerResizeHTML({\n borderColor: resizeBorderColor,\n}: ImageHtmlOptions): CreateElementData[] {\n const result: CreateElementData[] = [];\n Xs.forEach(x =>\n Ys.forEach(y =>\n result.push(\n (x == '') == (y == '') ? getResizeHandleHTML(x, y, resizeBorderColor) : null\n )\n )\n );\n return result;\n}\n\n/**\n * @internal\n * Get HTML for resize handles on the sides\n */\nexport function getSideResizeHTML({\n borderColor: resizeBorderColor,\n}: ImageHtmlOptions): CreateElementData[] {\n const result: CreateElementData[] = [];\n Xs.forEach(x =>\n Ys.forEach(y =>\n result.push(\n (x == '') != (y == '') ? getResizeHandleHTML(x, y, resizeBorderColor) : null\n )\n )\n );\n return result;\n}\n\nfunction getResizeHandleHTML(x: X, y: Y, borderColor: string): CreateElementData {\n const leftOrRight = x == 'w' ? 'left' : 'right';\n const topOrBottom = y == 'n' ? 'top' : 'bottom';\n const leftOrRightValue = x == '' ? '50%' : '0px';\n const topOrBottomValue = y == '' ? '50%' : '0px';\n const direction = y + x;\n\n return x == '' && y == ''\n ? {\n tag: 'div',\n style: `position:absolute;left:0;right:0;top:0;bottom:0;border:solid 1px ${borderColor};pointer-events:none`,\n }\n : {\n tag: 'div',\n style: `position:absolute;${leftOrRight}:${leftOrRightValue};${topOrBottom}:${topOrBottomValue}`,\n children: [\n {\n tag: 'div',\n style: `position:relative;width:${RESIZE_HANDLE_SIZE}px;height:${RESIZE_HANDLE_SIZE}px;background-color: ${borderColor};cursor:${direction}-resize;${topOrBottom}:-${RESIZE_HANDLE_MARGIN}px;${leftOrRight}:-${RESIZE_HANDLE_MARGIN}px`,\n className: ImageEditElementClass.ResizeHandle,\n dataset: { x, y },\n },\n ],\n };\n}\n","import ImageEditInfo from '../types/ImageEditInfo';\nimport ImageSize from '../types/ImageSize';\n\n/**\n * @internal\n * Get target size of an image with a percentage\n * @param editInfo\n * @param percentage\n * @returns [width, height] array\n */\nexport default function getTargetSizeByPercentage(\n editInfo: ImageEditInfo,\n percentage: number\n): ImageSize {\n const {\n naturalWidth,\n naturalHeight,\n leftPercent: left,\n topPercent: top,\n rightPercent: right,\n bottomPercent: bottom,\n } = editInfo;\n const width = naturalWidth * (1 - left - right) * percentage;\n const height = naturalHeight * (1 - top - bottom) * percentage;\n return { width, height };\n}\n","import getEditInfoFromImage from '../editInfoUtils/getEditInfoFromImage';\nimport getTargetSizeByPercentage from '../editInfoUtils/getTargetSizeByPercentage';\n\n/**\n * Check if the image is already resized to the given percentage\n * @param image The image to check\n * @param percentage The percentage to check\n */\nexport default function isResizedTo(image: HTMLImageElement, percentage: number): boolean {\n const editInfo = getEditInfoFromImage(image);\n const { width, height } = getTargetSizeByPercentage(editInfo, percentage);\n return (\n Math.round(width) == Math.round(editInfo.widthPx) &&\n Math.round(height) == Math.round(editInfo.heightPx)\n );\n}\n","import { NodeType } from 'roosterjs-editor-types';\n\n/** NodeId attribute */\nconst NODE_ID_ATTRIBUTE_NAME = 'NodeId';\n\n/**\n * @internal\n * Custom data for dom elements\n */\nexport default interface WordCustomData {\n /** The dict storing custom data, key is element Id, value is dictionary */\n dict: { [key: string]: { [key: string]: number } };\n\n /** Next node Id to use */\n nextNodeId: number;\n}\n\n/**\n * @internal\n * Create an empty WordCustomData\n */\nexport function createCustomData(): WordCustomData {\n return {\n dict: {},\n nextNodeId: 1,\n };\n}\n\n/**\n * @internal\n * Sets the specified object data\n */\nexport function setObject(wordCustomData: WordCustomData, element: Node, key: string, value: any) {\n // Get the id for the element\n if (element.nodeType == NodeType.Element) {\n let id = getAndSetNodeId(wordCustomData, element as HTMLElement);\n if (id != '') {\n // Get the values for the element\n if (!wordCustomData.dict[id]) {\n // First time dictionary creation\n wordCustomData.dict[id] = {};\n }\n wordCustomData.dict[id][key] = value;\n }\n }\n}\n\n/**\n * @internal\n * Reads the specified object data\n */\nexport function getObject(wordCustomData: WordCustomData, element: Node, key: string): any {\n if (element.nodeType == NodeType.Element) {\n let id = getAndSetNodeId(wordCustomData, element as HTMLElement);\n if (id != '') {\n return wordCustomData.dict[id] && wordCustomData.dict[id][key];\n }\n }\n\n return null;\n}\n\n/**\n * Get the unique id for the specified node...\n */\nfunction getAndSetNodeId(wordCustomData: WordCustomData, element: HTMLElement): string {\n let id = element.getAttribute(NODE_ID_ATTRIBUTE_NAME);\n if (!id) {\n id = wordCustomData.nextNodeId.toString();\n wordCustomData.nextNodeId++;\n element.setAttribute(NODE_ID_ATTRIBUTE_NAME, id);\n }\n return id;\n}\n","import ListMetadata from './ListMetadata';\n\n/**\n * @internal\n * Holds the ids for the lists already seen for a specified level\n */\nexport default interface LevelLists {\n /**\n * The metadata for the lists seen at this level\n * key: word list id, value: list metadata\n */\n listsMetadata: { [key: string]: ListMetadata };\n\n /** Unique id of the list currently at this level */\n currentUniqueListId: number;\n}\n\n/**\n * @internal\n * create an empty LevelLists\n */\nexport function createLevelLists(): LevelLists {\n return {\n listsMetadata: {},\n currentUniqueListId: -1,\n };\n}\n","/**\n * @internal\n */\nexport const WORD_ORDERED_LIST_SELECTOR = 'div.ListContainerWrapper > ul[class^=\"BulletListStyle\"]';\n\n/**\n * @internal\n */\nexport const WORD_UNORDERED_LIST_SELECTOR =\n 'div.ListContainerWrapper > ol[class^=\"NumberListStyle\"]';\n\n/**\n * @internal\n */\nexport const WORD_ONLINE_IDENTIFYING_SELECTOR = `${WORD_ORDERED_LIST_SELECTOR},${WORD_UNORDERED_LIST_SELECTOR}`;\n\n/**\n * @internal\n */\nexport const LIST_CONTAINER_ELEMENT_CLASS_NAME = 'ListContainerWrapper';\n\n/**\n * @internal\n */\nexport const UNORDERED_LIST_TAG_NAME = 'UL';\n\n/**\n * @internal\n */\nexport const ORDERED_LIST_TAG_NAME = 'OL';\n\nconst TEXT_CONTAINER_ELEMENT_CLASS_NAME = 'OutlineElement';\n\n/**\n * @internal\n */\nexport const WAC_IDENTIFY_SELECTOR = `ul[class^=\"BulletListStyle\"]>.${TEXT_CONTAINER_ELEMENT_CLASS_NAME},ol[class^=\"NumberListStyle\"]>.${TEXT_CONTAINER_ELEMENT_CLASS_NAME}`;\n","export { default as createEditor } from './createEditor';\r\nexport * from 'roosterjs-editor-types';\r\nexport * from 'roosterjs-editor-dom';\r\nexport * from 'roosterjs-editor-core';\r\nexport * from 'roosterjs-editor-api';\r\nexport * from 'roosterjs-editor-plugins';\r\nexport * from 'roosterjs-color-utils';\r\n","import { Editor } from 'roosterjs-editor-core';\r\nimport { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types';\r\nimport { getDarkColor } from 'roosterjs-color-utils';\r\nimport { ContentEdit, HyperLink, Paste } from 'roosterjs-editor-plugins';\r\n\r\n/**\r\n * Create an editor instance with most common options\r\n * @param contentDiv The html div element needed for creating the editor\r\n * @param additionalPlugins The additional user defined plugins. Currently the default plugins that are already included are\r\n * ContentEdit, HyperLink and Paste, user don't need to add those.\r\n * @param initialContent The initial content to show in editor. It can't be removed by undo, user need to manually remove it if needed.\r\n * @returns The editor instance\r\n */\r\nexport default function createEditor(\r\n contentDiv: HTMLDivElement,\r\n additionalPlugins?: EditorPlugin[],\r\n initialContent?: string\r\n): IEditor {\r\n let plugins: EditorPlugin[] = [new HyperLink(), new Paste(), new ContentEdit()];\r\n\r\n if (additionalPlugins) {\r\n plugins = plugins.concat(additionalPlugins);\r\n }\r\n\r\n let options: EditorOptions = {\r\n plugins: plugins,\r\n initialContent: initialContent,\r\n getDarkColor: getDarkColor,\r\n defaultFormat: {\r\n fontFamily: 'Calibri,Arial,Helvetica,sans-serif',\r\n fontSize: '11pt',\r\n textColor: '#000000',\r\n },\r\n };\r\n return new Editor(contentDiv, options);\r\n}\r\n","import { coreApiMap } from '../coreApi/coreApiMap';\r\nimport {\r\n BlockElement,\r\n ChangeSource,\r\n ClipboardData,\r\n ColorTransformDirection,\r\n ContentPosition,\r\n CorePlugins,\r\n DefaultFormat,\r\n DOMEventHandler,\r\n EditorCore,\r\n EditorOptions,\r\n EditorPlugin,\r\n EditorUndoState,\r\n ExperimentalFeatures,\r\n GenericContentEditFeature,\r\n GetContentMode,\r\n IContentTraverser,\r\n IEditor,\r\n InsertOption,\r\n IPositionContentSearcher,\r\n NodePosition,\r\n PendableFormatState,\r\n PluginEvent,\r\n PluginEventData,\r\n PluginEventFromType,\r\n PluginEventType,\r\n PositionType,\r\n QueryScope,\r\n Region,\r\n RegionType,\r\n SelectionPath,\r\n StyleBasedFormatState,\r\n TrustedHTMLHandler,\r\n} from 'roosterjs-editor-types';\r\nimport createCorePlugins, {\r\n getPluginState,\r\n PLACEHOLDER_PLUGIN_NAME,\r\n} from '../corePlugins/createCorePlugins';\r\nimport {\r\n cacheGetEventData,\r\n collapseNodes,\r\n contains,\r\n ContentTraverser,\r\n createRange,\r\n deleteSelectedContent,\r\n getRegionsFromRange,\r\n findClosestElementAncestor,\r\n getBlockElementAtNode,\r\n getSelectionPath,\r\n getTagOfNode,\r\n isNodeEmpty,\r\n safeInstanceOf,\r\n Position,\r\n PositionContentSearcher,\r\n queryElements,\r\n wrap,\r\n isPositionAtBeginningOf,\r\n arrayPush,\r\n toArray,\r\n} from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * RoosterJs core editor class\r\n */\r\nexport default class Editor implements IEditor {\r\n private core: EditorCore;\r\n\r\n //#region Lifecycle\r\n\r\n /**\r\n * Creates an instance of Editor\r\n * @param contentDiv The DIV HTML element which will be the container element of editor\r\n * @param options An optional options object to customize the editor\r\n */\r\n constructor(contentDiv: HTMLDivElement, options: EditorOptions = {}) {\r\n // 1. Make sure all parameters are valid\r\n if (getTagOfNode(contentDiv) != 'DIV') {\r\n throw new Error('contentDiv must be an HTML DIV element');\r\n }\r\n\r\n // 2. Store options values to local variables\r\n const corePlugins = createCorePlugins(contentDiv, options);\r\n const plugins: EditorPlugin[] = [];\r\n Object.keys(corePlugins).forEach(\r\n (name: typeof PLACEHOLDER_PLUGIN_NAME | keyof CorePlugins) => {\r\n if (name == PLACEHOLDER_PLUGIN_NAME) {\r\n arrayPush(plugins, options.plugins);\r\n } else {\r\n plugins.push(corePlugins[name]);\r\n }\r\n }\r\n );\r\n this.core = {\r\n contentDiv,\r\n api: {\r\n ...coreApiMap,\r\n ...(options.coreApiOverride || {}),\r\n },\r\n plugins: plugins.filter(x => !!x),\r\n ...getPluginState(corePlugins),\r\n trustedHTMLHandler: options.trustedHTMLHandler || ((html: string) => html),\r\n };\r\n\r\n // 3. Initialize plugins\r\n this.core.plugins.forEach(plugin => plugin.initialize(this));\r\n\r\n // 4. Ensure user will type in a container node, not the editor content DIV\r\n this.ensureTypeInContainer(\r\n new Position(this.core.contentDiv, PositionType.Begin).normalize()\r\n );\r\n }\r\n\r\n /**\r\n * Dispose this editor, dispose all plugins and custom data\r\n */\r\n public dispose(): void {\r\n this.core.plugins.reverse().forEach(plugin => plugin.dispose());\r\n this.core = null;\r\n }\r\n\r\n /**\r\n * Get whether this editor is disposed\r\n * @returns True if editor is disposed, otherwise false\r\n */\r\n public isDisposed(): boolean {\r\n return !this.core;\r\n }\r\n\r\n //#endregion\r\n\r\n //#region Node API\r\n\r\n /**\r\n * Insert node into editor\r\n * @param node The node to insert\r\n * @param option Insert options. Default value is:\r\n * position: ContentPosition.SelectionStart\r\n * updateCursor: true\r\n * replaceSelection: true\r\n * insertOnNewLine: false\r\n * @returns true if node is inserted. Otherwise false\r\n */\r\n public insertNode(node: Node, option?: InsertOption): boolean {\r\n return node ? this.core.api.insertNode(this.core, node, option) : false;\r\n }\r\n\r\n /**\r\n * Delete a node from editor content\r\n * @param node The node to delete\r\n * @returns true if node is deleted. Otherwise false\r\n */\r\n public deleteNode(node: Node): boolean {\r\n // Only remove the node when it falls within editor\r\n if (node && this.contains(node)) {\r\n node.parentNode.removeChild(node);\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * Replace a node in editor content with another node\r\n * @param existingNode The existing node to be replaced\r\n * @param toNode node to replace to\r\n * @param transformColorForDarkMode (optional) Whether to transform new node to dark mode. Default is false\r\n * @returns true if node is replaced. Otherwise false\r\n */\r\n public replaceNode(\r\n existingNode: Node,\r\n toNode: Node,\r\n transformColorForDarkMode?: boolean\r\n ): boolean {\r\n // Only replace the node when it falls within editor\r\n if (this.contains(existingNode) && toNode) {\r\n this.core.api.transformColor(\r\n this.core,\r\n transformColorForDarkMode ? toNode : null,\r\n true /*includeSelf*/,\r\n () => existingNode.parentNode.replaceChild(toNode, existingNode),\r\n ColorTransformDirection.LightToDark\r\n );\r\n\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * Get BlockElement at given node\r\n * @param node The node to create InlineElement\r\n * @returns The BlockElement result\r\n */\r\n public getBlockElementAtNode(node: Node): BlockElement {\r\n return getBlockElementAtNode(this.core.contentDiv, node);\r\n }\r\n\r\n public contains(arg: Node | Range): boolean {\r\n return contains(this.core.contentDiv, arg);\r\n }\r\n\r\n public queryElements(\r\n selector: string,\r\n scopeOrCallback: QueryScope | ((node: Node) => any) = QueryScope.Body,\r\n callback?: (node: Node) => any\r\n ) {\r\n let scope = scopeOrCallback instanceof Function ? QueryScope.Body : scopeOrCallback;\r\n callback = scopeOrCallback instanceof Function ? scopeOrCallback : callback;\r\n\r\n let range = scope == QueryScope.Body ? null : this.getSelectionRange();\r\n return queryElements(this.core.contentDiv, selector, callback, scope, range);\r\n }\r\n\r\n /**\r\n * Collapse nodes within the given start and end nodes to their common ancestor node,\r\n * split parent nodes if necessary\r\n * @param start The start node\r\n * @param end The end node\r\n * @param canSplitParent True to allow split parent node there are nodes before start or after end under the same parent\r\n * and the returned nodes will be all nodes from start through end after splitting\r\n * False to disallow split parent\r\n * @returns When canSplitParent is true, returns all node from start through end after splitting,\r\n * otherwise just return start and end\r\n */\r\n public collapseNodes(start: Node, end: Node, canSplitParent: boolean): Node[] {\r\n return collapseNodes(this.core.contentDiv, start, end, canSplitParent);\r\n }\r\n\r\n //#endregion\r\n\r\n //#region Content API\r\n\r\n /**\r\n * Check whether the editor contains any visible content\r\n * @param trim Whether trim the content string before check. Default is false\r\n * @returns True if there's no visible content, otherwise false\r\n */\r\n public isEmpty(trim?: boolean): boolean {\r\n return isNodeEmpty(this.core.contentDiv, trim);\r\n }\r\n\r\n /**\r\n * Get current editor content as HTML string\r\n * @param mode specify what kind of HTML content to retrieve\r\n * @returns HTML string representing current editor content\r\n */\r\n public getContent(mode: GetContentMode = GetContentMode.CleanHTML): string {\r\n return this.core.api.getContent(this.core, mode);\r\n }\r\n\r\n /**\r\n * Set HTML content to this editor. All existing content will be replaced. A ContentChanged event will be triggered\r\n * @param content HTML content to set in\r\n * @param triggerContentChangedEvent True to trigger a ContentChanged event. Default value is true\r\n */\r\n public setContent(content: string, triggerContentChangedEvent: boolean = true) {\r\n this.core.api.setContent(this.core, content, triggerContentChangedEvent);\r\n }\r\n\r\n /**\r\n * Insert HTML content into editor\r\n * @param HTML content to insert\r\n * @param option Insert options. Default value is:\r\n * position: ContentPosition.SelectionStart\r\n * updateCursor: true\r\n * replaceSelection: true\r\n * insertOnNewLine: false\r\n */\r\n public insertContent(content: string, option?: InsertOption) {\r\n if (content) {\r\n const doc = this.getDocument();\r\n const body = new DOMParser().parseFromString(\r\n this.core.trustedHTMLHandler(content),\r\n 'text/html'\r\n )?.body;\r\n let allNodes = body?.childNodes ? toArray(body.childNodes) : [];\r\n\r\n // If it is to insert on new line, and there are more than one node in the collection, wrap all nodes with\r\n // a parent DIV before calling insertNode on each top level sub node. Otherwise, every sub node may get wrapped\r\n // separately to show up on its own line\r\n if (option && option.insertOnNewLine && allNodes.length > 1) {\r\n allNodes = [wrap(allNodes)];\r\n }\r\n\r\n let fragment = doc.createDocumentFragment();\r\n allNodes.forEach(node => fragment.appendChild(node));\r\n\r\n this.insertNode(fragment, option);\r\n }\r\n }\r\n\r\n /**\r\n * Delete selected content\r\n */\r\n public deleteSelectedContent(): NodePosition {\r\n const range = this.getSelectionRange();\r\n return range && !range.collapsed && deleteSelectedContent(this.core.contentDiv, range);\r\n }\r\n\r\n /**\r\n * Paste into editor using a clipboardData object\r\n * @param clipboardData Clipboard data retrieved from clipboard\r\n * @param pasteAsText Force pasting as plain text. Default value is false\r\n * @param applyCurrentStyle True if apply format of current selection to the pasted content,\r\n * false to keep original format. Default value is false. When pasteAsText is true, this parameter is ignored\r\n */\r\n public paste(\r\n clipboardData: ClipboardData,\r\n pasteAsText?: boolean,\r\n applyCurrentFormat?: boolean\r\n ) {\r\n if (!clipboardData) {\r\n return;\r\n }\r\n\r\n if (clipboardData.snapshotBeforePaste) {\r\n // Restore original content before paste a new one\r\n this.setContent(clipboardData.snapshotBeforePaste);\r\n } else {\r\n clipboardData.snapshotBeforePaste = this.getContent(\r\n GetContentMode.RawHTMLWithSelection\r\n );\r\n }\r\n\r\n const range = this.getSelectionRange();\r\n const pos = range && Position.getStart(range);\r\n const fragment = this.core.api.createPasteFragment(\r\n this.core,\r\n clipboardData,\r\n pos,\r\n pasteAsText,\r\n applyCurrentFormat\r\n );\r\n\r\n this.addUndoSnapshot(() => {\r\n this.insertNode(fragment);\r\n return clipboardData;\r\n }, ChangeSource.Paste);\r\n }\r\n\r\n //#endregion\r\n\r\n //#region Focus and Selection\r\n\r\n /**\r\n * Get current selection range from Editor.\r\n * It does a live pull on the selection, if nothing retrieved, return whatever we have in cache.\r\n * @param tryGetFromCache Set to true to retrieve the selection range from cache if editor doesn't own the focus now.\r\n * Default value is true\r\n * @returns current selection range, or null if editor never got focus before\r\n */\r\n public getSelectionRange(tryGetFromCache: boolean = true): Range {\r\n return this.core.api.getSelectionRange(this.core, tryGetFromCache);\r\n }\r\n\r\n /**\r\n * Get current selection in a serializable format\r\n * It does a live pull on the selection, if nothing retrieved, return whatever we have in cache.\r\n * @returns current selection path, or null if editor never got focus before\r\n */\r\n public getSelectionPath(): SelectionPath {\r\n const range = this.getSelectionRange();\r\n return range && getSelectionPath(this.core.contentDiv, range);\r\n }\r\n\r\n /**\r\n * Check if focus is in editor now\r\n * @returns true if focus is in editor, otherwise false\r\n */\r\n public hasFocus(): boolean {\r\n return this.core.api.hasFocus(this.core);\r\n }\r\n\r\n /**\r\n * Focus to this editor, the selection was restored to where it was before, no unexpected scroll.\r\n */\r\n public focus() {\r\n this.core.api.focus(this.core);\r\n }\r\n\r\n public select(arg1: any, arg2?: any, arg3?: any, arg4?: any): boolean {\r\n let range = !arg1\r\n ? null\r\n : safeInstanceOf(arg1, 'Range')\r\n ? arg1\r\n : Array.isArray(arg1.start) && Array.isArray(arg1.end)\r\n ? createRange(\r\n this.core.contentDiv,\r\n (arg1).start,\r\n (arg1).end\r\n )\r\n : createRange(arg1, arg2, arg3, arg4);\r\n return this.contains(range) && this.core.api.selectRange(this.core, range);\r\n }\r\n\r\n /**\r\n * Get current focused position. Return null if editor doesn't have focus at this time.\r\n */\r\n public getFocusedPosition(): NodePosition {\r\n let sel = this.getDocument().defaultView?.getSelection();\r\n if (this.contains(sel && sel.focusNode)) {\r\n return new Position(sel.focusNode, sel.focusOffset);\r\n }\r\n\r\n let range = this.getSelectionRange();\r\n if (range) {\r\n return Position.getStart(range);\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Get an HTML element from current cursor position.\r\n * When expectedTags is not specified, return value is the current node (if it is HTML element)\r\n * or its parent node (if current node is a Text node).\r\n * When expectedTags is specified, return value is the first ancestor of current node which has\r\n * one of the expected tags.\r\n * If no element found within editor by the given tag, return null.\r\n * @param selector Optional, an HTML selector to find HTML element with.\r\n * @param startFrom Start search from this node. If not specified, start from current focused position\r\n * @param event Optional, if specified, editor will try to get cached result from the event object first.\r\n * If it is not cached before, query from DOM and cache the result into the event object\r\n */\r\n public getElementAtCursor(\r\n selector?: string,\r\n startFrom?: Node,\r\n event?: PluginEvent\r\n ): HTMLElement {\r\n event = startFrom ? null : event; // Only use cache when startFrom is not specified, for different start position can have different result\r\n\r\n return cacheGetEventData(event, 'GET_ELEMENT_AT_CURSOR_' + selector, () => {\r\n if (!startFrom) {\r\n let position = this.getFocusedPosition();\r\n startFrom = position && position.node;\r\n }\r\n return (\r\n startFrom && findClosestElementAncestor(startFrom, this.core.contentDiv, selector)\r\n );\r\n });\r\n }\r\n\r\n /**\r\n * Check if this position is at beginning of the editor.\r\n * This will return true if all nodes between the beginning of target node and the position are empty.\r\n * @param position The position to check\r\n * @returns True if position is at beginning of the editor, otherwise false\r\n */\r\n public isPositionAtBeginning(position: NodePosition): boolean {\r\n return isPositionAtBeginningOf(position, this.core.contentDiv);\r\n }\r\n\r\n /**\r\n * Get impacted regions from selection\r\n */\r\n public getSelectedRegions(type: RegionType = RegionType.Table): Region[] {\r\n const range = this.getSelectionRange();\r\n return range ? getRegionsFromRange(this.core.contentDiv, range, type) : [];\r\n }\r\n\r\n //#endregion\r\n\r\n //#region EVENT API\r\n\r\n public addDomEventHandler(\r\n nameOrMap: string | Record,\r\n handler?: DOMEventHandler\r\n ): () => void {\r\n const eventsToMap = typeof nameOrMap == 'string' ? { [nameOrMap]: handler } : nameOrMap;\r\n return this.core.api.attachDomEvent(this.core, eventsToMap);\r\n }\r\n\r\n /**\r\n * Trigger an event to be dispatched to all plugins\r\n * @param eventType Type of the event\r\n * @param data data of the event with given type, this is the rest part of PluginEvent with the given type\r\n * @param broadcast indicates if the event needs to be dispatched to all plugins\r\n * True means to all, false means to allow exclusive handling from one plugin unless no one wants that\r\n * @returns the event object which is really passed into plugins. Some plugin may modify the event object so\r\n * the result of this function provides a chance to read the modified result\r\n */\r\n public triggerPluginEvent(\r\n eventType: T,\r\n data: PluginEventData,\r\n broadcast?: boolean\r\n ): PluginEventFromType {\r\n let event = ({\r\n eventType,\r\n ...data,\r\n } as any) as PluginEventFromType;\r\n this.core.api.triggerEvent(this.core, event, broadcast);\r\n\r\n return event;\r\n }\r\n\r\n /**\r\n * Trigger a ContentChangedEvent\r\n * @param source Source of this event, by default is 'SetContent'\r\n * @param data additional data for this event\r\n */\r\n public triggerContentChangedEvent(\r\n source: ChangeSource | string = ChangeSource.SetContent,\r\n data?: any\r\n ) {\r\n this.triggerPluginEvent(PluginEventType.ContentChanged, {\r\n source,\r\n data,\r\n });\r\n }\r\n\r\n //#endregion\r\n\r\n //#region Undo API\r\n\r\n /**\r\n * Undo last edit operation\r\n */\r\n public undo() {\r\n this.focus();\r\n this.core.api.restoreUndoSnapshot(this.core, -1 /*step*/);\r\n }\r\n\r\n /**\r\n * Redo next edit operation\r\n */\r\n public redo() {\r\n this.focus();\r\n this.core.api.restoreUndoSnapshot(this.core, 1 /*step*/);\r\n }\r\n\r\n /**\r\n * Add undo snapshot, and execute a format callback function, then add another undo snapshot, then trigger\r\n * ContentChangedEvent with given change source.\r\n * If this function is called nested, undo snapshot will only be added in the outside one\r\n * @param callback The callback function to perform formatting, returns a data object which will be used as\r\n * the data field in ContentChangedEvent if changeSource is not null.\r\n * @param changeSource The change source to use when fire ContentChangedEvent. When the value is not null,\r\n * a ContentChangedEvent will be fired with change source equal to this value\r\n * @param canUndoByBackspace True if this action can be undone when user press Backspace key (aka Auto Complete).\r\n */\r\n public addUndoSnapshot(\r\n callback?: (start: NodePosition, end: NodePosition) => any,\r\n changeSource?: ChangeSource | string,\r\n canUndoByBackspace?: boolean\r\n ) {\r\n this.core.api.addUndoSnapshot(this.core, callback, changeSource, canUndoByBackspace);\r\n }\r\n\r\n /**\r\n * Whether there is an available undo/redo snapshot\r\n */\r\n public getUndoState(): EditorUndoState {\r\n const { hasNewContent, snapshotsService } = this.core.undo;\r\n return {\r\n canUndo: hasNewContent || snapshotsService.canMove(-1 /*previousSnapshot*/),\r\n canRedo: snapshotsService.canMove(1 /*nextSnapshot*/),\r\n };\r\n }\r\n\r\n //#endregion\r\n\r\n //#region Misc\r\n\r\n /**\r\n * Get document which contains this editor\r\n * @returns The HTML document which contains this editor\r\n */\r\n public getDocument(): Document {\r\n return this.core.contentDiv.ownerDocument;\r\n }\r\n\r\n /**\r\n * Get the scroll container of the editor\r\n */\r\n public getScrollContainer(): HTMLElement {\r\n return this.core.domEvent.scrollContainer;\r\n }\r\n\r\n /**\r\n * Get custom data related to this editor\r\n * @param key Key of the custom data\r\n * @param getter Getter function. If custom data for the given key doesn't exist,\r\n * call this function to get one and store it if it is specified. Otherwise return undefined\r\n * @param disposer An optional disposer function to dispose this custom data when\r\n * dispose editor.\r\n */\r\n public getCustomData(key: string, getter?: () => T, disposer?: (value: T) => void): T {\r\n return (this.core.lifecycle.customData[key] = this.core.lifecycle.customData[key] || {\r\n value: getter ? getter() : undefined,\r\n disposer,\r\n }).value as T;\r\n }\r\n\r\n /**\r\n * Check if editor is in IME input sequence\r\n * @returns True if editor is in IME input sequence, otherwise false\r\n */\r\n public isInIME(): boolean {\r\n return this.core.domEvent.isInIME;\r\n }\r\n\r\n /**\r\n * Get default format of this editor\r\n * @returns Default format object of this editor\r\n */\r\n public getDefaultFormat(): DefaultFormat {\r\n return this.core.lifecycle.defaultFormat;\r\n }\r\n\r\n /**\r\n * Get a content traverser for the whole editor\r\n * @param startNode The node to start from. If not passed, it will start from the beginning of the body\r\n */\r\n public getBodyTraverser(startNode?: Node): IContentTraverser {\r\n return ContentTraverser.createBodyTraverser(this.core.contentDiv, startNode);\r\n }\r\n\r\n /**\r\n * Get a content traverser for current selection\r\n */\r\n public getSelectionTraverser(): IContentTraverser {\r\n let range = this.getSelectionRange();\r\n return (\r\n range &&\r\n ContentTraverser.createSelectionTraverser(\r\n this.core.contentDiv,\r\n this.getSelectionRange()\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * Get a content traverser for current block element start from specified position\r\n * @param startFrom Start position of the traverser. Default value is ContentPosition.SelectionStart\r\n */\r\n public getBlockTraverser(\r\n startFrom: ContentPosition = ContentPosition.SelectionStart\r\n ): IContentTraverser {\r\n let range = this.getSelectionRange();\r\n return (\r\n range && ContentTraverser.createBlockTraverser(this.core.contentDiv, range, startFrom)\r\n );\r\n }\r\n\r\n /**\r\n * Get a text traverser of current selection\r\n * @param event Optional, if specified, editor will try to get cached result from the event object first.\r\n * If it is not cached before, query from DOM and cache the result into the event object\r\n */\r\n public getContentSearcherOfCursor(event?: PluginEvent): IPositionContentSearcher {\r\n return cacheGetEventData(event, 'CONTENTSEARCHER', () => {\r\n let range = this.getSelectionRange();\r\n return (\r\n range && new PositionContentSearcher(this.core.contentDiv, Position.getStart(range))\r\n );\r\n });\r\n }\r\n\r\n /**\r\n * Run a callback function asynchronously\r\n * @param callback The callback function to run\r\n */\r\n public runAsync(callback: (editor: IEditor) => void) {\r\n let win = this.core.contentDiv.ownerDocument.defaultView || window;\r\n win.requestAnimationFrame(() => {\r\n if (!this.isDisposed() && callback) {\r\n callback(this);\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Set DOM attribute of editor content DIV\r\n * @param name Name of the attribute\r\n * @param value Value of the attribute\r\n */\r\n public setEditorDomAttribute(name: string, value: string) {\r\n if (value === null) {\r\n this.core.contentDiv.removeAttribute(name);\r\n } else {\r\n this.core.contentDiv.setAttribute(name, value);\r\n }\r\n }\r\n\r\n /**\r\n * get DOM attribute of editor content DIV\r\n * @param name Name of the attribute\r\n */\r\n public getEditorDomAttribute(name: string): string {\r\n return this.core.contentDiv.getAttribute(name);\r\n }\r\n\r\n /**\r\n * Get current relative distance from top-left corner of the given element to top-left corner of editor content DIV.\r\n * @param element The element to calculate from. If the given element is not in editor, return value will be null\r\n * @param addScroll When pass true, The return value will also add scrollLeft and scrollTop if any. So the value\r\n * may be different than what user is seeing from the view. When pass false, scroll position will be ignored.\r\n * @returns An [x, y] array which contains the left and top distances, or null if the given element is not in editor.\r\n */\r\n getRelativeDistanceToEditor(element: HTMLElement, addScroll?: boolean): number[] {\r\n if (this.contains(element)) {\r\n const contentDiv = this.core.contentDiv;\r\n const editorRect = contentDiv.getBoundingClientRect();\r\n const elementRect = element.getBoundingClientRect();\r\n\r\n if (editorRect && elementRect) {\r\n let x = elementRect.left - editorRect?.left;\r\n let y = elementRect.top - editorRect?.top;\r\n\r\n if (addScroll) {\r\n x += contentDiv.scrollLeft;\r\n y += contentDiv.scrollTop;\r\n }\r\n\r\n return [x, y];\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Add a Content Edit feature.\r\n * @param feature The feature to add\r\n */\r\n public addContentEditFeature(feature: GenericContentEditFeature) {\r\n feature?.keys.forEach(key => {\r\n let array = this.core.edit.features[key] || [];\r\n array.push(feature);\r\n this.core.edit.features[key] = array;\r\n });\r\n }\r\n\r\n /**\r\n * Get style based format state from current selection, including font name/size and colors\r\n */\r\n public getStyleBasedFormatState(node?: Node): StyleBasedFormatState {\r\n if (!node) {\r\n const range = this.getSelectionRange();\r\n node = range && Position.getStart(range).normalize().node;\r\n }\r\n return this.core.api.getStyleBasedFormatState(this.core, node);\r\n }\r\n\r\n /**\r\n * Get the pendable format such as underline and bold\r\n * @param forceGetStateFromDOM If set to true, will force get the format state from DOM tree.\r\n * @returns The pending format state\r\n */\r\n public getPendableFormatState(forceGetStateFromDOM?: boolean): PendableFormatState {\r\n return this.core.api.getPendableFormatState(this.core, forceGetStateFromDOM);\r\n }\r\n\r\n /**\r\n * Ensure user will type into a container element rather than into the editor content DIV directly\r\n * @param position The position that user is about to type to\r\n * @param keyboardEvent Optional keyboard event object\r\n */\r\n public ensureTypeInContainer(position: NodePosition, keyboardEvent?: KeyboardEvent) {\r\n this.core.api.ensureTypeInContainer(this.core, position, keyboardEvent);\r\n }\r\n\r\n //#endregion\r\n\r\n //#region Dark mode APIs\r\n\r\n /**\r\n * Set the dark mode state and transforms the content to match the new state.\r\n * @param nextDarkMode The next status of dark mode. True if the editor should be in dark mode, false if not.\r\n */\r\n public setDarkModeState(nextDarkMode?: boolean) {\r\n if (this.isDarkMode() == nextDarkMode) {\r\n return;\r\n }\r\n\r\n const currentContent = this.getContent(GetContentMode.CleanHTML);\r\n\r\n this.triggerContentChangedEvent(\r\n nextDarkMode ? ChangeSource.SwitchToDarkMode : ChangeSource.SwitchToLightMode\r\n );\r\n this.setContent(currentContent);\r\n }\r\n\r\n /**\r\n * Check if the editor is in dark mode\r\n * @returns True if the editor is in dark mode, otherwise false\r\n */\r\n public isDarkMode(): boolean {\r\n return this.core.lifecycle.isDarkMode;\r\n }\r\n\r\n /**\r\n * Make the editor in \"Shadow Edit\" mode.\r\n * In Shadow Edit mode, all format change will finally be ignored.\r\n * This can be used for building a live preview feature for format button, to allow user\r\n * see format result without really apply it.\r\n * This function can be called repeated. If editor is already in shadow edit mode, we can still\r\n * use this function to do more shadow edit operation.\r\n */\r\n public startShadowEdit() {\r\n this.core.api.switchShadowEdit(this.core, true /*isOn*/);\r\n }\r\n\r\n /**\r\n * Leave \"Shadow Edit\" mode, all changes made during shadow edit will be discarded\r\n */\r\n public stopShadowEdit() {\r\n this.core.api.switchShadowEdit(this.core, false /*isOn*/);\r\n }\r\n\r\n /**\r\n * Check if editor is in Shadow Edit mode\r\n */\r\n public isInShadowEdit() {\r\n return !!this.core.lifecycle.shadowEditFragment;\r\n }\r\n\r\n /**\r\n * Check if the given experimental feature is enabled\r\n * @param feature The feature to check\r\n */\r\n public isFeatureEnabled(feature: ExperimentalFeatures): boolean {\r\n return this.core.lifecycle.experimentalFeatures.indexOf(feature) >= 0;\r\n }\r\n\r\n /**\r\n * Get a function to convert HTML string to trusted HTML string.\r\n * By default it will just return the input HTML directly. To override this behavior,\r\n * pass your own trusted HTML handler to EditorOptions.trustedHTMLHandler\r\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types\r\n */\r\n getTrustedHTMLHandler(): TrustedHTMLHandler {\r\n return this.core.trustedHTMLHandler;\r\n }\r\n\r\n //#endregion\r\n}\r\n","import { addUndoSnapshot } from './addUndoSnapshot';\nimport { attachDomEvent } from './attachDomEvent';\nimport { CoreApiMap } from 'roosterjs-editor-types';\nimport { createPasteFragment } from './createPasteFragment';\nimport { ensureTypeInContainer } from './ensureTypeInContainer';\nimport { focus } from './focus';\nimport { getContent } from './getContent';\nimport { getPendableFormatState } from './getPendableFormatState';\nimport { getSelectionRange } from './getSelectionRange';\nimport { getStyleBasedFormatState } from './getStyleBasedFormatState';\nimport { hasFocus } from './hasFocus';\nimport { insertNode } from './insertNode';\nimport { restoreUndoSnapshot } from './restoreUndoSnapshot';\nimport { selectRange } from './selectRange';\nimport { setContent } from './setContent';\nimport { switchShadowEdit } from './switchShadowEdit';\nimport { transformColor } from './transformColor';\nimport { triggerEvent } from './triggerEvent';\n\n/**\n * @internal\n */\nexport const coreApiMap: CoreApiMap = {\n attachDomEvent,\n addUndoSnapshot,\n createPasteFragment,\n ensureTypeInContainer,\n focus,\n getContent,\n getSelectionRange,\n getStyleBasedFormatState,\n getPendableFormatState,\n hasFocus,\n insertNode,\n restoreUndoSnapshot,\n selectRange,\n setContent,\n switchShadowEdit,\n transformColor,\n triggerEvent,\n};\n","import { Position } from 'roosterjs-editor-dom';\nimport {\n AddUndoSnapshot,\n ChangeSource,\n ContentChangedEvent,\n EditorCore,\n NodePosition,\n PluginEventType,\n GetContentMode,\n} from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Call an editing callback with adding undo snapshots around, and trigger a ContentChanged event if change source is specified.\n * Undo snapshot will not be added if this call is nested inside another addUndoSnapshot() call.\n * @param core The EditorCore object\n * @param callback The editing callback, accepting current selection start and end position, returns an optional object used as the data field of ContentChangedEvent.\n * @param changeSource The ChangeSource string of ContentChangedEvent. @default ChangeSource.Format. Set to null to avoid triggering ContentChangedEvent\n * @param canUndoByBackspace True if this action can be undone when user press Backspace key (aka Auto Complete).\n */\nexport const addUndoSnapshot: AddUndoSnapshot = (\n core: EditorCore,\n callback: (start: NodePosition, end: NodePosition) => any,\n changeSource: ChangeSource | string,\n canUndoByBackspace: boolean\n) => {\n const undoState = core.undo;\n const isNested = undoState.isNested;\n const isShadowEdit = !!core.lifecycle.shadowEditFragment;\n let data: any;\n\n if (!isNested) {\n undoState.isNested = true;\n\n if (!isShadowEdit) {\n undoState.snapshotsService.addSnapshot(\n core.api.getContent(core, GetContentMode.RawHTMLWithSelection),\n canUndoByBackspace\n );\n undoState.hasNewContent = false;\n }\n }\n\n try {\n if (callback) {\n let range = core.api.getSelectionRange(core, true /*tryGetFromCache*/);\n data = callback(\n range && Position.getStart(range).normalize(),\n range && Position.getEnd(range).normalize()\n );\n\n if (!isNested && !isShadowEdit) {\n undoState.snapshotsService.addSnapshot(\n core.api.getContent(core, GetContentMode.RawHTMLWithSelection),\n false /*isAutoCompleteSnapshot*/\n );\n undoState.hasNewContent = false;\n }\n }\n } finally {\n if (!isNested) {\n undoState.isNested = false;\n }\n }\n\n if (callback && changeSource) {\n let event: ContentChangedEvent = {\n eventType: PluginEventType.ContentChanged,\n source: changeSource,\n data: data,\n };\n core.api.triggerEvent(core, event, true /*broadcast*/);\n }\n\n if (canUndoByBackspace) {\n const range = core.api.getSelectionRange(core, false /*tryGetFromCache*/);\n\n if (range) {\n core.undo.hasNewContent = false;\n core.undo.autoCompletePosition = Position.getStart(range);\n }\n }\n};\n","import collapseNodes from '../utils/collapseNodes';\r\nimport contains from '../utils/contains';\r\nimport createRange from '../selection/createRange';\r\nimport getTagOfNode from '../utils/getTagOfNode';\r\nimport isBlockElement from '../utils/isBlockElement';\r\nimport isNodeAfter from '../utils/isNodeAfter';\r\nimport wrap from '../utils/wrap';\r\nimport { BlockElement } from 'roosterjs-editor-types';\r\nimport { splitBalancedNodeRange } from '../utils/splitParentNode';\r\n\r\nconst STRUCTURE_NODE_TAGS = ['TD', 'TH', 'LI', 'BLOCKQUOTE'];\r\n\r\n/**\r\n * @internal\r\n * This represents a block that is identified by a start and end node\r\n * This is for cases like <root>Hello<BR>World</root>\r\n * in that case, Hello<BR> is a block, World is another block\r\n * Such block cannot be represented by a NodeBlockElement since they don't chained up\r\n * to a single parent node, instead they have a start and end\r\n * This start and end must be in same sibling level and have same parent in DOM tree\r\n */\r\nexport default class StartEndBlockElement implements BlockElement {\r\n constructor(private rootNode: Node, private startNode: Node, private endNode: Node) {}\r\n\r\n static getBlockContext(node: Node): HTMLElement {\r\n while (node && !isBlockElement(node)) {\r\n node = node.parentNode;\r\n }\r\n return node as HTMLElement;\r\n }\r\n\r\n /**\r\n * Collapse this element to a single DOM element.\r\n * If the content nodes are separated in different root nodes, wrap them to a single node\r\n * If the content nodes are included in root node with other nodes, split root node\r\n */\r\n public collapseToSingleElement(): HTMLElement {\r\n let nodes = collapseNodes(\r\n StartEndBlockElement.getBlockContext(this.startNode),\r\n this.startNode,\r\n this.endNode,\r\n true /*canSplitParent*/\r\n );\r\n let blockContext = StartEndBlockElement.getBlockContext(this.startNode);\r\n while (\r\n nodes[0] &&\r\n nodes[0] != blockContext &&\r\n nodes[0].parentNode != this.rootNode &&\r\n STRUCTURE_NODE_TAGS.indexOf(getTagOfNode(nodes[0].parentNode)) < 0\r\n ) {\r\n nodes = [splitBalancedNodeRange(nodes)];\r\n }\r\n return nodes.length == 1 && isBlockElement(nodes[0])\r\n ? (nodes[0] as HTMLElement)\r\n : wrap(nodes);\r\n }\r\n\r\n /**\r\n * Gets the start node\r\n */\r\n public getStartNode(): Node {\r\n return this.startNode;\r\n }\r\n\r\n /**\r\n * Gets the end node\r\n */\r\n public getEndNode(): Node {\r\n return this.endNode;\r\n }\r\n\r\n /**\r\n * Checks equals of two blocks\r\n */\r\n public equals(blockElement: BlockElement): boolean {\r\n return (\r\n this.startNode == blockElement.getStartNode() &&\r\n this.endNode == blockElement.getEndNode()\r\n );\r\n }\r\n\r\n /**\r\n * Checks if another block is after this current\r\n */\r\n public isAfter(blockElement: BlockElement): boolean {\r\n return isNodeAfter(this.getStartNode(), blockElement.getEndNode());\r\n }\r\n\r\n /**\r\n * Checks if an Html node is contained within the block\r\n */\r\n public contains(node: Node): boolean {\r\n return (\r\n contains(this.startNode, node, true /*treatSameNodeAsContain*/) ||\r\n contains(this.endNode, node, true /*treatSameNodeAsContain*/) ||\r\n (isNodeAfter(node, this.startNode) && isNodeAfter(this.endNode, node))\r\n );\r\n }\r\n\r\n /**\r\n * Get the text content of this block element\r\n */\r\n public getTextContent(): string {\r\n const range = createRange(this.getStartNode(), this.getEndNode());\r\n return range ? range.toString() : '';\r\n }\r\n}\r\n","import contains from '../utils/contains';\r\nimport getBlockElementAtNode from '../blockElements/getBlockElementAtNode';\r\nimport getFirstLastBlockElement from '../blockElements/getFirstLastBlockElement';\r\nimport getInlineElementAtNode from '../inlineElements/getInlineElementAtNode';\r\nimport TraversingScoper from './TraversingScoper';\r\nimport { BlockElement, InlineElement } from 'roosterjs-editor-types';\r\nimport { getFirstInlineElement } from '../inlineElements/getFirstLastInlineElement';\r\n\r\n/**\r\n * @internal\r\n * provides a scope object for traversing the entire editor body starting from the beginning\r\n */\r\nexport default class BodyScoper implements TraversingScoper {\r\n private startNode: Node;\r\n\r\n /**\r\n * Construct a new instance of BodyScoper class\r\n * @param rootNode Root node of the body\r\n * @param startNode The node to start from. If not passed, it will start from the beginning of the body\r\n */\r\n constructor(public rootNode: Node, startNode?: Node) {\r\n this.startNode = contains(rootNode, startNode) ? startNode : null;\r\n }\r\n\r\n /**\r\n * Get the start block element\r\n */\r\n public getStartBlockElement(): BlockElement {\r\n return this.startNode\r\n ? getBlockElementAtNode(this.rootNode, this.startNode)\r\n : getFirstLastBlockElement(this.rootNode, true /*isFirst*/);\r\n }\r\n\r\n /**\r\n * Get the start inline element\r\n */\r\n public getStartInlineElement(): InlineElement {\r\n return this.startNode\r\n ? getInlineElementAtNode(this.rootNode, this.startNode)\r\n : getFirstInlineElement(this.rootNode);\r\n }\r\n\r\n /**\r\n * Since the scope is global, all blocks under the root node are in scope\r\n */\r\n public isBlockInScope(blockElement: BlockElement): boolean {\r\n return contains(this.rootNode, blockElement.getStartNode());\r\n }\r\n\r\n /**\r\n * Since we're at body scope, inline elements never need to be trimmed\r\n */\r\n public trimInlineElement(inlineElement: InlineElement): InlineElement {\r\n return inlineElement;\r\n }\r\n}\r\n","import EmptyInlineElement from '../inlineElements/EmptyInlineElement';\r\nimport getBlockElementAtNode from '../blockElements/getBlockElementAtNode';\r\nimport getInlineElementAtNode from '../inlineElements/getInlineElementAtNode';\r\nimport NodeBlockElement from '../blockElements/NodeBlockElement';\r\nimport Position from '../selection/Position';\r\nimport safeInstanceOf from '../utils/safeInstanceOf';\r\nimport TraversingScoper from './TraversingScoper';\r\nimport { BlockElement, ContentPosition, InlineElement, NodePosition } from 'roosterjs-editor-types';\r\nimport { getInlineElementAfter } from '../inlineElements/getInlineElementBeforeAfter';\r\nimport {\r\n getFirstInlineElement,\r\n getLastInlineElement,\r\n} from '../inlineElements/getFirstLastInlineElement';\r\n\r\n/**\r\n * @internal\r\n * This provides traversing content in a selection start block\r\n * This is commonly used for those cursor context sensitive plugin,\r\n * they want to know text being typed at cursor\r\n * This provides a scope for parsing from cursor position up to begin of the selection block\r\n */\r\nexport default class SelectionBlockScoper implements TraversingScoper {\r\n private block: BlockElement;\r\n private position: NodePosition;\r\n\r\n /**\r\n * Create a new instance of SelectionBlockScoper class\r\n * @param rootNode The root node of the whole scope\r\n * @param position Position of the selection start\r\n * @param startFrom Where to start, can be Begin, End, SelectionStart\r\n */\r\n constructor(\r\n public rootNode: Node,\r\n position: NodePosition | Range,\r\n private startFrom: ContentPosition\r\n ) {\r\n position = safeInstanceOf(position, 'Range') ? Position.getStart(position) : position;\r\n this.position = position.normalize();\r\n this.block = getBlockElementAtNode(this.rootNode, this.position.node);\r\n }\r\n\r\n /**\r\n * Get the start block element\r\n */\r\n public getStartBlockElement(): BlockElement {\r\n return this.block;\r\n }\r\n\r\n /**\r\n * Get the start inline element\r\n * The start inline refers to inline before the selection start\r\n * The reason why we choose the one before rather after is, when cursor is at the end of a paragraph,\r\n * the one after likely will point to inline in next paragraph which may be null if the cursor is at bottom of editor\r\n */\r\n public getStartInlineElement(): InlineElement {\r\n if (this.block) {\r\n switch (this.startFrom) {\r\n case ContentPosition.Begin:\r\n case ContentPosition.End:\r\n case ContentPosition.DomEnd:\r\n return getFirstLastInlineElementFromBlockElement(\r\n this.block,\r\n this.startFrom == ContentPosition.Begin\r\n );\r\n case ContentPosition.SelectionStart:\r\n // Get the inline before selection start point, and ensure it falls in the selection block\r\n let startInline = getInlineElementAfter(this.rootNode, this.position);\r\n return startInline && this.block.contains(startInline.getContainerNode())\r\n ? startInline\r\n : new EmptyInlineElement(this.position, this.block);\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Check if the given block element is in current scope\r\n * @param blockElement The block element to check\r\n */\r\n public isBlockInScope(blockElement: BlockElement): boolean {\r\n return this.block && blockElement ? this.block.equals(blockElement) : false;\r\n }\r\n\r\n /**\r\n * Trim the incoming inline element, and return an inline element\r\n * This just tests and return the inline element if it is in block\r\n * This is a block scoper, which is not like selection scoper where it may cut an inline element in half\r\n * A block scoper does not cut an inline in half\r\n */\r\n public trimInlineElement(inlineElement: InlineElement): InlineElement {\r\n return this.block && inlineElement && this.block.contains(inlineElement.getContainerNode())\r\n ? inlineElement\r\n : null;\r\n }\r\n}\r\n\r\n/**\r\n * Get first/last InlineElement of the given BlockElement\r\n * @param block The BlockElement to get InlineElement from\r\n * @param isFirst True to get first InlineElement, false to get last InlineElement\r\n */\r\nfunction getFirstLastInlineElementFromBlockElement(\r\n block: BlockElement,\r\n isFirst: boolean\r\n): InlineElement {\r\n if (block instanceof NodeBlockElement) {\r\n let blockNode = block.getStartNode();\r\n return isFirst ? getFirstInlineElement(blockNode) : getLastInlineElement(blockNode);\r\n } else {\r\n return getInlineElementAtNode(block, isFirst ? block.getStartNode() : block.getEndNode());\r\n }\r\n}\r\n","import getBlockElementAtNode from '../blockElements/getBlockElementAtNode';\r\nimport PartialInlineElement from '../inlineElements/PartialInlineElement';\r\nimport Position from '../selection/Position';\r\nimport TraversingScoper from './TraversingScoper';\r\nimport { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types';\r\nimport { getInlineElementAfter } from '../inlineElements/getInlineElementBeforeAfter';\r\n\r\n/**\r\n * @internal\r\n * This is selection scoper that provide a start inline as the start of the selection\r\n * and checks if a block falls in the selection (isBlockInScope)\r\n * last trimInlineElement to trim any inline element to return a partial that falls in the selection\r\n */\r\nexport default class SelectionScoper implements TraversingScoper {\r\n private start: NodePosition;\r\n private end: NodePosition;\r\n private startBlock: BlockElement;\r\n private startInline: InlineElement;\r\n\r\n /**\r\n * Create a new instance of SelectionScoper class\r\n * @param rootNode The root node of the content\r\n * @param range The selection range to scope to\r\n */\r\n constructor(public rootNode: Node, range: Range) {\r\n this.start = Position.getStart(range).normalize();\r\n this.end = Position.getEnd(range).normalize();\r\n }\r\n\r\n /**\r\n * Provide a start block as the first block after the cursor\r\n */\r\n public getStartBlockElement(): BlockElement {\r\n if (!this.startBlock) {\r\n this.startBlock = getBlockElementAtNode(this.rootNode, this.start.node);\r\n }\r\n\r\n return this.startBlock;\r\n }\r\n\r\n /**\r\n * Provide a start inline as the first inline after the cursor\r\n */\r\n public getStartInlineElement(): InlineElement {\r\n if (!this.startInline) {\r\n this.startInline = this.trimInlineElement(\r\n getInlineElementAfter(this.rootNode, this.start)\r\n );\r\n }\r\n\r\n return this.startInline;\r\n }\r\n\r\n /**\r\n * Checks if a block completely falls in the selection\r\n * @param block The BlockElement to check\r\n */\r\n public isBlockInScope(block: BlockElement): boolean {\r\n if (!block) {\r\n return false;\r\n }\r\n let inScope = false;\r\n let selStartBlock = this.getStartBlockElement();\r\n if (this.start.equalTo(this.end)) {\r\n inScope = selStartBlock && selStartBlock.equals(block);\r\n } else {\r\n let selEndBlock = getBlockElementAtNode(this.rootNode, this.end.node);\r\n\r\n // There are three cases that are considered as \"block in scope\"\r\n // 1) The start of selection falls on the block\r\n // 2) The end of selection falls on the block\r\n // 3) the block falls in-between selection start and end\r\n inScope =\r\n selStartBlock &&\r\n selEndBlock &&\r\n (block.equals(selStartBlock) ||\r\n block.equals(selEndBlock) ||\r\n (block.isAfter(selStartBlock) && selEndBlock.isAfter(block)));\r\n }\r\n\r\n return inScope;\r\n }\r\n\r\n /**\r\n * Trim an incoming inline. If it falls completely outside selection, return null\r\n * otherwise return a partial that represents the portion that falls in the selection\r\n * @param inline The InlineElement to check\r\n */\r\n public trimInlineElement(inline: InlineElement): InlineElement {\r\n if (!inline || this.start.equalTo(this.end)) {\r\n return null;\r\n }\r\n\r\n // Temp code. Will be changed to using InlineElement.getStart/EndPosition() soon\r\n let start = inline.getStartPosition();\r\n let end = inline.getEndPosition();\r\n\r\n if (start.isAfter(this.end) || this.start.isAfter(end)) {\r\n return null;\r\n }\r\n\r\n let startPartial = false;\r\n let endPartial = false;\r\n\r\n if (this.start.isAfter(start)) {\r\n start = this.start;\r\n startPartial = true;\r\n }\r\n\r\n if (end.isAfter(this.end)) {\r\n end = this.end;\r\n endPartial = true;\r\n }\r\n\r\n return start.isAfter(end) || start.equalTo(end)\r\n ? null\r\n : startPartial || endPartial\r\n ? new PartialInlineElement(inline, startPartial && start, endPartial && end)\r\n : inline;\r\n }\r\n}\r\n","import extractClipboardItems from './extractClipboardItems';\nimport extractClipboardItemsForIE from './extractClipboardItemsForIE';\nimport toArray from '../utils/toArray';\nimport { ClipboardData, ExtractClipboardEventOption } from 'roosterjs-editor-types';\n\ninterface WindowForIE extends Window {\n clipboardData: DataTransfer;\n}\n\n/**\n * @deprecated Use extractClipboardItems and extractClipboardItemsForIE instead\n * Extract a Clipboard event\n * @param event The paste event\n * @param callback Callback function when data is ready\n * @param options Options to retrieve more items from the event, including HTML string and other customized items\n * @returns An object with the following properties:\n * types: Available types from the clipboard event\n * text: Plain text from the clipboard event\n * image: Image file from the clipboard event\n * html: Html string from the clipboard event. When set to null, it means there's no HTML found from the event.\n * When set to undefined, it means can't retrieve HTML string, there may be HTML string but direct retrieving is\n * not supported by browser.\n */\nexport default function extractClipboardEvent(\n event: ClipboardEvent,\n callback: (clipboardData: ClipboardData) => void,\n options?: ExtractClipboardEventOption\n) {\n const dataTransfer =\n event.clipboardData ||\n ((event.target).ownerDocument.defaultView).clipboardData;\n\n if (dataTransfer.items) {\n event.preventDefault();\n extractClipboardItems(toArray(dataTransfer.items), options).then(callback);\n } else {\n extractClipboardItemsForIE(dataTransfer, callback, options);\n }\n}\n","import setColor from './setColor';\r\nimport { DefaultFormat } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Apply format to an HTML element\r\n * @param element The HTML element to apply format to\r\n * @param format The format to apply\r\n */\r\nexport default function applyFormat(\r\n element: HTMLElement,\r\n format: DefaultFormat,\r\n isDarkMode?: boolean\r\n) {\r\n if (format) {\r\n let elementStyle = element.style;\r\n let {\r\n fontFamily,\r\n fontSize,\r\n textColor,\r\n textColors,\r\n backgroundColor,\r\n backgroundColors,\r\n bold,\r\n italic,\r\n underline,\r\n } = format;\r\n\r\n if (fontFamily) {\r\n elementStyle.fontFamily = fontFamily;\r\n }\r\n if (fontSize) {\r\n elementStyle.fontSize = fontSize;\r\n }\r\n\r\n setColor(element, textColors || textColor, false /*isBackground*/, isDarkMode);\r\n setColor(element, backgroundColors || backgroundColor, true /*isBackground*/, isDarkMode);\r\n\r\n if (bold) {\r\n elementStyle.fontWeight = 'bold';\r\n }\r\n if (italic) {\r\n elementStyle.fontStyle = 'italic';\r\n }\r\n if (underline) {\r\n elementStyle.textDecoration = 'underline';\r\n }\r\n }\r\n}\r\n","import { DocumentCommand, PendableFormatState } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Names of Pendable formats\r\n */\r\nexport type PendableFormatNames = keyof PendableFormatState;\r\n\r\n/**\r\n * A map from pendable format name to document command\r\n */\r\nexport const PendableFormatCommandMap: { [key in PendableFormatNames]: DocumentCommand } = {\r\n /**\r\n * Bold\r\n */\r\n isBold: DocumentCommand.Bold,\r\n\r\n /**\r\n * Italic\r\n */\r\n isItalic: DocumentCommand.Italic,\r\n\r\n /**\r\n * Underline\r\n */\r\n isUnderline: DocumentCommand.Underline,\r\n\r\n /**\r\n * StrikeThrough\r\n */\r\n isStrikeThrough: DocumentCommand.StrikeThrough,\r\n\r\n /**\r\n * Subscript\r\n */\r\n isSubscript: DocumentCommand.Subscript,\r\n\r\n /**\r\n * Superscript\r\n */\r\n isSuperscript: DocumentCommand.Superscript,\r\n};\r\n\r\n/**\r\n * Get Pendable Format State at cursor.\r\n * @param document The HTML Document to get format state from\r\n * @returns A PendableFormatState object which contains the values of pendable format states\r\n */\r\nexport default function getPendableFormatState(document: Document): PendableFormatState {\r\n let keys = Object.keys(PendableFormatCommandMap) as PendableFormatNames[];\r\n\r\n return keys.reduce((state, key) => {\r\n state[key] = document.queryCommandState(PendableFormatCommandMap[key]);\r\n return state;\r\n }, {});\r\n}\r\n","import { LinkData } from 'roosterjs-editor-types';\r\n\r\ninterface LinkMatchRule {\r\n match: RegExp;\r\n except?: RegExp;\r\n normalizeUrl?: (url: string) => string;\r\n}\r\n\r\n// http exclude matching regex\r\n// invalid URL example (in particular on IE and Edge):\r\n// - http://www.bing.com%00, %00 before ? (question mark) is considered invalid. IE/Edge throws invalid argument exception\r\n// - http://www.bing.com%1, %1 is invalid\r\n// - http://www.bing.com%g, %g is invalid (IE and Edge expects a two hex value after a %)\r\n// - http://www.bing.com%, % as ending is invalid (IE and Edge expects a two hex value after a %)\r\n// All above % cases if they're after ? (question mark) is then considered valid again\r\n// Similar for @, it needs to be after / (forward slash), or ? (question mark). Otherwise IE/Edge will throw security exception\r\n// - http://www.bing.com@name, @name before ? (question mark) is considered invalid\r\n// - http://www.bing.com/@name, is valid sine it is after / (forward slash)\r\n// - http://www.bing.com?@name, is also valid since it is after ? (question mark)\r\n// The regex below is essentially a break down of:\r\n// ^[^?]+%[^0-9a-f]+ => to exclude URL like www.bing.com%%\r\n// ^[^?]+%[0-9a-f][^0-9a-f]+ => to exclude URL like www.bing.com%1\r\n// ^[^?]+%00 => to exclude URL like www.bing.com%00\r\n// ^[^?]+%$ => to exclude URL like www.bing.com%\r\n// ^https?:\\/\\/[^?\\/]+@ => to exclude URL like http://www.bing.com@name\r\n// ^www\\.[^?\\/]+@ => to exclude URL like www.bing.com@name\r\n// , => to exclude url like www.bing,,com\r\nconst httpExcludeRegEx = /^[^?]+%[^0-9a-f]+|^[^?]+%[0-9a-f][^0-9a-f]+|^[^?]+%00|^[^?]+%$|^https?:\\/\\/[^?\\/]+@|^www\\.[^?\\/]+@/i;\r\n\r\n// via https://tools.ietf.org/html/rfc1035 Page 7\r\nconst labelRegEx = '[a-z0-9](?:[a-z0-9-]*[a-z0-9])?'; // We're using case insensitive regexes below so don't bother including A-Z\r\nconst domainNameRegEx = `(?:${labelRegEx}\\\\.)*${labelRegEx}`;\r\nconst domainPortRegEx = `${domainNameRegEx}(?:\\\\:[0-9]+)?`;\r\nconst domainPortWithUrlRegEx = `${domainPortRegEx}(?:[\\\\/\\\\?]\\\\S*)?`;\r\n\r\nconst linkMatchRules: { [schema: string]: LinkMatchRule } = {\r\n http: {\r\n match: new RegExp(\r\n `^(?:microsoft-edge:)?http:\\\\/\\\\/${domainPortWithUrlRegEx}|www\\\\.${domainPortWithUrlRegEx}`,\r\n 'i'\r\n ),\r\n except: httpExcludeRegEx,\r\n normalizeUrl: url =>\r\n new RegExp('^(?:microsoft-edge:)?http:\\\\/\\\\/', 'i').test(url) ? url : 'http://' + url,\r\n },\r\n https: {\r\n match: new RegExp(`^(?:microsoft-edge:)?https:\\\\/\\\\/${domainPortWithUrlRegEx}`, 'i'),\r\n except: httpExcludeRegEx,\r\n },\r\n mailto: { match: new RegExp('^mailto:\\\\S+@\\\\S+\\\\.\\\\S+', 'i') },\r\n notes: { match: new RegExp('^notes:\\\\/\\\\/\\\\S+', 'i') },\r\n file: { match: new RegExp('^file:\\\\/\\\\/\\\\/?\\\\S+', 'i') },\r\n unc: { match: new RegExp('^\\\\\\\\\\\\\\\\\\\\S+', 'i') },\r\n ftp: {\r\n match: new RegExp(\r\n `^ftp:\\\\/\\\\/${domainPortWithUrlRegEx}|ftp\\\\.${domainPortWithUrlRegEx}`,\r\n 'i'\r\n ),\r\n normalizeUrl: url => (new RegExp('^ftp:\\\\/\\\\/', 'i').test(url) ? url : 'ftp://' + url),\r\n },\r\n news: { match: new RegExp(`^news:(\\\\/\\\\/)?${domainPortWithUrlRegEx}`, 'i') },\r\n telnet: { match: new RegExp(`^telnet:(\\\\/\\\\/)?${domainPortWithUrlRegEx}`, 'i') },\r\n gopher: { match: new RegExp(`^gopher:\\\\/\\\\/${domainPortWithUrlRegEx}`, 'i') },\r\n wais: { match: new RegExp(`^wais:(\\\\/\\\\/)?${domainPortWithUrlRegEx}`, 'i') },\r\n};\r\n\r\n/**\r\n * Try to match a given string with link match rules, return matched link\r\n * @param url Input url to match\r\n * @param option Link match option, exact or partial. If it is exact match, we need\r\n * to check the length of matched link and url\r\n * @param rules Optional link match rules, if not passed, only the default link match\r\n * rules will be applied\r\n * @returns The matched link data, or null if no match found.\r\n * The link data includes an original url and a normalized url\r\n */\r\nexport default function matchLink(url: string): LinkData {\r\n if (url) {\r\n for (let schema of Object.keys(linkMatchRules)) {\r\n let rule = linkMatchRules[schema];\r\n let matches = url.match(rule.match);\r\n if (matches && matches[0] == url && (!rule.except || !rule.except.test(url))) {\r\n return {\r\n scheme: schema,\r\n originalUrl: url,\r\n normalizedUrl: rule.normalizeUrl ? rule.normalizeUrl(url) : url,\r\n };\r\n }\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n","import ContentTraverser from '../contentTraverser/ContentTraverser';\r\n\r\n/**\r\n * get block element's text content.\r\n * @param rootNode Root node that the get the textContent of.\r\n * @returns text content of given text content.\r\n */\r\nexport default function getTextContent(rootNode: Node): string {\r\n const traverser = ContentTraverser.createBodyTraverser(rootNode);\r\n let block = traverser && traverser.currentBlockElement;\r\n let textContent: string[] = [];\r\n\r\n while (block) {\r\n textContent.push(block.getTextContent());\r\n block = traverser.getNextBlockElement();\r\n }\r\n\r\n return textContent.join('\\n');\r\n}\r\n","import changeElementTag from './changeElementTag';\nimport contains from './contains';\nimport createRange from '../selection/createRange';\nimport findClosestElementAncestor from './findClosestElementAncestor';\nimport getBlockElementAtNode from '../blockElements/getBlockElementAtNode';\nimport getTagOfNode from './getTagOfNode';\nimport isNodeEmpty from './isNodeEmpty';\nimport isPositionAtBeginningOf from '../selection/isPositionAtBeginningOf';\nimport isVoidHtmlElement from './isVoidHtmlElement';\nimport LinkInlineElement from '../inlineElements/LinkInlineElement';\nimport Position from '../selection/Position';\nimport PositionContentSearcher from '../contentTraverser/PositionContentSearcher';\nimport queryElements from './queryElements';\nimport splitTextNode from './splitTextNode';\nimport toArray from './toArray';\nimport unwrap from './unwrap';\nimport VTable from '../table/VTable';\nimport wrap from './wrap';\nimport { NodePosition, NodeType, PositionType, QueryScope } from 'roosterjs-editor-types';\nimport { splitBalancedNodeRange } from './splitParentNode';\n\nconst adjustSteps: ((\n root: HTMLElement,\n nodeToInsert: Node,\n position: NodePosition,\n range: Range\n) => NodePosition)[] = [\n adjustInsertPositionForHyperLink,\n adjustInsertPositionForStructuredNode,\n adjustInsertPositionForParagraph,\n adjustInsertPositionForVoidElement,\n adjustInsertPositionForMoveCursorOutOfALink,\n];\n\n/**\n * Adjust position for A tag don't be nested inside another A tag.\n */\nfunction adjustInsertPositionForHyperLink(\n root: HTMLElement,\n nodeToInsert: Node,\n position: NodePosition,\n range: Range\n): NodePosition {\n let blockElement = getBlockElementAtNode(root, position.node);\n\n if (blockElement) {\n // Find the first tag within current block which covers current selection\n // If there are more than one nested, let's handle the first one only since that is not a common scenario.\n let anchor = queryElements(\n root,\n 'a[href]',\n null /*forEachCallback*/,\n QueryScope.OnSelection,\n createRange(position)\n ).filter((a: HTMLElement) => blockElement.contains(a))[0];\n\n // If this is about to insert node to an empty A tag, clear the A tag and reset position\n if (anchor && isNodeEmpty(anchor)) {\n position = new Position(anchor, PositionType.Before);\n safeRemove(anchor);\n anchor = null;\n }\n\n // If this is about to insert nodes which contains A tag into another A tag, need to break current A tag\n // otherwise we will have nested A tags which is a wrong HTML structure\n if (\n anchor &&\n ((nodeToInsert as HTMLElement))?.querySelector &&\n ((nodeToInsert as HTMLElement))?.querySelector('a[href]')\n ) {\n let normalizedPosition = position.normalize();\n let parentNode = normalizedPosition.node.parentNode;\n let nextNode =\n normalizedPosition.node.nodeType == NodeType.Text\n ? splitTextNode(\n normalizedPosition.node,\n normalizedPosition.offset,\n false /*returnFirstPart*/\n )\n : normalizedPosition.isAtEnd\n ? normalizedPosition.node.nextSibling\n : normalizedPosition.node;\n let splitter: Node = root.ownerDocument.createTextNode('');\n parentNode.insertBefore(splitter, nextNode);\n\n while (contains(anchor, splitter)) {\n splitter = splitBalancedNodeRange(splitter);\n }\n\n position = new Position(splitter, PositionType.Before);\n safeRemove(splitter);\n }\n }\n\n return position;\n}\n\n/**\n * Adjust position for a node don't be nested inside tags like BR, LI, TD.\n */\nfunction adjustInsertPositionForStructuredNode(\n root: HTMLElement,\n nodeToInsert: Node,\n position: NodePosition,\n range: Range\n): NodePosition {\n let rootNodeToInsert = nodeToInsert;\n\n if (rootNodeToInsert.nodeType == NodeType.DocumentFragment) {\n let rootNodes = toArray(rootNodeToInsert.childNodes).filter(\n (n: ChildNode) => getTagOfNode(n) != 'BR'\n );\n rootNodeToInsert = rootNodes.length == 1 ? rootNodes[0] : null;\n }\n\n let tag = getTagOfNode(rootNodeToInsert);\n let hasBrNextToRoot = tag && getTagOfNode(rootNodeToInsert.nextSibling) == 'BR';\n let listItem = findClosestElementAncestor(position.node, root, 'LI');\n let listNode = listItem && findClosestElementAncestor(listItem, root, 'OL,UL');\n let tdNode = findClosestElementAncestor(position.node, root, 'TD,TH');\n let trNode = tdNode && findClosestElementAncestor(tdNode, root, 'TR');\n\n if (tag == 'LI') {\n tag = listNode ? getTagOfNode(listNode) : 'UL';\n rootNodeToInsert = wrap(rootNodeToInsert, tag);\n }\n\n if ((tag == 'OL' || tag == 'UL') && getTagOfNode(rootNodeToInsert.firstChild) == 'LI') {\n let shouldInsertListAsText = !rootNodeToInsert.firstChild.nextSibling && !hasBrNextToRoot;\n\n if (hasBrNextToRoot && rootNodeToInsert.parentNode) {\n safeRemove(rootNodeToInsert.nextSibling);\n }\n\n if (shouldInsertListAsText) {\n unwrap(rootNodeToInsert.firstChild);\n unwrap(rootNodeToInsert);\n } else if (getTagOfNode(listNode) == tag) {\n unwrap(rootNodeToInsert);\n position = new Position(\n listItem,\n isPositionAtBeginningOf(position, listItem)\n ? PositionType.Before\n : PositionType.After\n );\n }\n } else if (tag == 'TABLE' && trNode) {\n // When inserting a table into a table, if these tables have the same column count, and\n // current position is at beginning of a row, then merge these two tables\n let newTable = new VTable(rootNodeToInsert);\n let currentTable = new VTable(tdNode);\n if (\n currentTable.col == 0 &&\n tdNode == currentTable.getCell(currentTable.row, 0).td &&\n newTable.cells[0] &&\n newTable.cells[0].length == currentTable.cells[0].length &&\n isPositionAtBeginningOf(position, tdNode)\n ) {\n if (\n getTagOfNode(rootNodeToInsert.firstChild) == 'TBODY' &&\n !rootNodeToInsert.firstChild.nextSibling\n ) {\n unwrap(rootNodeToInsert.firstChild);\n }\n unwrap(rootNodeToInsert);\n position = new Position(trNode, PositionType.After);\n }\n }\n\n return position;\n}\n\n/**\n * Change P tag to DIV, when a new node when insert node.\n */\n\nfunction adjustInsertPositionForParagraph(\n root: HTMLElement,\n nodeToInsert: Node,\n position: NodePosition,\n range: Range\n): NodePosition {\n if (getTagOfNode(position.node) == 'P') {\n // Insert into a P tag may cause issues when the inserted content contains any block element.\n // Change P tag to DIV to make sure it works well\n let pos = position.normalize();\n let div = changeElementTag(position.node, 'div');\n if (pos.node != div) {\n position = pos;\n }\n }\n\n return position;\n}\n\n/**\n * Adjust position for a node that can have children.\n */\n\nfunction adjustInsertPositionForVoidElement(\n root: HTMLElement,\n nodeToInsert: Node,\n position: NodePosition,\n range: Range\n): NodePosition {\n if (isVoidHtmlElement(position.node)) {\n position = new Position(\n position.node,\n position.isAtEnd ? PositionType.After : PositionType.Before\n );\n }\n return position;\n}\n\n/**\n * Adjust the position cursor out of link when a new node is inserted.\n */\n\nfunction adjustInsertPositionForMoveCursorOutOfALink(\n root: HTMLElement,\n nodeToInsert: Node,\n position: NodePosition,\n range: Range\n): NodePosition {\n if (range && range.collapsed) {\n const searcher = new PositionContentSearcher(root, Position.getStart(range));\n const inlineElementBefore = searcher.getInlineElementBefore();\n const inlineElementAfter = searcher.getInlineElementAfter();\n if (inlineElementBefore instanceof LinkInlineElement) {\n position = new Position(inlineElementBefore.getContainerNode(), PositionType.After);\n } else if (inlineElementAfter instanceof LinkInlineElement) {\n position = new Position(inlineElementAfter.getContainerNode(), PositionType.Before);\n }\n }\n return position;\n}\n\n/**\n *\n * @param root the contentDiv of the ditor\n * @param nodeToInsert the node to be inserted\n * @param position the position of the node to be inserted\n * @param range the range current or cached range of the editor\n * @returns the adjusted position of the inserted node\n */\n\nexport default function adjustInsertPositionBySteps(\n root: HTMLElement,\n nodeToInsert: Node,\n position: NodePosition,\n range: Range\n): NodePosition {\n adjustSteps.forEach(handler => {\n position = handler(root, nodeToInsert, position, range);\n });\n return position;\n}\n\nfunction safeRemove(node: Node) {\n node?.parentNode?.removeChild(node);\n}\n","import createElement from '../utils/createElement';\nimport getRootListNode from './getRootListNode';\nimport getSelectedBlockElementsInRegion from '../region/getSelectedBlockElementsInRegion';\nimport isNodeInRegion from '../region/isNodeInRegion';\nimport Position from '../selection/Position';\nimport safeInstanceOf from '../utils/safeInstanceOf';\nimport shouldSkipNode from '../utils/shouldSkipNode';\nimport toArray from '../utils/toArray';\nimport VList from './VList';\nimport wrap from '../utils/wrap';\nimport { getLeafSibling } from '../utils/getLeafSibling';\nimport { isListElement } from './getListTypeFromNode';\nimport { KnownCreateElementDataIndex, ListType, Region } from 'roosterjs-editor-types';\nimport { PositionType } from 'roosterjs-editor-types';\n\nconst ListSelector = 'ol,ul';\n\n/**\n * @internal\n * @param region The region to get VList from\n * @param includeSiblingLists True to also try get lists before and after the selection and merge them together,\n * false to only include the list for the selected blocks\n * @param startNode (Optional) When specified, try get VList which will contain this node.\n * If not specified, get VList from selection of this region\n */\nexport default function createVListFromRegion(\n region: Region,\n includeSiblingLists?: boolean,\n startNode?: Node\n): VList {\n if (!region) {\n return null;\n }\n\n let nodes: Node[] = [];\n\n if (startNode) {\n const list = getRootListNode(region, ListSelector, startNode);\n if (list) {\n nodes.push(list);\n }\n } else {\n const blocks = getSelectedBlockElementsInRegion(region);\n blocks.forEach(block => {\n const list = getRootListNode(region, ListSelector, block.getStartNode());\n\n if (list) {\n if (nodes[nodes.length - 1] != list) {\n nodes.push(list);\n }\n if (\n nodes.length == 1 &&\n safeInstanceOf(list, 'HTMLOListElement') &&\n list.start > 1\n ) {\n // Do not include sibling lists if this list is not start from 1\n includeSiblingLists = false;\n }\n } else {\n nodes.push(block.collapseToSingleElement());\n }\n });\n\n if (nodes.length == 0 && !region.rootNode.firstChild) {\n const newNode = createElement(\n KnownCreateElementDataIndex.EmptyLine,\n region.rootNode.ownerDocument\n );\n region.rootNode.appendChild(newNode);\n nodes.push(newNode);\n region.fullSelectionStart = new Position(newNode, PositionType.Begin);\n region.fullSelectionEnd = new Position(newNode, PositionType.End);\n }\n\n if (includeSiblingLists) {\n tryIncludeSiblingNode(region, nodes, false /*isNext*/);\n tryIncludeSiblingNode(region, nodes, true /*isNext*/);\n }\n\n nodes = nodes.filter(node => !shouldSkipNode(node, true /*ignoreSpace*/));\n }\n\n let vList: VList = null;\n\n if (nodes.length > 0) {\n const firstNode = nodes.shift();\n vList = isListElement(firstNode)\n ? new VList(firstNode)\n : createVListFromItemNode(firstNode);\n\n nodes.forEach(node => {\n if (isListElement(node)) {\n vList.mergeVList(new VList(node));\n } else {\n vList.appendItem(node, ListType.None);\n }\n });\n }\n\n return vList;\n}\n\nfunction tryIncludeSiblingNode(region: Region, nodes: Node[], isNext: boolean) {\n let node = nodes[isNext ? nodes.length - 1 : 0];\n node = getLeafSibling(region.rootNode, node, isNext, region.skipTags, true /*ignoreSpace*/);\n node = getRootListNode(region, ListSelector, node);\n if (isNodeInRegion(region, node) && isListElement(node)) {\n if (isNext) {\n if (!safeInstanceOf(node, 'HTMLOListElement') || node.start == 1) {\n // Only include sibling list when\n // 1. This is a unordered list, OR\n // 2. This list starts from 1\n nodes.push(node);\n }\n } else {\n nodes.unshift(node);\n }\n }\n}\n\nfunction createVListFromItemNode(node: Node): VList {\n // Wrap all child nodes under a single one, and put the new list under original root node\n // so that the list can carry over styles under the root node.\n const childNodes = toArray(node.childNodes);\n const nodeForItem = childNodes.length == 1 ? childNodes[0] : wrap(childNodes, 'SPAN');\n\n // Create a temporary OL root element for this list.\n const listNode = node.ownerDocument.createElement('ol'); // Either OL or UL is ok here\n node.appendChild(listNode);\n\n // Create the VList and append items\n const vList = new VList(listNode);\n vList.appendItem(nodeForItem, ListType.None);\n\n return vList;\n}\n","import arrayPush from '../utils/arrayPush';\nimport getRootListNode from './getRootListNode';\nimport isNodeAfter from '../utils/isNodeAfter';\nimport isNodeInRegion from '../region/isNodeInRegion';\nimport queryElements from '../utils/queryElements';\nimport VList from './VList';\nimport { ListType, RegionBase } from 'roosterjs-editor-types';\n\nconst CHAIN_NAME_PREFIX = '__List_Chain_';\nconst CHAIN_DATASET_NAME = 'listchain';\nconst AFTER_CURSOR_DATASET_NAME = 'listchainafter';\nlet lastChainIndex = 0;\n\n/**\n * Represent a chain of list nodes.\n * A chain of lists is a virtual link of lists that have continuous numbers, when editor one of them,\n * all others should also be updated in order to main the list number to be continuous.\n */\nexport default class VListChain {\n private lastNumber = 0;\n private lastNumberBeforeCursor = 0;\n\n /**\n * Create an array of VListChain from current region in editor\n * @param region The region to create VListChain from\n * @param currentNode Optional current node, used for mark lists that are after this node\n * @param nameGenerator Used by test code only\n */\n static createListChains(\n region: RegionBase | RegionBase[],\n currentNode?: Node,\n nameGenerator?: () => string\n ): VListChain[] {\n const regions = Array.isArray(region) ? region : region ? [region] : [];\n const result: VListChain[] = [];\n regions.forEach(region => {\n const chains: VListChain[] = [];\n let lastList: HTMLOListElement;\n\n queryElements(region.rootNode, 'ol', ol => {\n const list = getRootListNode(region, 'ol', ol);\n\n if (lastList != list) {\n const chain =\n chains.filter(c => c.canAppendToTail(list))[0] ||\n new VListChain(region, (nameGenerator || createListChainName)());\n const index = chains.indexOf(chain);\n const afterCurrentNode = currentNode && isNodeAfter(list, currentNode);\n\n if (!afterCurrentNode) {\n // Make sure current one is at the front if current block has not been met, so that\n // the first chain is always the nearest one from current node\n if (index >= 0) {\n chains.splice(index, 1);\n }\n\n chains.unshift(chain);\n } else if (index < 0) {\n chains.push(chain);\n }\n\n chain.append(list, afterCurrentNode);\n lastList = list;\n }\n });\n\n arrayPush(result, chains);\n });\n\n return result;\n }\n\n /**\n * Check if a list with the given start number can be appended next to the last list before cursor\n * @param startNumber The start number of the new list\n */\n canAppendAtCursor(startNumber: number): boolean {\n return this.lastNumberBeforeCursor + 1 == startNumber;\n }\n\n /**\n * Create a VList to wrap the block of the given node, and append to current chain\n * @param container The container node to create list at\n * @param startNumber Start number of the new list\n */\n createVListAtBlock(container: Node, startNumber: number): VList {\n if (container) {\n const list = container.ownerDocument.createElement('ol');\n\n list.start = startNumber;\n this.applyChainName(list);\n container.parentNode.insertBefore(list, container);\n\n const vList = new VList(list);\n\n vList.appendItem(container, ListType.None);\n return vList;\n } else {\n return null;\n }\n }\n\n /**\n * After change the lists, commit the change to all lists in this chain to update the list number,\n * and clear the temporary dataset values added to list node\n */\n commit() {\n const lists = this.getLists();\n let lastNumber = 0;\n\n for (let i = 0; i < lists.length; i++) {\n const list = lists[i];\n list.start = lastNumber + 1;\n\n const vlist = new VList(list);\n\n lastNumber = vlist.getLastItemNumber();\n\n delete list.dataset[CHAIN_DATASET_NAME];\n delete list.dataset[AFTER_CURSOR_DATASET_NAME];\n\n vlist.writeBack();\n }\n }\n\n /**\n * Construct a new instance of VListChain class\n * @param editor Editor object\n */\n private constructor(private region: RegionBase, private name: string) {}\n\n /**\n * Check if the given list node is can be appended into current list chain\n * @param list The list node to check\n */\n private canAppendToTail(list: HTMLOListElement) {\n return this.lastNumber + 1 == list.start;\n }\n\n /**\n * Append the given list node into this VListChain\n * @param list The list node to append\n * @param isAfterCurrentNode Whether this list is after current node\n */\n private append(list: HTMLOListElement, isAfterCurrentNode: boolean) {\n this.applyChainName(list);\n this.lastNumber = new VList(list).getLastItemNumber();\n\n if (isAfterCurrentNode) {\n list.dataset[AFTER_CURSOR_DATASET_NAME] = 'true';\n } else {\n this.lastNumberBeforeCursor = this.lastNumber;\n }\n }\n\n private applyChainName(list: HTMLOListElement) {\n list.dataset[CHAIN_DATASET_NAME] = this.name;\n }\n\n private getLists() {\n return queryElements(\n this.region.rootNode,\n `ol[data-${CHAIN_DATASET_NAME}=${this.name}]`\n ).filter(node => isNodeInRegion(this.region, node)) as HTMLOListElement[];\n }\n}\n\nfunction createListChainName() {\n return CHAIN_NAME_PREFIX + lastChainIndex++;\n}\n","import { RegionType } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Constants for each region type\n */\nexport interface RegionTypeData {\n /**\n * Tags that child elements will be skipped\n */\n skipTags: string[];\n\n /**\n * Selector of outer node of a region\n */\n outerSelector: string;\n\n /**\n * Selector of inner node of a region\n */\n innerSelector: string;\n}\n\nconst regionTypeData: Record = {\n [RegionType.Table]: {\n skipTags: ['TABLE'],\n outerSelector: 'table',\n innerSelector: 'td,th',\n },\n};\n\n/**\n * @internal\n */\nexport default regionTypeData;\n","import createElement from '../utils/createElement';\r\nimport createRange from './createRange';\r\nimport normalizeRect from '../utils/normalizeRect';\r\nimport { NodePosition, NodeType, Rect } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Get bounding rect of this position\r\n * @param position The position to get rect from\r\n */\r\nexport default function getPositionRect(position: NodePosition): Rect {\r\n if (!position) {\r\n return null;\r\n }\r\n\r\n let range = createRange(position);\r\n\r\n // 1) try to get rect using range.getBoundingClientRect()\r\n let rect = range.getBoundingClientRect && normalizeRect(range.getBoundingClientRect());\r\n\r\n if (rect) {\r\n return rect;\r\n }\r\n\r\n // 2) try to get rect using range.getClientRects\r\n position = position.normalize();\r\n const rects = range.getClientRects && range.getClientRects();\r\n rect = rects && rects.length == 1 && normalizeRect(rects[0]);\r\n if (rect) {\r\n return rect;\r\n }\r\n\r\n // 3) if node is text node, try inserting a SPAN and get the rect of SPAN for others\r\n if (position.node.nodeType == NodeType.Text) {\r\n const span = createElement(\r\n { tag: 'span', children: ['\\u200b'] },\r\n position.node.ownerDocument\r\n );\r\n range = createRange(position);\r\n range.insertNode(span);\r\n rect = span.getBoundingClientRect && normalizeRect(span.getBoundingClientRect());\r\n span.parentNode.removeChild(span);\r\n if (rect) {\r\n return rect;\r\n }\r\n }\r\n\r\n // 4) try getBoundingClientRect on element\r\n let element = position.element;\r\n if (element && element.getBoundingClientRect) {\r\n rect = normalizeRect(element.getBoundingClientRect());\r\n if (rect) {\r\n return rect;\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n","import getInnerHTML from '../utils/getInnerHTML';\nimport getSelectionPath from './getSelectionPath';\nimport getTagOfNode from '../utils/getTagOfNode';\nimport queryElements from '../utils/queryElements';\n\n/**\n * Get inner Html of a root node with a selection path which can be used for restore selection.\n * The result string can be used by setHtmlWithSelectionPath() to restore the HTML and selection.\n * @param rootNode Root node to get inner Html from\n * @param range The range of selection. If pass null, no selection path will be added\n * @returns Inner HTML of the root node, followed by HTML comment contains selection path if the given range is valid\n */\nexport default function getHtmlWithSelectionPath(\n rootNode: HTMLElement | DocumentFragment,\n range: Range\n): string {\n if (!rootNode) {\n return '';\n }\n\n const { startContainer, endContainer, startOffset, endOffset } = range || {};\n let isDOMChanged = false;\n\n queryElements(rootNode, 'table', table => {\n let tbody: HTMLTableSectionElement = null;\n\n for (let child = table.firstChild; child; child = child.nextSibling) {\n if (getTagOfNode(child) == 'TR') {\n if (!tbody) {\n tbody = table.ownerDocument.createElement('tbody');\n table.insertBefore(tbody, child);\n }\n\n tbody.appendChild(child);\n child = tbody;\n\n isDOMChanged = true;\n } else {\n tbody = null;\n }\n }\n });\n\n if (range && isDOMChanged) {\n try {\n range.setStart(startContainer, startOffset);\n range.setEnd(endContainer, endOffset);\n } catch {}\n }\n\n const content = getInnerHTML(rootNode);\n const selectionPath = range && getSelectionPath(rootNode, range);\n\n return selectionPath ? `${content}` : content;\n}\n","import createRange from './createRange';\r\nimport { NodeType, SelectionPath, TrustedHTMLHandler } from 'roosterjs-editor-types';\r\n\r\nconst LastCommentRegex = /$/;\r\n\r\n/**\r\n * Restore inner Html of a root element from given html string. If the string contains selection path,\r\n * remove the selection path and return a range represented by the path\r\n * @param root The root element\r\n * @param html The html to restore\r\n * @returns A selection range if the html contains a valid selection path, otherwise null\r\n */\r\nexport default function setHtmlWithSelectionPath(\r\n rootNode: HTMLElement,\r\n html: string,\r\n trustedHTMLHandler?: TrustedHTMLHandler\r\n): Range {\r\n if (!rootNode) {\r\n return null;\r\n }\r\n\r\n html = html || '';\r\n const lastComment = LastCommentRegex.exec(html);\r\n rootNode.innerHTML = trustedHTMLHandler?.(html) || html;\r\n const path = getSelectionPath(rootNode, lastComment?.[1]);\r\n\r\n return path && createRange(rootNode, path.start, path.end);\r\n}\r\n\r\nfunction getSelectionPath(root: HTMLElement, alternativeComment: string): SelectionPath {\r\n let pathCommentValue: string = '';\r\n let pathCommentNode: Node = null;\r\n let path: SelectionPath = null;\r\n if (root.lastChild?.nodeType == NodeType.Comment) {\r\n pathCommentNode = root.lastChild;\r\n pathCommentValue = pathCommentNode.nodeValue;\r\n } else {\r\n pathCommentValue = alternativeComment;\r\n }\r\n\r\n if (pathCommentValue) {\r\n try {\r\n path = JSON.parse(pathCommentValue) as SelectionPath;\r\n if (path && path.start?.length > 0 && path.end?.length > 0) {\r\n if (pathCommentNode) {\r\n root.removeChild(pathCommentNode);\r\n }\r\n } else {\r\n path = null;\r\n }\r\n } catch {}\r\n }\r\n\r\n return path;\r\n}\r\n","import { Browser } from '../utils/Browser';\n\n/**\n * Add the given range into selection of the given document\n * @param range The range to select\n * @param skipSameRange When set to true, do nothing if the given range is the same with current selection,\n * otherwise it will always remove current selection range and set to the given one.\n * This parameter is always treat as true in Edge to avoid some weird runtime exception.\n */\nexport default function addRangeToSelection(range: Range, skipSameRange?: boolean) {\n const selection = range?.commonAncestorContainer?.ownerDocument?.defaultView?.getSelection();\n if (selection) {\n let needAddRange = true;\n\n if (selection.rangeCount > 0) {\n // Workaround IE exception 800a025e\n try {\n let currentRange: Range;\n // Do not remove/add range if current selection is the same with target range\n // Without this check, execCommand() may fail in Edge since we changed the selection\n if (\n (skipSameRange || Browser.isEdge) &&\n (currentRange = selection.rangeCount == 1 ? selection.getRangeAt(0) : null) &&\n currentRange.startContainer == range.startContainer &&\n currentRange.startOffset == range.startOffset &&\n currentRange.endContainer == range.endContainer &&\n currentRange.endOffset == range.endOffset\n ) {\n needAddRange = false;\n } else {\n selection.removeAllRanges();\n }\n } catch (e) {}\n }\n\n if (needAddRange) {\n selection.addRange(range);\n }\n }\n}\n","import arrayPush from '../utils/arrayPush';\nimport collapseNodesInRegion from '../region/collapseNodesInRegion';\nimport getRegionsFromRange from '../region/getRegionsFromRange';\nimport getSelectionRangeInRegion from '../region/getSelectionRangeInRegion';\nimport mergeBlocksInRegion from '../region/mergeBlocksInRegion';\nimport Position from './Position';\nimport queryElements from '../utils/queryElements';\nimport safeInstanceOf from '../utils/safeInstanceOf';\nimport splitTextNode from '../utils/splitTextNode';\nimport { PositionType, QueryScope, RegionType } from 'roosterjs-editor-types';\n\n/**\n * Delete selected content, and return the new position to select\n * @param core The EditorCore object.\n * @param range The range to delete\n */\nexport default function deleteSelectedContent(root: HTMLElement, range: Range) {\n let nodeBefore: Node = null;\n\n // 1. TABLE and TR node in selected should be deleted. It is possible we don't detect them from step 2\n // since table cells will fall in to different regions\n const nodesToDelete: Node[] = queryElements(\n root,\n 'table,tr',\n null /*callback*/,\n QueryScope.InSelection,\n range\n );\n\n // 2. Loop all selected regions, find out those nodes need to be deleted and merged.\n // We don't delete them directly here because delete node from one region may cause selection range\n // another region becomes invalid. So we delay the process of deletion.\n const regions = getRegionsFromRange(root, range, RegionType.Table);\n const nodesPairToMerge = regions\n .map(region => {\n const regionRange = getSelectionRangeInRegion(region);\n if (!regionRange) {\n return null;\n }\n\n const { startContainer, endContainer, startOffset, endOffset } = regionRange;\n\n // Make sure there are node before and after the merging point.\n // This is required by mergeBlocksInRegion API.\n // This may create some empty text node as anchor\n let [beforeEnd, afterEnd] = ensureBeforeAndAfter(\n endContainer,\n endOffset,\n false /*isStart*/\n );\n let [beforeStart, afterStart] = ensureBeforeAndAfter(\n startContainer,\n startOffset,\n true /*isStart*/\n );\n nodeBefore = nodeBefore || beforeStart;\n\n // Find out all nodes to be deleted\n const nodes = collapseNodesInRegion(region, [afterStart, beforeEnd]);\n arrayPush(nodesToDelete, nodes);\n return { region, beforeStart, afterEnd };\n })\n .filter(x => !!x);\n\n // 3. Delete all nodes that we found\n nodesToDelete.forEach(node => node.parentNode?.removeChild(node));\n\n // 4. Merge lines for each region, so that after we don't see extra line breaks\n nodesPairToMerge.forEach(nodes =>\n mergeBlocksInRegion(nodes.region, nodes.beforeStart, nodes.afterEnd)\n );\n\n return nodeBefore && new Position(nodeBefore, PositionType.End);\n}\n\nfunction ensureBeforeAndAfter(node: Node, offset: number, isStart: boolean) {\n if (safeInstanceOf(node, 'Text')) {\n const newNode = splitTextNode(node, offset, isStart);\n return isStart ? [newNode, node] : [node, newNode];\n } else {\n let nodeBefore: Node = node.childNodes[offset - 1];\n let nodeAfter: Node = node.childNodes[offset];\n\n // Condition 1: node child nodes\n // (\"I\" means cursor; \"o\" means a DOM node, \"[ ]\" means a parent node)\n // [ I ]\n // need to use parent node instead to convert to condition 2\n if (!nodeBefore && !nodeAfter) {\n if (isStart) {\n nodeAfter = node;\n nodeBefore = nodeAfter.previousSibling;\n } else {\n nodeBefore = node;\n nodeAfter = nodeBefore.nextSibling;\n }\n }\n\n // Condition 2: Either nodeBefore or nodeAfter is null (XOR case)\n // [ o I ] or [ I o]\n // need to add empty text node to convert to condition 3\n if ((nodeBefore || nodeAfter) && (!nodeBefore || !nodeAfter)) {\n const emptyNode = node.ownerDocument.createTextNode('');\n (nodeBefore || nodeAfter).parentNode?.insertBefore(emptyNode, nodeAfter);\n if (nodeBefore) {\n nodeAfter = emptyNode;\n } else {\n nodeBefore = emptyNode;\n }\n }\n\n // Condition 3: Both nodeBefore and nodeAfter are not null\n // [o I o]\n // return the nodes\n return [nodeBefore, nodeAfter];\n }\n}\n","import clearProceedingSnapshots from './clearProceedingSnapshots';\r\nimport { Snapshots } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Add a new snapshot to the given snapshots data structure\r\n * @param snapshots The snapshots data structure to add new snapshot into\r\n * @param snapshot The snapshot to add\r\n * @param isAutoCompleteSnapshot Whether this is a snapshot before auto complete action\r\n */\r\nexport default function addSnapshot(\r\n snapshots: Snapshots,\r\n snapshot: string,\r\n isAutoCompleteSnapshot: boolean\r\n) {\r\n if (snapshots.currentIndex < 0 || snapshot != snapshots.snapshots[snapshots.currentIndex]) {\r\n clearProceedingSnapshots(snapshots);\r\n snapshots.snapshots.push(snapshot);\r\n snapshots.currentIndex++;\r\n snapshots.totalSize += snapshot.length;\r\n\r\n let removeCount = 0;\r\n while (\r\n removeCount < snapshots.snapshots.length &&\r\n snapshots.totalSize > snapshots.maxSize\r\n ) {\r\n snapshots.totalSize -= snapshots.snapshots[removeCount].length;\r\n removeCount++;\r\n }\r\n\r\n if (removeCount > 0) {\r\n snapshots.snapshots.splice(0, removeCount);\r\n snapshots.currentIndex -= removeCount;\r\n snapshots.autoCompleteIndex -= removeCount;\r\n }\r\n\r\n if (isAutoCompleteSnapshot) {\r\n snapshots.autoCompleteIndex = snapshots.currentIndex;\r\n }\r\n }\r\n}\r\n","import canMoveCurrentSnapshot from './canMoveCurrentSnapshot';\nimport { Snapshots } from 'roosterjs-editor-types';\n\n/**\n * Move current snapshot with the given step if can move this step. Otherwise no action and return null\n * @param snapshots The snapshots data structure to move\n * @param step The step to move\n * @returns If can move with the given step, returns the snapshot after move, otherwise null\n */\nexport default function moveCurrentSnapshot(snapshots: Snapshots, step: number): string {\n if (canMoveCurrentSnapshot(snapshots, step)) {\n snapshots.currentIndex += step;\n snapshots.autoCompleteIndex = -1;\n return snapshots.snapshots[snapshots.currentIndex];\n } else {\n return null;\n }\n}\n\n/**\n * @deprecated\n * For backward compatibility only\n */\nexport const moveCurrentSnapsnot = moveCurrentSnapshot;\n","import { Snapshots } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Create initial snapshots\r\n * @param maxSize max size of all snapshots\r\n */\r\nexport default function createSnapshots(maxSize: number): Snapshots {\r\n return {\r\n snapshots: [],\r\n totalSize: 0,\r\n currentIndex: -1,\r\n autoCompleteIndex: -1,\r\n maxSize,\r\n };\r\n}\r\n","import { Snapshots } from 'roosterjs-editor-types';\n\n/**\n * Whether there is a snapshot added before auto complete and it can be undone now\n */\nexport default function canUndoAutoComplete(snapshots: Snapshots): boolean {\n return (\n snapshots.autoCompleteIndex >= 0 &&\n snapshots.currentIndex - snapshots.autoCompleteIndex == 1\n );\n}\n","import changeElementTag from '../utils/changeElementTag';\nimport getInheritableStyles from './getInheritableStyles';\nimport getPredefinedCssForElement from './getPredefinedCssForElement';\nimport getStyles from '../style/getStyles';\nimport getTagOfNode from '../utils/getTagOfNode';\nimport safeInstanceOf from '../utils/safeInstanceOf';\nimport setStyles from '../style/setStyles';\nimport toArray from '../utils/toArray';\nimport { cloneObject } from './cloneObject';\nimport {\n getAllowedAttributes,\n getAllowedCssClassesRegex,\n getTagReplacement,\n getDefaultStyleValues,\n getStyleCallbacks,\n} from './getAllowedValues';\nimport {\n AttributeCallbackMap,\n CssStyleCallbackMap,\n ElementCallbackMap,\n HtmlSanitizerOptions,\n NodeType,\n PredefinedCssMap,\n SanitizeHtmlOptions,\n StringMap,\n} from 'roosterjs-editor-types';\n\n/**\n * HTML sanitizer class provides two features:\n * 1. Convert global CSS to inline CSS\n * 2. Sanitize an HTML document, remove unnecessary/dangerous attribute/nodes\n */\nexport default class HtmlSanitizer {\n /**\n * Convert global CSS to inline CSS if any\n * @param html HTML source\n * @param additionalStyleNodes (Optional) additional HTML STYLE elements used as global CSS\n */\n static convertInlineCss(html: string, additionalStyleNodes?: HTMLStyleElement[]) {\n let sanitizer = new HtmlSanitizer({\n additionalGlobalStyleNodes: additionalStyleNodes,\n });\n return sanitizer.exec(html, true /*convertCssOnly*/);\n }\n\n /**\n * Sanitize HTML string, remove any unused HTML node/attribute/CSS.\n * @param html HTML source string\n * @param options Options used for this sanitizing process\n */\n static sanitizeHtml(html: string, options?: SanitizeHtmlOptions) {\n options = options || {};\n let sanitizer = new HtmlSanitizer(options);\n let currentStyles = safeInstanceOf(options.currentElementOrStyle, 'HTMLElement')\n ? getInheritableStyles(options.currentElementOrStyle)\n : options.currentElementOrStyle;\n return sanitizer.exec(html, options.convertCssOnly, currentStyles);\n }\n\n private elementCallbacks: ElementCallbackMap;\n private styleCallbacks: CssStyleCallbackMap;\n private attributeCallbacks: AttributeCallbackMap;\n private tagReplacements: Record;\n private allowedAttributes: string[];\n private allowedCssClassesRegex: RegExp;\n private defaultStyleValues: StringMap;\n private additionalPredefinedCssForElement: PredefinedCssMap;\n private additionalGlobalStyleNodes: HTMLStyleElement[];\n private preserveHtmlComments: boolean;\n private unknownTagReplacement: string;\n\n /**\n * Construct a new instance of HtmlSanitizer\n * @param options Options for HtmlSanitizer\n */\n constructor(options?: HtmlSanitizerOptions) {\n options = options || {};\n this.elementCallbacks = cloneObject(options.elementCallbacks);\n this.styleCallbacks = getStyleCallbacks(options.cssStyleCallbacks);\n this.attributeCallbacks = cloneObject(options.attributeCallbacks);\n this.tagReplacements = getTagReplacement(options.additionalTagReplacements);\n this.allowedAttributes = getAllowedAttributes(options.additionalAllowedAttributes);\n this.allowedCssClassesRegex = getAllowedCssClassesRegex(\n options.additionalAllowedCssClasses\n );\n this.defaultStyleValues = getDefaultStyleValues(options.additionalDefaultStyleValues);\n this.additionalPredefinedCssForElement = options.additionalPredefinedCssForElement;\n this.additionalGlobalStyleNodes = options.additionalGlobalStyleNodes || [];\n this.preserveHtmlComments = options.preserveHtmlComments;\n this.unknownTagReplacement = options.unknownTagReplacement;\n }\n\n /**\n * Sanitize HTML string\n * This function will do the following work:\n * 1. Convert global CSS into inline CSS\n * 2. Remove dangerous HTML tags and attributes\n * 3. Remove useless CSS properties\n * @param html The input HTML\n * @param convertInlineCssOnly Whether only convert inline css and skip html content sanitizing\n * @param currentStyles Current inheritable CSS styles\n */\n exec(html: string, convertCssOnly?: boolean, currentStyles?: StringMap): string {\n const parser = new DOMParser();\n const doc = parser.parseFromString(unsafeConvertToTrustedHTML(html) || '', 'text/html');\n\n if (doc && doc.body && doc.body.firstChild) {\n this.convertGlobalCssToInlineCss(doc);\n if (!convertCssOnly) {\n this.sanitize(doc.body, currentStyles);\n }\n }\n return (doc && doc.body && doc.body.innerHTML) || '';\n }\n\n /**\n * Sanitize an HTML element, remove unnecessary or dangerous elements/attribute/CSS rules\n * @param rootNode Root node to sanitize\n * @param currentStyles Current CSS styles. Inheritable styles in the given node which has\n * the same value with current styles will be ignored.\n */\n sanitize(rootNode: Node, currentStyles?: StringMap) {\n if (!rootNode) {\n return '';\n }\n currentStyles = cloneObject(currentStyles, getInheritableStyles(null));\n this.processNode(rootNode, currentStyles, {});\n }\n\n /**\n * Convert global CSS into inline CSS\n * @param rootNode The HTML Document\n */\n convertGlobalCssToInlineCss(rootNode: ParentNode) {\n let styleNodes = toArray(rootNode.querySelectorAll('style'));\n let styleSheets = this.additionalGlobalStyleNodes\n .reverse()\n .map(node => node.sheet as CSSStyleSheet)\n .concat(styleNodes.map(node => node.sheet as CSSStyleSheet).reverse())\n .filter(sheet => sheet);\n for (let styleSheet of styleSheets) {\n for (let j = styleSheet.cssRules.length - 1; j >= 0; j--) {\n // Skip any none-style rule, i.e. @page\n let styleRule = styleSheet.cssRules[j] as CSSStyleRule;\n let text = styleRule && styleRule.style ? styleRule.style.cssText : null;\n if (styleRule.type != CSSRule.STYLE_RULE || !text || !styleRule.selectorText) {\n continue;\n }\n // Make sure the selector is not empty\n for (let selector of styleRule.selectorText.split(',')) {\n if (!selector || !selector.trim() || selector.indexOf(':') >= 0) {\n continue;\n }\n let nodes = toArray(rootNode.querySelectorAll(selector));\n // Always put existing styles after so that they have higher priority\n // Which means if both global style and inline style apply to the same element,\n // inline style will have higher priority\n nodes.forEach(node =>\n node.setAttribute('style', text + (node.getAttribute('style') || ''))\n );\n }\n }\n }\n\n styleNodes.forEach(node => {\n if (node.parentNode) {\n node.parentNode.removeChild(node);\n }\n });\n }\n\n private processNode(node: Node, currentStyle: StringMap, context: Object) {\n const nodeType = node.nodeType;\n const isElement = nodeType == NodeType.Element;\n const isText = nodeType == NodeType.Text;\n const isFragment = nodeType == NodeType.DocumentFragment;\n const isComment = nodeType == NodeType.Comment;\n\n let shouldKeep = false;\n\n if (isElement) {\n const tag = getTagOfNode(node);\n const callback = this.elementCallbacks[tag];\n let replacement = this.tagReplacements[tag.toLowerCase()];\n\n if (replacement === undefined) {\n replacement = this.unknownTagReplacement;\n }\n\n if (callback) {\n shouldKeep = callback(node as HTMLElement, context);\n } else if (tag.indexOf(':') > 0) {\n shouldKeep = true;\n } else if (tag == replacement || replacement == '*') {\n shouldKeep = true;\n } else if (replacement && /^[a-zA-Z][\\w\\-]*$/.test(replacement)) {\n node = changeElementTag(node as HTMLElement, replacement);\n shouldKeep = true;\n }\n } else if (isText) {\n const whiteSpace = currentStyle['white-space'];\n shouldKeep =\n whiteSpace == 'pre' ||\n whiteSpace == 'pre-line' ||\n whiteSpace == 'pre-wrap' ||\n !/^[\\r\\n]*$/g.test(node.nodeValue);\n } else if (isFragment) {\n shouldKeep = true;\n } else if (isComment) {\n shouldKeep = this.preserveHtmlComments;\n } else {\n shouldKeep = false;\n }\n\n if (!shouldKeep) {\n node.parentNode.removeChild(node);\n } else if (\n isText &&\n (currentStyle['white-space'] == 'pre' || currentStyle['white-space'] == 'pre-wrap')\n ) {\n node.nodeValue = node.nodeValue.replace(/^ /gm, '\\u00A0').replace(/ {2}/g, ' \\u00A0');\n } else if (isElement || isFragment) {\n let thisStyle = cloneObject(currentStyle);\n let element = node;\n if (isElement) {\n this.processAttributes(element, context);\n this.preprocessCss(element, thisStyle);\n this.processCss(element, thisStyle, context);\n }\n\n let child: Node = element.firstChild;\n let next: Node;\n for (; child; child = next) {\n next = child.nextSibling;\n this.processNode(child, thisStyle, context);\n }\n }\n }\n\n private preprocessCss(element: HTMLElement, thisStyle: StringMap) {\n const predefinedStyles = getPredefinedCssForElement(\n element,\n this.additionalPredefinedCssForElement\n );\n if (predefinedStyles) {\n Object.keys(predefinedStyles).forEach(name => {\n thisStyle[name] = predefinedStyles[name];\n });\n }\n }\n\n private processCss(element: HTMLElement, thisStyle: StringMap, context: Object) {\n const styles = getStyles(element);\n Object.keys(styles).forEach(name => {\n const value = styles[name];\n let callback = this.styleCallbacks[name];\n let isInheritable = thisStyle[name] != undefined;\n let keep =\n (!callback || callback(value, element, thisStyle, context)) &&\n value != 'inherit' &&\n value.indexOf('expression') < 0 &&\n name.substr(0, 1) != '-' &&\n this.defaultStyleValues[name] != value &&\n ((isInheritable && value != thisStyle[name]) ||\n (!isInheritable && value != 'initial' && value != 'normal'));\n if (keep && isInheritable) {\n thisStyle[name] = value;\n }\n\n if (!keep) {\n delete styles[name];\n }\n });\n\n setStyles(element, styles);\n }\n\n private processAttributes(element: HTMLElement, context: Object) {\n for (let i = element.attributes.length - 1; i >= 0; i--) {\n let attribute = element.attributes[i];\n let name = attribute.name.toLowerCase().trim();\n let value = attribute.value;\n let callback = this.attributeCallbacks[name];\n\n let newValue = callback\n ? callback(value, element, context)\n : this.allowedAttributes.indexOf(name) >= 0 || name.indexOf('data-') == 0\n ? value\n : null;\n\n if (name == 'class' && this.allowedCssClassesRegex) {\n newValue = this.processCssClass(value, newValue);\n }\n\n if (\n newValue === null ||\n newValue === undefined ||\n newValue.match(/s\\n*c\\n*r\\n*i\\n*p\\n*t\\n*:/i) // match script: with any NewLine inside. Browser will ignore those NewLine char and still treat it as script prefix\n ) {\n element.removeAttribute(name);\n } else {\n attribute.value = newValue;\n }\n }\n }\n\n private processCssClass(originalValue: string, calculatedValue: string): string {\n const originalClasses = originalValue ? originalValue.split(' ') : [];\n const calculatedClasses = calculatedValue ? calculatedValue.split(' ') : [];\n\n originalClasses.forEach(className => {\n if (\n this.allowedCssClassesRegex.test(className) &&\n calculatedClasses.indexOf(className) < 0\n ) {\n calculatedClasses.push(className);\n }\n });\n\n return calculatedClasses.length > 0 ? calculatedClasses.join(' ') : null;\n }\n}\n\nconst trustedTypes = (window).trustedTypes;\nconst policy = trustedTypes?.createPolicy('roosterjsUnsafeConvertHTML', {\n // This is unsafe. However, we only use this function for HtmlSanitizer which we will\n // sanitize HTML tree by our own code. So we just directly return the HTML string here.\n createHTML: (html: string) => html,\n});\n\nconst unsafeConvertToTrustedHTML = policy\n ? (html: string) => policy.createHTML(html)\n : (html: string) => html;\n","import { cloneObject } from './cloneObject';\nimport { CssStyleCallbackMap, StringMap } from 'roosterjs-editor-types';\n\nconst HTML_TAG_REPLACEMENT: Record = {\n // Allowed tags\n a: '*',\n abbr: '*',\n address: '*',\n area: '*',\n article: '*',\n aside: '*',\n b: '*',\n bdi: '*',\n bdo: '*',\n blockquote: '*',\n body: '*',\n br: '*',\n button: '*',\n canvas: '*',\n caption: '*',\n center: '*',\n cite: '*',\n code: '*',\n col: '*',\n colgroup: '*',\n data: '*',\n datalist: '*',\n dd: '*',\n del: '*',\n details: '*',\n dfn: '*',\n dialog: '*',\n dir: '*',\n div: '*',\n dl: '*',\n dt: '*',\n em: '*',\n fieldset: '*',\n figcaption: '*',\n figure: '*',\n font: '*',\n footer: '*',\n h1: '*',\n h2: '*',\n h3: '*',\n h4: '*',\n h5: '*',\n h6: '*',\n head: '*',\n header: '*',\n hgroup: '*',\n hr: '*',\n html: '*',\n i: '*',\n img: '*',\n input: '*',\n ins: '*',\n kbd: '*',\n label: '*',\n legend: '*',\n li: '*',\n main: '*',\n map: '*',\n mark: '*',\n menu: '*',\n menuitem: '*',\n meter: '*',\n nav: '*',\n ol: '*',\n optgroup: '*',\n option: '*',\n output: '*',\n p: '*',\n picture: '*',\n pre: '*',\n progress: '*',\n q: '*',\n rp: '*',\n rt: '*',\n ruby: '*',\n s: '*',\n samp: '*',\n section: '*',\n select: '*',\n small: '*',\n span: '*',\n strike: '*',\n strong: '*',\n sub: '*',\n summary: '*',\n sup: '*',\n table: '*',\n tbody: '*',\n td: '*',\n template: '*',\n textarea: '*',\n tfoot: '*',\n th: '*',\n thead: '*',\n time: '*',\n tr: '*',\n tt: '*',\n u: '*',\n ul: '*',\n var: '*',\n wbr: '*',\n xmp: '*',\n\n // Replaced tags:\n form: 'SPAN',\n\n // Disallowed tags\n applet: null,\n audio: null,\n base: null,\n basefont: null,\n embed: null,\n frame: null,\n frameset: null,\n iframe: null,\n link: null,\n meta: null,\n noscript: null,\n object: null,\n param: null,\n script: null,\n slot: null,\n source: null,\n style: null,\n title: null,\n track: null,\n video: null,\n};\n\nconst ALLOWED_HTML_ATTRIBUTES = (\n 'accept,align,alt,checked,cite,color,cols,colspan,contextmenu,' +\n 'coords,datetime,default,dir,dirname,disabled,download,face,headers,height,hidden,high,href,' +\n 'hreflang,ismap,kind,label,lang,list,low,max,maxlength,media,min,multiple,open,optimum,pattern,' +\n 'placeholder,readonly,rel,required,reversed,rows,rowspan,scope,selected,shape,size,sizes,span,' +\n 'spellcheck,src,srclang,srcset,start,step,style,tabindex,target,title,translate,type,usemap,valign,value,' +\n 'width,wrap'\n).split(',');\n\nconst DEFAULT_STYLE_VALUES: { [name: string]: string } = {\n 'background-color': 'transparent',\n 'border-bottom-color': 'rgb(0, 0, 0)',\n 'border-bottom-style': 'none',\n 'border-bottom-width': '0px',\n 'border-image-outset': '0',\n 'border-image-repeat': 'stretch',\n 'border-image-slice': '100%',\n 'border-image-source': 'none',\n 'border-image-width': '1',\n 'border-left-color': 'rgb(0, 0, 0)',\n 'border-left-style': 'none',\n 'border-left-width': '0px',\n 'border-right-color': 'rgb(0, 0, 0)',\n 'border-right-style': 'none',\n 'border-right-width': '0px',\n 'border-top-color': 'rgb(0, 0, 0)',\n 'border-top-style': 'none',\n 'border-top-width': '0px',\n 'outline-color': 'transparent',\n 'outline-style': 'none',\n 'outline-width': '0px',\n overflow: 'visible',\n 'text-decoration': 'none',\n '-webkit-text-stroke-width': '0px',\n 'word-wrap': 'break-word',\n 'margin-left': '0px',\n 'margin-right': '0px',\n padding: '0px',\n 'padding-top': '0px',\n 'padding-left': '0px',\n 'padding-right': '0px',\n 'padding-bottom': '0px',\n border: '0px',\n 'border-top': '0px',\n 'border-left': '0px',\n 'border-right': '0px',\n 'border-bottom': '0px',\n 'vertical-align': 'baseline',\n float: 'none',\n};\n\n// This is to preserve entity related CSS classes when paste.\nconst ALLOWED_CSS_CLASSES: string[] = [];\n\n/**\n * @internal\n */\nexport function getTagReplacement(\n additionalReplacements: Record\n): Record {\n const result = { ...HTML_TAG_REPLACEMENT };\n const replacements = additionalReplacements || {};\n Object.keys(replacements).forEach(key => {\n if (key) {\n result[key.toLowerCase()] = replacements[key];\n }\n });\n\n return result;\n}\n\n/**\n * @internal\n */\nexport function getAllowedAttributes(additionalAttributes: string[]): string[] {\n return unique(ALLOWED_HTML_ATTRIBUTES.concat(additionalAttributes || [])).map(attr =>\n attr.toLocaleLowerCase()\n );\n}\n\n/**\n * @internal\n */\nexport function getAllowedCssClassesRegex(additionalCssClasses: string[]): RegExp {\n const patterns = ALLOWED_CSS_CLASSES.concat(additionalCssClasses || []);\n return patterns.length > 0 ? new RegExp(patterns.join('|')) : null;\n}\n\n/**\n * @internal\n */\nexport function getDefaultStyleValues(additionalDefaultStyles: StringMap): StringMap {\n let result = cloneObject(DEFAULT_STYLE_VALUES);\n if (additionalDefaultStyles) {\n Object.keys(additionalDefaultStyles).forEach(name => {\n let value = additionalDefaultStyles[name];\n if (value !== null && value !== undefined) {\n result[name] = value;\n } else {\n delete result[name];\n }\n });\n }\n\n return result;\n}\n\n/**\n * @internal\n */\nexport function getStyleCallbacks(callbacks: CssStyleCallbackMap): CssStyleCallbackMap {\n let result = cloneObject(callbacks);\n result.position = result.position || removeValue;\n result.width = result.width || removeWidthForLiAndDiv;\n return result;\n}\n\nfunction removeValue(): null {\n return null;\n}\n\nfunction removeWidthForLiAndDiv(value: string, element: HTMLElement) {\n let tag = element.tagName;\n return !(tag == 'LI' || tag == 'DIV');\n}\n\nfunction unique(array: T[]): T[] {\n return array.filter((value, index, self) => self.indexOf(value) == index);\n}\n","import { HtmlSanitizerOptions } from 'roosterjs-editor-types';\n\n/**\n * Create default value of HtmlSanitizerOptions with every property set\n */\nexport default function createDefaultHtmlSanitizerOptions(): Required {\n return {\n elementCallbacks: {},\n attributeCallbacks: {},\n cssStyleCallbacks: {},\n additionalTagReplacements: {},\n additionalAllowedAttributes: [],\n additionalAllowedCssClasses: [],\n additionalDefaultStyleValues: {},\n additionalGlobalStyleNodes: [],\n additionalPredefinedCssForElement: {},\n preserveHtmlComments: false,\n unknownTagReplacement: null,\n };\n}\n","/**\n * Chain all callback for an attribute together\n * @param map The source callback map\n * @param name Name of the property to chain\n * @param newCallback A new callback to process the given name on the given map.\n * If the same property got multiple callbacks, the final return value will be the return\n * value of the latest callback\n */\nexport default function chainSanitizerCallback R, R>(\n map: Record R>,\n name: string,\n newCallback: TChainedFn\n) {\n if (!map[name]) {\n map[name] = newCallback;\n } else {\n const originalCallback = map[name];\n map[name] = (...args: TOriginalArgs) => {\n originalCallback(...args);\n return newCallback(...args);\n };\n }\n}\n","import { EntityClasses } from 'roosterjs-editor-types';\n\nconst CONTENT_EDITABLE = 'contenteditable';\n\n/**\n * Commit information of an entity (type, isReadonly, id) into the wrapper node as CSS Classes\n * @param wrapper The entity wrapper element\n * @param type Entity type\n * @param isReadonly Whether this is a readonly entity\n * @param id Optional Id of the entity\n */\nexport default function commitEntity(\n wrapper: HTMLElement,\n type: string,\n isReadonly: boolean,\n id?: string\n) {\n if (wrapper) {\n wrapper.className = `${EntityClasses.ENTITY_INFO_NAME} ${\n EntityClasses.ENTITY_TYPE_PREFIX\n }${type} ${id ? `${EntityClasses.ENTITY_ID_PREFIX}${id} ` : ''}${\n EntityClasses.ENTITY_READONLY_PREFIX\n }${isReadonly ? '1' : '0'}`;\n\n if (isReadonly) {\n wrapper.contentEditable = 'false';\n } else if (wrapper.getAttribute(CONTENT_EDITABLE)) {\n wrapper.removeAttribute(CONTENT_EDITABLE);\n }\n }\n}\n","import { Entity, EntityClasses } from 'roosterjs-editor-types';\n\n/**\n * Get Entity object from an entity root element\n * @param element The entity root element. If this element is not an entity root element,\n * it will return null\n */\nexport default function getEntityFromElement(element: HTMLElement): Entity {\n let isEntity = false;\n let type: string;\n let id = '';\n let isReadonly = false;\n\n element?.className?.split(' ').forEach(name => {\n if (name == EntityClasses.ENTITY_INFO_NAME) {\n isEntity = true;\n } else if (name.indexOf(EntityClasses.ENTITY_TYPE_PREFIX) == 0) {\n type = name.substr(EntityClasses.ENTITY_TYPE_PREFIX.length);\n } else if (name.indexOf(EntityClasses.ENTITY_ID_PREFIX) == 0) {\n id = name.substr(EntityClasses.ENTITY_ID_PREFIX.length);\n } else if (name.indexOf(EntityClasses.ENTITY_READONLY_PREFIX) == 0) {\n isReadonly = name.substr(EntityClasses.ENTITY_READONLY_PREFIX.length) == '1';\n }\n });\n\n return isEntity\n ? {\n wrapper: element,\n id,\n type,\n isReadonly,\n }\n : null;\n}\n","import { EntityClasses } from 'roosterjs-editor-types';\n\n/**\n * @internal Get a selector string for specified entity type and id\n * @param type (Optional) Type of entity\n * @param id (Optional) Id of entity\n */\nexport default function getEntitySelector(type?: string, id?: string): string {\n const typeSelector = type ? `.${EntityClasses.ENTITY_TYPE_PREFIX}${type}` : '';\n const idSelector = id ? `.${EntityClasses.ENTITY_ID_PREFIX}${id}` : '';\n return '.' + EntityClasses.ENTITY_INFO_NAME + typeSelector + idSelector;\n}\n","import { PluginEvent } from 'roosterjs-editor-types';\n\n/**\n * Gets the cached event data by cache key from event object if there is already one.\n * Otherwise, call getter function to create one, and cache it.\n * @param event The event object\n * @param key Cache key string, need to be unique\n * @param getter Getter function to get the object when it is not in cache yet\n */\nexport default function cacheGetEventData(event: PluginEvent, key: string, getter: () => T): T {\n let result =\n event && event.eventDataCache && event.eventDataCache.hasOwnProperty(key)\n ? event.eventDataCache[key]\n : getter();\n if (event) {\n event.eventDataCache = event.eventDataCache || {};\n event.eventDataCache[key] = result;\n }\n\n return result;\n}\n","import { PluginEvent } from 'roosterjs-editor-types';\n\n/**\n * Clear a cached object by its key from an event object\n * @param event The event object\n * @param key The cache key\n */\nexport default function clearEventDataCache(event: PluginEvent, key?: string): void {\n if (event && event.eventDataCache) {\n if (key && event.eventDataCache.hasOwnProperty(key)) {\n delete event.eventDataCache[key];\n } else if (!key) {\n event.eventDataCache = {};\n }\n }\n}\n","import isModifierKey from './isModifierKey';\n\n/**\n * Returns true when the event was fired from a key that produces a character value, otherwise false\n * This detection is not 100% accurate. event.key is not fully supported by all browsers, and in some browsers (e.g. IE),\n * event.key is longer than 1 for num pad input. But here we just want to improve performance as much as possible.\n * So if we missed some case here it is still acceptable.\n * @param event The keyboard event object\n */\nexport default function isCharacterValue(event: KeyboardEvent): boolean {\n return !isModifierKey(event) && event.key && event.key.length == 1;\n}\n","import { Browser } from '../utils/Browser';\n\n/**\n * Check if Ctrl key (Windows) or Meta key (Mac) is pressed for the given Event\n * @param event A Keyboard event or Mouse event object\n * @returns True if Ctrl key is pressed on Windows or Meta key is pressed on Mac\n */\nconst isCtrlOrMetaPressed: (event: KeyboardEvent | MouseEvent) => boolean = Browser.isMac\n ? event => event.metaKey\n : event => event.ctrlKey;\nexport default isCtrlOrMetaPressed;\n","import {\n AttachDomEvent,\n DOMEventHandler,\n DOMEventHandlerObject,\n EditorCore,\n PluginDomEvent,\n} from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Attach a DOM event to the editor content DIV\n * @param core The EditorCore object\n * @param eventName The DOM event name\n * @param pluginEventType Optional event type. When specified, editor will trigger a plugin event with this name when the DOM event is triggered\n * @param beforeDispatch Optional callback function to be invoked when the DOM event is triggered before trigger plugin event\n */\nexport const attachDomEvent: AttachDomEvent = (\n core: EditorCore,\n eventMap: Record\n) => {\n const disposers = Object.keys(eventMap || {}).map(eventName => {\n const { pluginEventType, beforeDispatch } = extractHandler(eventMap[eventName]);\n let onEvent = (event: UIEvent) => {\n if (beforeDispatch) {\n beforeDispatch(event);\n }\n if (pluginEventType != null) {\n core.api.triggerEvent(\n core,\n {\n eventType: pluginEventType,\n rawEvent: event,\n },\n false /*broadcast*/\n );\n }\n };\n core.contentDiv.addEventListener(eventName, onEvent);\n return () => {\n core.contentDiv.removeEventListener(eventName, onEvent);\n };\n });\n return () => disposers.forEach(disposers => disposers());\n};\n\nfunction extractHandler(handlerObj: DOMEventHandler): DOMEventHandlerObject {\n let result: DOMEventHandlerObject = {\n pluginEventType: null,\n beforeDispatch: null,\n };\n\n if (typeof handlerObj === 'number') {\n result.pluginEventType = handlerObj;\n } else if (typeof handlerObj === 'function') {\n result.beforeDispatch = handlerObj;\n } else if (typeof handlerObj === 'object') {\n result = handlerObj;\n }\n return result;\n}\n","import {\n applyFormat,\n applyTextStyle,\n createDefaultHtmlSanitizerOptions,\n getInheritableStyles,\n getTagOfNode,\n HtmlSanitizer,\n moveChildNodes,\n toArray,\n wrap,\n} from 'roosterjs-editor-dom';\nimport {\n BeforePasteEvent,\n ClipboardData,\n CreatePasteFragment,\n EditorCore,\n PluginEventType,\n DefaultFormat,\n NodePosition,\n} from 'roosterjs-editor-types';\n\nconst START_FRAGMENT = '';\nconst END_FRAGMENT = '';\nconst NBSP_HTML = '\\u00A0';\n\n/**\n * @internal\n * Create a DocumentFragment for paste from a ClipboardData\n * @param core The EditorCore object.\n * @param clipboardData Clipboard data retrieved from clipboard\n * @param position The position to paste to\n * @param pasteAsText True to force use plain text as the content to paste, false to choose HTML or Image if any\n * @param applyCurrentStyle True if apply format of current selection to the pasted content,\n * false to keep original format\n */\nexport const createPasteFragment: CreatePasteFragment = (\n core: EditorCore,\n clipboardData: ClipboardData,\n position: NodePosition,\n pasteAsText: boolean,\n applyCurrentStyle: boolean\n) => {\n if (!clipboardData) {\n return null;\n }\n\n // Step 1: Prepare BeforePasteEvent object\n const event = createBeforePasteEvent(core, clipboardData);\n const { fragment, sanitizingOption } = event;\n const { rawHtml, text, imageDataUri } = clipboardData;\n const document = core.contentDiv.ownerDocument;\n let doc: HTMLDocument;\n\n // Step 2: Retrieve Metadata from Html and the Html that was copied.\n if (\n rawHtml &&\n (doc = new DOMParser().parseFromString(core.trustedHTMLHandler(rawHtml), 'text/html'))?.body\n ) {\n const attributes = doc.querySelector('html')?.attributes;\n (attributes ? toArray(attributes) : []).reduce((attrs, attr) => {\n attrs[attr.name] = attr.value;\n return attrs;\n }, event.htmlAttributes);\n toArray(doc.querySelectorAll('meta')).reduce((attrs, meta) => {\n attrs[meta.name] = meta.content;\n return attrs;\n }, event.htmlAttributes);\n\n clipboardData.htmlFirstLevelChildTags = [];\n doc?.body.childNodes.forEach(node =>\n clipboardData.htmlFirstLevelChildTags.push(getTagOfNode(node))\n );\n\n // Move all STYLE nodes into header, and save them into sanitizing options.\n // Because if we directly move them into a fragment, all sheets under STYLE will be lost.\n processStyles(doc, style => {\n doc.head.appendChild(style);\n sanitizingOption.additionalGlobalStyleNodes.push(style);\n });\n\n const startIndex = rawHtml.indexOf(START_FRAGMENT);\n const endIndex = rawHtml.lastIndexOf(END_FRAGMENT);\n\n if (startIndex >= 0 && endIndex >= startIndex + START_FRAGMENT.length) {\n event.htmlBefore = rawHtml.substr(0, startIndex);\n event.htmlAfter = rawHtml.substr(endIndex + END_FRAGMENT.length);\n clipboardData.html = rawHtml.substring(startIndex + START_FRAGMENT.length, endIndex);\n doc.body.innerHTML = core.trustedHTMLHandler(clipboardData.html);\n\n // Remove style nodes just added by setting innerHTML of body since we already have all\n // style nodes in header.\n // Here we use doc.body instead of doc because we only want to remove STYLE nodes under BODY\n // and the nodes under HEAD are still used when convert global CSS to inline\n processStyles(doc.body, style => style.parentNode?.removeChild(style));\n }\n }\n\n // Step 3: Fill the BeforePasteEvent object, especially the fragment for paste\n if (!pasteAsText && !text && imageDataUri) {\n // Paste image\n const img = document.createElement('img');\n img.style.maxWidth = '100%';\n img.src = imageDataUri;\n fragment.appendChild(img);\n } else if (!pasteAsText && rawHtml && doc ? doc.body : false) {\n moveChildNodes(fragment, doc.body);\n\n if (applyCurrentStyle && position) {\n const format = getCurrentFormat(core, position.node);\n applyTextStyle(fragment, node => applyFormat(node, format));\n }\n } else if (text) {\n // Paste text\n text.split('\\n').forEach((line, index, lines) => {\n line = line\n .replace(/^ /g, NBSP_HTML)\n .replace(/\\r/g, '')\n .replace(/ {2}/g, ' ' + NBSP_HTML);\n const textNode = document.createTextNode(line);\n\n // There are 3 scenarios:\n // 1. Single line: Paste as it is\n // 2. Two lines: Add
between the lines\n // 3. 3 or More lines, For first and last line, paste as it is. For middle lines, wrap with DIV, and add BR if it is empty line\n if (lines.length == 2 && index == 0) {\n // 1 of 2 lines scenario, add BR\n fragment.appendChild(textNode);\n fragment.appendChild(document.createElement('br'));\n } else if (index > 0 && index < lines.length - 1) {\n // Middle line of >=3 lines scenario, wrap with DIV\n fragment.appendChild(wrap(line == '' ? document.createElement('br') : textNode));\n } else {\n // All others, paste as it is\n fragment.appendChild(textNode);\n }\n });\n }\n\n // Step 4: Trigger BeforePasteEvent so that plugins can do proper change before paste\n core.api.triggerEvent(core, event, true /*broadcast*/);\n\n // Step 5. Sanitize the fragment before paste to make sure the content is safe\n const sanitizer = new HtmlSanitizer(event.sanitizingOption);\n\n sanitizer.convertGlobalCssToInlineCss(fragment);\n sanitizer.sanitize(fragment, position && getInheritableStyles(position.element));\n\n return fragment;\n};\n\nfunction getCurrentFormat(core: EditorCore, node: Node): DefaultFormat {\n const pendableFormat = core.api.getPendableFormatState(core, true /** forceGetStateFromDOM*/);\n const styleBasedFormat = core.api.getStyleBasedFormatState(core, node);\n return {\n fontFamily: styleBasedFormat.fontName,\n fontSize: styleBasedFormat.fontSize,\n textColor: styleBasedFormat.textColor,\n backgroundColor: styleBasedFormat.backgroundColor,\n textColors: styleBasedFormat.textColors,\n backgroundColors: styleBasedFormat.backgroundColors,\n bold: pendableFormat.isBold,\n italic: pendableFormat.isItalic,\n underline: pendableFormat.isUnderline,\n };\n}\n\nfunction createBeforePasteEvent(core: EditorCore, clipboardData: ClipboardData): BeforePasteEvent {\n return {\n eventType: PluginEventType.BeforePaste,\n clipboardData,\n fragment: core.contentDiv.ownerDocument.createDocumentFragment(),\n sanitizingOption: createDefaultHtmlSanitizerOptions(),\n htmlBefore: '',\n htmlAfter: '',\n htmlAttributes: {},\n };\n}\n\nfunction processStyles(node: ParentNode, callback: (style: HTMLStyleElement) => void) {\n toArray(node.querySelectorAll('style')).forEach(callback);\n}\n","import {\n ContentPosition,\n EditorCore,\n EnsureTypeInContainer,\n KnownCreateElementDataIndex,\n NodePosition,\n PositionType,\n} from 'roosterjs-editor-types';\nimport {\n applyFormat,\n createElement,\n createRange,\n getBlockElementAtNode,\n isNodeEmpty,\n Position,\n safeInstanceOf,\n} from 'roosterjs-editor-dom';\n\n/**\n * @internal\n * When typing goes directly under content div, many things can go wrong\n * We fix it by wrapping it with a div and reposition cursor within the div\n */\nexport const ensureTypeInContainer: EnsureTypeInContainer = (\n core: EditorCore,\n position: NodePosition,\n keyboardEvent?: KeyboardEvent\n) => {\n position = position.normalize();\n const block = getBlockElementAtNode(core.contentDiv, position.node);\n let formatNode: HTMLElement;\n\n if (block) {\n formatNode = block.collapseToSingleElement();\n\n // if the block is empty, apply default format\n // Otherwise, leave it as it is as we don't want to change the style for existing data\n // unless the block was just created by the keyboard event (e.g. ctrl+a & start typing)\n const shouldSetNodeStyles =\n isNodeEmpty(formatNode) ||\n (keyboardEvent && wasNodeJustCreatedByKeyboardEvent(keyboardEvent, formatNode));\n formatNode = formatNode && shouldSetNodeStyles ? formatNode : null;\n } else {\n // Only reason we don't get the selection block is that we have an empty content div\n // which can happen when users removes everything (i.e. select all and DEL, or backspace from very end to begin)\n // The fix is to add a DIV wrapping, apply default format and move cursor over\n formatNode = createElement(\n KnownCreateElementDataIndex.EmptyLine,\n core.contentDiv.ownerDocument\n ) as HTMLElement;\n core.api.insertNode(core, formatNode, {\n position: ContentPosition.End,\n updateCursor: false,\n replaceSelection: false,\n insertOnNewLine: false,\n });\n\n // element points to a wrapping node we added \"

\". We should move the selection left to
\n position = new Position(formatNode.firstChild, PositionType.Begin);\n }\n\n if (formatNode) {\n applyFormat(formatNode, core.lifecycle.defaultFormat, core.lifecycle.isDarkMode);\n }\n\n // If this is triggered by a keyboard event, let's select the new position\n if (keyboardEvent) {\n core.api.selectRange(core, createRange(position));\n }\n};\n\nfunction wasNodeJustCreatedByKeyboardEvent(event: KeyboardEvent, formatNode: HTMLElement) {\n return (\n safeInstanceOf(event.target, 'Node') &&\n event.target.contains(formatNode) &&\n event.key === formatNode.innerText\n );\n}\n","import { createRange, getFirstLeafNode } from 'roosterjs-editor-dom';\nimport { EditorCore, Focus, PositionType } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Focus to editor. If there is a cached selection range, use it as current selection\n * @param core The EditorCore object\n */\nexport const focus: Focus = (core: EditorCore) => {\n if (!core.lifecycle.shadowEditFragment) {\n if (\n !core.api.hasFocus(core) ||\n !core.api.getSelectionRange(core, false /*tryGetFromCache*/)\n ) {\n // Focus (document.activeElement indicates) and selection are mostly in sync, but could be out of sync in some extreme cases.\n // i.e. if you programmatically change window selection to point to a non-focusable DOM element (i.e. tabindex=-1 etc.).\n // On Chrome/Firefox, it does not change document.activeElement. On Edge/IE, it change document.activeElement to be body\n // Although on Chrome/Firefox, document.activeElement points to editor, you cannot really type which we don't want (no cursor).\n // So here we always do a live selection pull on DOM and make it point in Editor. The pitfall is, the cursor could be reset\n // to very begin to of editor since we don't really have last saved selection (created on blur which does not fire in this case).\n // It should be better than the case you cannot type\n if (\n !core.domEvent.selectionRange ||\n !core.api.selectRange(core, core.domEvent.selectionRange, true /*skipSameRange*/)\n ) {\n let node = getFirstLeafNode(core.contentDiv) || core.contentDiv;\n core.api.selectRange(\n core,\n createRange(node, PositionType.Begin),\n true /*skipSameRange*/\n );\n }\n }\n\n // remember to clear cached selection range\n core.domEvent.selectionRange = null;\n\n // This is more a fallback to ensure editor gets focus if it didn't manage to move focus to editor\n if (!core.api.hasFocus(core)) {\n core.contentDiv.focus();\n }\n }\n};\n","import {\n ColorTransformDirection,\n EditorCore,\n GetContent,\n GetContentMode,\n PluginEventType,\n} from 'roosterjs-editor-types';\nimport {\n createRange,\n getHtmlWithSelectionPath,\n getSelectionPath,\n getTextContent,\n safeInstanceOf,\n} from 'roosterjs-editor-dom';\n\n/**\n * @internal\n * Get current editor content as HTML string\n * @param core The EditorCore object\n * @param mode specify what kind of HTML content to retrieve\n * @returns HTML string representing current editor content\n */\nexport const getContent: GetContent = (core: EditorCore, mode: GetContentMode): string => {\n let content = '';\n const triggerExtractContentEvent = mode == GetContentMode.CleanHTML;\n const includeSelectionMarker = mode == GetContentMode.RawHTMLWithSelection;\n\n // When there is fragment for shadow edit, always use the cached fragment as document since HTML node in editor\n // has been changed by uncommitted shadow edit which should be ignored.\n const root = core.lifecycle.shadowEditFragment || core.contentDiv;\n\n if (mode == GetContentMode.PlainText) {\n content = getTextContent(root);\n } else if (triggerExtractContentEvent || core.lifecycle.isDarkMode) {\n const clonedRoot = cloneNode(root);\n clonedRoot.normalize();\n\n const originalRange = core.api.getSelectionRange(core, true /*tryGetFromCache*/);\n const path = !includeSelectionMarker\n ? null\n : core.lifecycle.shadowEditFragment\n ? core.lifecycle.shadowEditSelectionPath\n : originalRange\n ? getSelectionPath(core.contentDiv, originalRange)\n : null;\n const range = path && createRange(clonedRoot, path.start, path.end);\n\n if (core.lifecycle.isDarkMode) {\n core.api.transformColor(\n core,\n clonedRoot,\n false /*includeSelf*/,\n null /*callback*/,\n ColorTransformDirection.DarkToLight\n );\n }\n\n if (triggerExtractContentEvent) {\n core.api.triggerEvent(\n core,\n {\n eventType: PluginEventType.ExtractContentWithDom,\n clonedRoot,\n },\n true /*broadcast*/\n );\n\n content = clonedRoot.innerHTML;\n } else if (range) {\n // range is not null, which means we want to include a selection path in the content\n content = getHtmlWithSelectionPath(clonedRoot, range);\n } else {\n content = clonedRoot.innerHTML;\n }\n } else {\n content = getHtmlWithSelectionPath(\n root,\n includeSelectionMarker && core.api.getSelectionRange(core, true /*tryGetFromCache*/)\n );\n }\n\n return content;\n};\n\nfunction cloneNode(node: HTMLElement | DocumentFragment): HTMLElement {\n let clonedNode: HTMLElement;\n if (safeInstanceOf(node, 'DocumentFragment')) {\n clonedNode = node.ownerDocument.createElement('div');\n clonedNode.appendChild(node.cloneNode(true /*deep*/));\n } else {\n clonedNode = node.cloneNode(true /*deep*/) as HTMLElement;\n }\n\n return clonedNode;\n}\n","import { contains, getTagOfNode, PendableFormatNames, Position } from 'roosterjs-editor-dom';\nimport { EditorCore, NodePosition, NodeType, PendableFormatState } from 'roosterjs-editor-types';\n\n/**\n * @param core The EditorCore object\n * @param forceGetStateFromDOM If set to true, will force get the format state from DOM tree.\n * @returns The cached format state if it exists. If the cached postion do not exist, search for pendable elements in the DOM tree and return the pendable format state.\n */\nexport function getPendableFormatState(\n core: EditorCore,\n forceGetStateFromDOM: boolean = false\n): PendableFormatState {\n const range = core.api.getSelectionRange(core, true /* tryGetFromCache*/);\n const isRangeCollapsed = range && range.collapsed;\n const cachedPendableFormatState = core.pendingFormatState.pendableFormatState;\n const cachedPosition = core.pendingFormatState.pendableFormatPosition?.normalize();\n const currentPosition = isRangeCollapsed && Position.getStart(range).normalize();\n const isSamePosition = currentPosition && currentPosition.equalTo(cachedPosition);\n\n if (range && cachedPendableFormatState && isSamePosition && !forceGetStateFromDOM) {\n return cachedPendableFormatState;\n } else {\n return queryCommandStateFromDOM(core, isRangeCollapsed, currentPosition);\n }\n}\n\nconst PendableStyleCheckers: Record<\n PendableFormatNames,\n (tagName: string, style: CSSStyleDeclaration) => boolean\n> = {\n isBold: (tag, style) =>\n tag == 'B' ||\n tag == 'STRONG' ||\n parseInt(style.fontWeight) >= 700 ||\n ['bold', 'bolder'].indexOf(style.fontWeight) >= 0,\n isUnderline: (tag, style) => tag == 'U' || style.textDecoration.indexOf('underline') >= 0,\n isItalic: (tag, style) => tag == 'I' || tag == 'EM' || style.fontStyle === 'italic',\n isSubscript: (tag, style) => tag == 'SUB' || style.verticalAlign === 'sub',\n isSuperscript: (tag, style) => tag == 'SUP' || style.verticalAlign === 'super',\n isStrikeThrough: (tag, style) =>\n tag == 'S' || tag == 'STRIKE' || style.textDecoration.indexOf('line-through') >= 0,\n};\n\n/**\n * CssFalsyCheckers checks for non pendable format that might overlay a pendable format, then it can prevent getPendableFormatState return falsy pendable format states.\n */\n\nconst CssFalsyCheckers: Record boolean> = {\n isBold: style =>\n (style.fontWeight !== '' && parseInt(style.fontWeight) < 700) ||\n style.fontWeight === 'normal',\n isUnderline: style =>\n style.textDecoration !== '' && style.textDecoration.indexOf('underline') < 0,\n isItalic: style => style.fontStyle !== '' && style.fontStyle !== 'italic',\n isSubscript: style => style.verticalAlign !== '' && style.verticalAlign !== 'sub',\n isSuperscript: style => style.verticalAlign !== '' && style.verticalAlign !== 'super',\n isStrikeThrough: style =>\n style.textDecoration !== '' && style.textDecoration.indexOf('line-through') < 0,\n};\n\nfunction queryCommandStateFromDOM(\n core: EditorCore,\n isCollapsed: boolean,\n currentPosition: NodePosition\n): PendableFormatState {\n let node = isCollapsed ? currentPosition?.node : document.getSelection().focusNode;\n let formatState: PendableFormatState = {};\n let pendablekeys: PendableFormatNames[] = [];\n while (contains(core.contentDiv, node)) {\n const tag = getTagOfNode(node);\n const style = node.nodeType == NodeType.Element && (node as HTMLElement).style;\n if (tag && style) {\n Object.keys(PendableStyleCheckers).forEach((key: PendableFormatNames) => {\n if (!(pendablekeys.indexOf(key) >= 0)) {\n formatState[key] = formatState[key] || PendableStyleCheckers[key](tag, style);\n if (CssFalsyCheckers[key](style)) {\n pendablekeys.push(key);\n }\n }\n });\n }\n node = node.parentNode;\n }\n return formatState;\n}\n","import { contains, createRange } from 'roosterjs-editor-dom';\nimport { EditorCore, GetSelectionRange } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Get current or cached selection range\n * @param core The EditorCore object\n * @param tryGetFromCache Set to true to retrieve the selection range from cache if editor doesn't own the focus now\n * @returns A Range object of the selection range\n */\nexport const getSelectionRange: GetSelectionRange = (\n core: EditorCore,\n tryGetFromCache: boolean\n) => {\n let result: Range = null;\n\n if (core.lifecycle.shadowEditFragment) {\n result =\n core.lifecycle.shadowEditSelectionPath &&\n createRange(\n core.contentDiv,\n core.lifecycle.shadowEditSelectionPath.start,\n core.lifecycle.shadowEditSelectionPath.end\n );\n\n return result;\n } else {\n if (!tryGetFromCache || core.api.hasFocus(core)) {\n let selection = core.contentDiv.ownerDocument.defaultView?.getSelection();\n if (selection && selection.rangeCount > 0) {\n let range = selection.getRangeAt(0);\n if (contains(core.contentDiv, range)) {\n result = range;\n }\n }\n }\n\n if (!result && tryGetFromCache) {\n result = core.domEvent.selectionRange;\n }\n\n return result;\n }\n};\n","import { DarkModeDatasetNames, EditorCore, GetStyleBasedFormatState } from 'roosterjs-editor-types';\nimport { findClosestElementAncestor, getComputedStyles } from 'roosterjs-editor-dom';\n\nconst ORIGINAL_STYLE_COLOR_SELECTOR = `[data-${DarkModeDatasetNames.OriginalStyleColor}],[data-${DarkModeDatasetNames.OriginalAttributeColor}]`;\nconst ORIGINAL_STYLE_BACK_COLOR_SELECTOR = `[data-${DarkModeDatasetNames.OriginalStyleBackgroundColor}],[data-${DarkModeDatasetNames.OriginalAttributeBackgroundColor}]`;\n\n/**\n * @internal\n * Get style based format state from current selection, including font name/size and colors\n * @param core The EditorCore objects\n * @param node The node to get style from\n */\nexport const getStyleBasedFormatState: GetStyleBasedFormatState = (\n core: EditorCore,\n node: Node\n) => {\n if (!node) {\n return {};\n }\n const styles = node ? getComputedStyles(node) : [];\n const isDarkMode = core.lifecycle.isDarkMode;\n const root = core.contentDiv;\n const ogTextColorNode =\n isDarkMode && findClosestElementAncestor(node, root, ORIGINAL_STYLE_COLOR_SELECTOR);\n const ogBackgroundColorNode =\n isDarkMode && findClosestElementAncestor(node, root, ORIGINAL_STYLE_BACK_COLOR_SELECTOR);\n\n return {\n fontName: styles[0],\n fontSize: styles[1],\n textColor: styles[2],\n backgroundColor: styles[3],\n textColors: ogTextColorNode\n ? {\n darkModeColor: styles[2],\n lightModeColor:\n ogTextColorNode.dataset[DarkModeDatasetNames.OriginalStyleColor] ||\n ogTextColorNode.dataset[DarkModeDatasetNames.OriginalAttributeColor],\n }\n : undefined,\n backgroundColors: ogBackgroundColorNode\n ? {\n darkModeColor: styles[3],\n lightModeColor:\n ogBackgroundColorNode.dataset[\n DarkModeDatasetNames.OriginalStyleBackgroundColor\n ] ||\n ogBackgroundColorNode.dataset[\n DarkModeDatasetNames.OriginalAttributeBackgroundColor\n ],\n }\n : undefined,\n };\n};\n","import {\n BlockElement,\n ContentPosition,\n ColorTransformDirection,\n EditorCore,\n InsertNode,\n InsertOption,\n NodeType,\n PositionType,\n} from 'roosterjs-editor-types';\nimport {\n createRange,\n getBlockElementAtNode,\n getFirstLastBlockElement,\n isBlockElement,\n isVoidHtmlElement,\n Position,\n safeInstanceOf,\n toArray,\n wrap,\n adjustInsertPosition,\n} from 'roosterjs-editor-dom';\n\nfunction getInitialRange(\n core: EditorCore,\n option: InsertOption\n): { range: Range; rangeToRestore: Range } {\n // Selection start replaces based on the current selection.\n // Range inserts based on a provided range.\n // Both have the potential to use the current selection to restore cursor position\n // So in both cases we need to store the selection state.\n let range = core.api.getSelectionRange(core, true /*tryGetFromCache*/);\n let rangeToRestore = null;\n if (option.position == ContentPosition.Range) {\n rangeToRestore = range;\n range = option.range;\n } else if (range) {\n rangeToRestore = range.cloneRange();\n }\n\n return { range, rangeToRestore };\n}\n\n/**\n * @internal\n * Insert a DOM node into editor content\n * @param core The EditorCore object. No op if null.\n * @param option An insert option object to specify how to insert the node\n */\nexport const insertNode: InsertNode = (core: EditorCore, node: Node, option: InsertOption) => {\n option = option || {\n position: ContentPosition.SelectionStart,\n insertOnNewLine: false,\n updateCursor: true,\n replaceSelection: true,\n };\n let contentDiv = core.contentDiv;\n\n if (option.updateCursor) {\n core.api.focus(core);\n }\n\n if (option.position == ContentPosition.Outside) {\n contentDiv.parentNode.insertBefore(node, contentDiv.nextSibling);\n return true;\n }\n\n core.api.transformColor(\n core,\n node,\n true /*includeSelf*/,\n () => {\n switch (option.position) {\n case ContentPosition.Begin:\n case ContentPosition.End: {\n let isBegin = option.position == ContentPosition.Begin;\n let block = getFirstLastBlockElement(contentDiv, isBegin);\n let insertedNode: Node | Node[];\n if (block) {\n let refNode = isBegin ? block.getStartNode() : block.getEndNode();\n if (\n option.insertOnNewLine ||\n refNode.nodeType == NodeType.Text ||\n isVoidHtmlElement(refNode)\n ) {\n // For insert on new line, or refNode is text or void html element (HR, BR etc.)\n // which cannot have children, i.e.
hello
world
. 'hello', 'world' are the\n // first and last node. Insert before 'hello' or after 'world', but still inside DIV\n if (safeInstanceOf(node, 'DocumentFragment')) {\n // if the node to be inserted is DocumentFragment, use its childNodes as insertedNode\n // because insertBefore() returns an empty DocumentFragment\n insertedNode = toArray(node.childNodes);\n refNode.parentNode.insertBefore(\n node,\n isBegin ? refNode : refNode.nextSibling\n );\n } else {\n insertedNode = refNode.parentNode.insertBefore(\n node,\n isBegin ? refNode : refNode.nextSibling\n );\n }\n } else {\n // if the refNode can have child, use appendChild (which is like to insert as first/last child)\n // i.e.
hello
, the content will be inserted before/after hello\n insertedNode = refNode.insertBefore(\n node,\n isBegin ? refNode.firstChild : null\n );\n }\n } else {\n // No first block, this can happen when editor is empty. Use appendChild to insert the content in contentDiv\n insertedNode = contentDiv.appendChild(node);\n }\n\n // Final check to see if the inserted node is a block. If not block and the ask is to insert on new line,\n // add a DIV wrapping\n if (insertedNode && option.insertOnNewLine) {\n const nodes = Array.isArray(insertedNode) ? insertedNode : [insertedNode];\n if (!isBlockElement(nodes[0]) || !isBlockElement(nodes[nodes.length - 1])) {\n wrap(nodes);\n }\n }\n\n break;\n }\n case ContentPosition.DomEnd:\n // Use appendChild to insert the node at the end of the content div.\n let insertedNode = contentDiv.appendChild(node);\n // Final check to see if the inserted node is a block. If not block and the ask is to insert on new line,\n // add a DIV wrapping\n if (insertedNode && option.insertOnNewLine && !isBlockElement(insertedNode)) {\n wrap(insertedNode);\n }\n break;\n case ContentPosition.Range:\n case ContentPosition.SelectionStart:\n let { range, rangeToRestore } = getInitialRange(core, option);\n if (!range) {\n return;\n }\n\n // if to replace the selection and the selection is not collapsed, remove the the content at selection first\n if (option.replaceSelection && !range.collapsed) {\n range.deleteContents();\n }\n\n let pos = Position.getStart(range);\n let blockElement: BlockElement;\n\n if (\n option.insertOnNewLine &&\n (blockElement = getBlockElementAtNode(contentDiv, pos.normalize().node))\n ) {\n pos = new Position(blockElement.getEndNode(), PositionType.After);\n } else {\n pos = adjustInsertPosition(contentDiv, node, pos, range);\n }\n\n let nodeForCursor =\n node.nodeType == NodeType.DocumentFragment ? node.lastChild : node;\n range = createRange(pos);\n range.insertNode(node);\n if (option.updateCursor && nodeForCursor) {\n rangeToRestore = createRange(\n new Position(nodeForCursor, PositionType.After).normalize()\n );\n }\n core.api.selectRange(core, rangeToRestore);\n\n break;\n }\n },\n ColorTransformDirection.LightToDark\n );\n\n return true;\n};\n","import { EditorCore, RestoreUndoSnapshot } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Restore an undo snapshot into editor\n * @param core The editor core object\n * @param step Steps to move, can be 0, positive or negative\n */\nexport const restoreUndoSnapshot: RestoreUndoSnapshot = (core: EditorCore, step: number) => {\n if (core.undo.hasNewContent && step < 0) {\n core.api.addUndoSnapshot(\n core,\n null /*callback*/,\n null /*changeSource*/,\n false /*canUndoByBackspace*/\n );\n }\n\n const snapshot = core.undo.snapshotsService.move(step);\n\n if (snapshot != null) {\n try {\n core.undo.isRestoring = true;\n core.api.setContent(core, snapshot, true /*triggerContentChangedEvent*/);\n } finally {\n core.undo.isRestoring = false;\n }\n }\n};\n","import { EditorCore, SelectRange } from 'roosterjs-editor-types';\nimport { hasFocus } from './hasFocus';\nimport {\n contains,\n getPendableFormatState,\n Position,\n PendableFormatNames,\n PendableFormatCommandMap,\n addRangeToSelection,\n} from 'roosterjs-editor-dom';\n\n/**\n * @internal\n * Change the editor selection to the given range\n * @param core The EditorCore object\n * @param range The range to select\n * @param skipSameRange When set to true, do nothing if the given range is the same with current selection\n * in editor, otherwise it will always remove current selection range and set to the given one.\n * This parameter is always treat as true in Edge to avoid some weird runtime exception.\n */\nexport const selectRange: SelectRange = (\n core: EditorCore,\n range: Range,\n skipSameRange?: boolean\n) => {\n if (!core.lifecycle.shadowEditSelectionPath && contains(core.contentDiv, range)) {\n addRangeToSelection(range, skipSameRange);\n\n if (!hasFocus(core)) {\n core.domEvent.selectionRange = range;\n }\n\n if (range.collapsed) {\n // If selected, and current selection is collapsed,\n // need to restore pending format state if exists.\n restorePendingFormatState(core);\n }\n\n return true;\n } else {\n return false;\n }\n};\n\n/**\n * Restore cached pending format state (if exist) to current selection\n */\nfunction restorePendingFormatState(core: EditorCore) {\n const {\n contentDiv,\n pendingFormatState,\n api: { getSelectionRange },\n } = core;\n\n if (pendingFormatState.pendableFormatState) {\n const document = contentDiv.ownerDocument;\n let formatState = getPendableFormatState(document);\n (Object.keys(PendableFormatCommandMap)).forEach(key => {\n if (!!pendingFormatState.pendableFormatState[key] != formatState[key]) {\n document.execCommand(PendableFormatCommandMap[key], false, null);\n }\n });\n\n const range = getSelectionRange(core, true /*tryGetFromCache*/);\n pendingFormatState.pendableFormatPosition = range && Position.getStart(range);\n }\n}\n","import { setHtmlWithSelectionPath } from 'roosterjs-editor-dom';\nimport {\n ChangeSource,\n ColorTransformDirection,\n EditorCore,\n PluginEventType,\n SetContent,\n} from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Set HTML content to this editor. All existing content will be replaced. A ContentChanged event will be triggered\n * if triggerContentChangedEvent is set to true\n * @param core The EditorCore object\n * @param content HTML content to set in\n * @param triggerContentChangedEvent True to trigger a ContentChanged event. Default value is true\n */\nexport const setContent: SetContent = (\n core: EditorCore,\n content: string,\n triggerContentChangedEvent: boolean\n) => {\n let contentChanged = false;\n if (core.contentDiv.innerHTML != content) {\n core.api.triggerEvent(\n core,\n {\n eventType: PluginEventType.BeforeSetContent,\n newContent: content,\n },\n true /*broadcast*/\n );\n\n const range = setHtmlWithSelectionPath(core.contentDiv, content, core.trustedHTMLHandler);\n core.api.selectRange(core, range);\n contentChanged = true;\n }\n\n // Convert content even if it hasn't changed.\n core.api.transformColor(\n core,\n core.contentDiv,\n false /*includeSelf*/,\n null /*callback*/,\n ColorTransformDirection.LightToDark\n );\n\n if (triggerContentChangedEvent && (contentChanged || core.lifecycle.isDarkMode)) {\n core.api.triggerEvent(\n core,\n {\n eventType: PluginEventType.ContentChanged,\n source: ChangeSource.SetContent,\n },\n false /*broadcast*/\n );\n }\n};\n","import { createRange, getSelectionPath, moveChildNodes } from 'roosterjs-editor-dom';\nimport { EditorCore, PluginEventType, SwitchShadowEdit } from 'roosterjs-editor-types';\n\n/**\n * @internal\n */\nexport const switchShadowEdit: SwitchShadowEdit = (core: EditorCore, isOn: boolean): void => {\n const { lifecycle, contentDiv } = core;\n let { shadowEditFragment, shadowEditSelectionPath } = lifecycle;\n const wasInShadowEdit = !!shadowEditFragment;\n\n if (isOn) {\n if (!wasInShadowEdit) {\n const range = core.api.getSelectionRange(core, true /*tryGetFromCache*/);\n shadowEditSelectionPath = range && getSelectionPath(contentDiv, range);\n shadowEditFragment = core.contentDiv.ownerDocument.createDocumentFragment();\n\n moveChildNodes(shadowEditFragment, contentDiv);\n shadowEditFragment.normalize();\n core.api.triggerEvent(\n core,\n {\n eventType: PluginEventType.EnteredShadowEdit,\n fragment: shadowEditFragment,\n selectionPath: shadowEditSelectionPath,\n },\n false /*broadcast*/\n );\n\n lifecycle.shadowEditFragment = shadowEditFragment;\n lifecycle.shadowEditSelectionPath = shadowEditSelectionPath;\n }\n\n moveChildNodes(contentDiv);\n contentDiv.appendChild(lifecycle.shadowEditFragment.cloneNode(true /*deep*/));\n } else {\n lifecycle.shadowEditFragment = null;\n lifecycle.shadowEditSelectionPath = null;\n\n if (wasInShadowEdit) {\n core.api.triggerEvent(\n core,\n {\n eventType: PluginEventType.LeavingShadowEdit,\n },\n false /*broadcast*/\n );\n\n moveChildNodes(contentDiv);\n contentDiv.appendChild(shadowEditFragment);\n core.api.focus(core);\n\n if (shadowEditSelectionPath) {\n core.api.selectRange(\n core,\n createRange(\n contentDiv,\n shadowEditSelectionPath.start,\n shadowEditSelectionPath.end\n )\n );\n }\n }\n }\n};\n","import { arrayPush, getComputedStyles, safeInstanceOf, toArray } from 'roosterjs-editor-dom';\nimport {\n ColorTransformDirection,\n DarkModeDatasetNames,\n EditorCore,\n TransformColor,\n} from 'roosterjs-editor-types';\n\nconst enum ColorAttributeEnum {\n CssColor = 0,\n HtmlColor = 1,\n CssDataSet = 2,\n HtmlDataSet = 3,\n}\n\nconst ColorAttributeName: { [key in ColorAttributeEnum]: string }[] = [\n {\n [ColorAttributeEnum.CssColor]: 'color',\n [ColorAttributeEnum.HtmlColor]: 'color',\n [ColorAttributeEnum.CssDataSet]: DarkModeDatasetNames.OriginalStyleColor,\n [ColorAttributeEnum.HtmlDataSet]: DarkModeDatasetNames.OriginalAttributeColor,\n },\n {\n [ColorAttributeEnum.CssColor]: 'background-color',\n [ColorAttributeEnum.HtmlColor]: 'bgcolor',\n [ColorAttributeEnum.CssDataSet]: DarkModeDatasetNames.OriginalStyleBackgroundColor,\n [ColorAttributeEnum.HtmlDataSet]: DarkModeDatasetNames.OriginalAttributeBackgroundColor,\n },\n];\n\n/**\n * @internal\n * Edit and transform color of elements between light mode and dark mode\n * @param core The EditorCore object\n * @param rootNode The root HTML elements to transform\n * @param includeSelf True to transform the root node as well, otherwise false\n * @param callback The callback function to invoke before do color transformation\n * @param direction To specify the transform direction, light to dark, or dark to light\n */\nexport const transformColor: TransformColor = (\n core: EditorCore,\n rootNode: Node,\n includeSelf: boolean,\n callback: () => void,\n direction: ColorTransformDirection\n) => {\n const elementsToTransform = core.lifecycle.isDarkMode ? getAll(rootNode, includeSelf) : [];\n const transformFunction =\n direction == ColorTransformDirection.DarkToLight\n ? transformToLightMode\n : core.lifecycle.onExternalContentTransform ||\n ((element: HTMLElement) => transformToDarkMode(element, core.lifecycle.getDarkColor));\n\n callback?.();\n\n elementsToTransform.forEach(\n element => element?.dataset && element.style && transformFunction(element)\n );\n};\n\nfunction transformToLightMode(element: HTMLElement) {\n ColorAttributeName.forEach(names => {\n // Reset color styles based on the content of the ogsc/ogsb data element.\n // If those data properties are empty or do not exist, set them anyway to clear the content.\n element.style.setProperty(\n names[ColorAttributeEnum.CssColor],\n getValueOrDefault(element.dataset[names[ColorAttributeEnum.CssDataSet]], '')\n );\n delete element.dataset[names[ColorAttributeEnum.CssDataSet]];\n\n // Some elements might have set attribute colors. We need to reset these as well.\n const value = element.dataset[names[ColorAttributeEnum.HtmlDataSet]];\n\n if (getValueOrDefault(value, null)) {\n element.setAttribute(names[ColorAttributeEnum.HtmlColor], value);\n } else {\n element.removeAttribute(names[ColorAttributeEnum.HtmlColor]);\n }\n\n delete element.dataset[names[ColorAttributeEnum.HtmlDataSet]];\n });\n}\n\nfunction transformToDarkMode(element: HTMLElement, getDarkColor: (color: string) => string) {\n const computedValues = getComputedStyles(element, ['color', 'background-color']);\n\n ColorAttributeName.forEach((names, index) => {\n const styleColor = element.style.getPropertyValue(names[ColorAttributeEnum.CssColor]);\n const attrColor = element.getAttribute(names[ColorAttributeEnum.HtmlColor]);\n\n if (\n !element.dataset[names[ColorAttributeEnum.CssDataSet]] &&\n !element.dataset[names[ColorAttributeEnum.HtmlDataSet]] &&\n (styleColor || attrColor)\n ) {\n const newColor = getDarkColor(computedValues[index] || styleColor || attrColor);\n element.style.setProperty(names[ColorAttributeEnum.CssColor], newColor, 'important');\n element.dataset[names[ColorAttributeEnum.CssDataSet]] = styleColor || '';\n\n if (attrColor) {\n element.setAttribute(names[ColorAttributeEnum.HtmlColor], newColor);\n element.dataset[names[ColorAttributeEnum.HtmlDataSet]] = attrColor;\n }\n }\n });\n}\n\nfunction getValueOrDefault(value: string, defaultValue: string | null) {\n return value && value != 'undefined' && value != 'null' ? value : defaultValue;\n}\n\nfunction getAll(rootNode: Node, includeSelf: boolean): HTMLElement[] {\n const result: HTMLElement[] = [];\n\n if (safeInstanceOf(rootNode, 'HTMLElement')) {\n if (includeSelf) {\n result.push(rootNode);\n }\n const allChildren = rootNode.getElementsByTagName('*');\n arrayPush(result, toArray(allChildren));\n } else if (safeInstanceOf(rootNode, 'DocumentFragment')) {\n const allChildren = rootNode.querySelectorAll('*');\n arrayPush(result, toArray(allChildren));\n }\n\n return result;\n}\n","import { EditorCore, EditorPlugin, PluginEvent, TriggerEvent } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Trigger a plugin event\n * @param core The EditorCore object\n * @param pluginEvent The event object to trigger\n * @param broadcast Set to true to skip the shouldHandleEventExclusively check\n */\nexport const triggerEvent: TriggerEvent = (\n core: EditorCore,\n pluginEvent: PluginEvent,\n broadcast: boolean\n) => {\n if (\n !core.lifecycle.shadowEditFragment &&\n (broadcast || !core.plugins.some(plugin => handledExclusively(pluginEvent, plugin)))\n ) {\n core.plugins.forEach(plugin => {\n if (plugin.onPluginEvent) {\n plugin.onPluginEvent(pluginEvent);\n }\n });\n }\n};\n\nfunction handledExclusively(event: PluginEvent, plugin: EditorPlugin): boolean {\n if (plugin.onPluginEvent && plugin.willHandleEventExclusively?.(event)) {\n plugin.onPluginEvent(event);\n return true;\n }\n\n return false;\n}\n","import CopyPastePlugin from './CopyPastePlugin';\nimport DOMEventPlugin from './DOMEventPlugin';\nimport EditPlugin from './EditPlugin';\nimport EntityPlugin from './EntityPlugin';\nimport LifecyclePlugin from './LifecyclePlugin';\nimport MouseUpPlugin from './MouseUpPlugin';\nimport PendingFormatStatePlugin from './PendingFormatStatePlugin';\nimport TypeInContainerPlugin from './TypeInContainerPlugin';\nimport UndoPlugin from './UndoPlugin';\nimport { CorePlugins, EditorOptions, PluginState } from 'roosterjs-editor-types';\n\n/**\n * @internal\n */\nexport const PLACEHOLDER_PLUGIN_NAME = '_placeholder';\n\n/**\n * @internal\n * Create Core Plugins\n * @param contentDiv Content DIV of editor\n * @param options Editor options\n */\nexport default function createCorePlugins(\n contentDiv: HTMLDivElement,\n options: EditorOptions\n): CorePlugins & { [PLACEHOLDER_PLUGIN_NAME]: null } {\n const map = options.corePluginOverride || {};\n // The order matters, some plugin needs to be put before/after others to make sure event\n // can be handled in right order\n return {\n typeInContainer: map.typeInContainer || new TypeInContainerPlugin(),\n edit: map.edit || new EditPlugin(),\n _placeholder: null,\n typeAfterLink: null, //deprecated after firefox update\n undo: map.undo || new UndoPlugin(options),\n domEvent: map.domEvent || new DOMEventPlugin(options, contentDiv),\n pendingFormatState: map.pendingFormatState || new PendingFormatStatePlugin(),\n mouseUp: map.mouseUp || new MouseUpPlugin(),\n copyPaste: map.copyPaste || new CopyPastePlugin(options),\n entity: map.entity || new EntityPlugin(),\n lifecycle: map.lifecycle || new LifecyclePlugin(options, contentDiv),\n };\n}\n\n/**\n * @internal\n * Get plugin state of core plugins\n * @param corePlugins CorePlugins object\n */\nexport function getPluginState(corePlugins: CorePlugins): PluginState {\n return {\n domEvent: corePlugins.domEvent.getState(),\n pendingFormatState: corePlugins.pendingFormatState.getState(),\n edit: corePlugins.edit.getState(),\n lifecycle: corePlugins.lifecycle.getState(),\n undo: corePlugins.undo.getState(),\n entity: corePlugins.entity.getState(),\n copyPaste: corePlugins.copyPaste.getState(),\n };\n}\n","import {\n addRangeToSelection,\n createElement,\n extractClipboardEvent,\n setHtmlWithSelectionPath,\n moveChildNodes,\n} from 'roosterjs-editor-dom';\nimport {\n ChangeSource,\n ContentPosition,\n CopyPastePluginState,\n EditorOptions,\n GetContentMode,\n IEditor,\n PluginEventType,\n ExperimentalFeatures,\n PluginWithState,\n KnownCreateElementDataIndex,\n} from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Copy and paste plugin for handling onCopy and onPaste event\n */\nexport default class CopyPastePlugin implements PluginWithState {\n private editor: IEditor;\n private disposer: () => void;\n private state: CopyPastePluginState;\n\n /**\n * Construct a new instance of CopyPastePlugin\n * @param options The editor options\n */\n constructor(options: EditorOptions) {\n this.state = {\n allowedCustomPasteType: options.allowedCustomPasteType || [],\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'CopyPaste';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.disposer = this.editor.addDomEventHandler({\n paste: this.onPaste,\n copy: e => this.onCutCopy(e, false /*isCut*/),\n cut: e => this.onCutCopy(e, true /*isCut*/),\n });\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.disposer();\n this.disposer = null;\n this.editor = null;\n }\n\n /**\n * Get plugin state object\n */\n getState() {\n return this.state;\n }\n\n private onCutCopy(event: Event, isCut: boolean) {\n const originalRange = this.editor.getSelectionRange();\n if (originalRange && !originalRange.collapsed) {\n const html = this.editor.getContent(GetContentMode.RawHTMLWithSelection);\n const tempDiv = this.getTempDiv(true /*forceInLightMode*/);\n const newRange = setHtmlWithSelectionPath(\n tempDiv,\n html,\n this.editor.getTrustedHTMLHandler()\n );\n\n if (newRange) {\n addRangeToSelection(newRange);\n }\n\n this.editor.triggerPluginEvent(PluginEventType.BeforeCutCopy, {\n clonedRoot: tempDiv,\n range: newRange,\n rawEvent: event as ClipboardEvent,\n isCut,\n });\n\n this.editor.runAsync(editor => {\n this.cleanUpAndRestoreSelection(tempDiv, originalRange);\n\n if (isCut) {\n editor.addUndoSnapshot(() => {\n const position = this.editor.deleteSelectedContent();\n editor.focus();\n editor.select(position);\n }, ChangeSource.Cut);\n }\n });\n }\n }\n\n private onPaste = (event: Event) => {\n let range: Range;\n\n extractClipboardEvent(\n event as ClipboardEvent,\n clipboardData => this.editor.paste(clipboardData),\n {\n allowLinkPreview: this.editor.isFeatureEnabled(\n ExperimentalFeatures.PasteWithLinkPreview\n ),\n allowedCustomPasteType: this.state.allowedCustomPasteType,\n getTempDiv: () => {\n range = this.editor.getSelectionRange();\n return this.getTempDiv();\n },\n removeTempDiv: div => {\n this.cleanUpAndRestoreSelection(div, range);\n },\n }\n );\n };\n\n private getTempDiv(forceInLightMode?: boolean) {\n const div = this.editor.getCustomData(\n 'CopyPasteTempDiv',\n () => {\n const tempDiv = createElement(\n KnownCreateElementDataIndex.CopyPasteTempDiv,\n this.editor.getDocument()\n ) as HTMLDivElement;\n this.editor.insertNode(tempDiv, {\n position: ContentPosition.Outside,\n });\n\n return tempDiv;\n },\n tempDiv => tempDiv.parentNode?.removeChild(tempDiv)\n );\n\n if (forceInLightMode) {\n div.style.backgroundColor = 'white';\n div.style.color = 'black';\n }\n\n div.style.display = '';\n div.focus();\n\n return div;\n }\n\n private cleanUpAndRestoreSelection(tempDiv: HTMLDivElement, range: Range) {\n this.editor.select(range);\n tempDiv.style.backgroundColor = '';\n tempDiv.style.color = '';\n tempDiv.style.display = 'none';\n moveChildNodes(tempDiv);\n }\n}\n","import { arrayPush, Browser, isCharacterValue } from 'roosterjs-editor-dom';\nimport {\n ChangeSource,\n ContextMenuProvider,\n DOMEventHandler,\n DOMEventPluginState,\n EditorOptions,\n EditorPlugin,\n IEditor,\n Keys,\n PluginEventType,\n PluginWithState,\n} from 'roosterjs-editor-types';\n\n/**\n * @internal\n * DOMEventPlugin handles customized DOM events, including:\n * 1. Keyboard event\n * 2. Mouse event\n * 3. IME state\n * 4. Drop event\n * 5. Focus and blur event\n * 6. Input event\n * 7. Scroll event\n * It contains special handling for Safari since Safari cannot get correct selection when onBlur event is triggered in editor.\n */\nexport default class DOMEventPlugin implements PluginWithState {\n private editor: IEditor;\n private disposer: () => void;\n private state: DOMEventPluginState;\n\n /**\n * Construct a new instance of DOMEventPlugin\n * @param options The editor options\n * @param contentDiv The editor content DIV\n */\n constructor(options: EditorOptions, contentDiv: HTMLDivElement) {\n this.state = {\n isInIME: false,\n scrollContainer: options.scrollContainer || contentDiv,\n selectionRange: null,\n stopPrintableKeyboardEventPropagation: !options.allowKeyboardEventPropagation,\n contextMenuProviders:\n options.plugins?.filter>(isContextMenuProvider) || [],\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'DOMEvent';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n\n const document = this.editor.getDocument();\n const eventHandlers: Record = {\n // 1. Keyboard event\n keypress: this.getEventHandler(PluginEventType.KeyPress),\n keydown: this.getEventHandler(PluginEventType.KeyDown),\n keyup: this.getEventHandler(PluginEventType.KeyUp),\n\n // 2. Mouse event\n mousedown: PluginEventType.MouseDown,\n contextmenu: this.onContextMenuEvent,\n\n // 3. IME state management\n compositionstart: () => (this.state.isInIME = true),\n compositionend: (rawEvent: CompositionEvent) => {\n this.state.isInIME = false;\n editor.triggerPluginEvent(PluginEventType.CompositionEnd, {\n rawEvent,\n });\n },\n\n // 4. Drop event\n drop: this.onDrop,\n\n // 5. Focus management\n focus: this.onFocus,\n\n // 6. Input event\n [Browser.isIE ? 'textinput' : 'input']: this.getEventHandler(PluginEventType.Input),\n };\n\n // 7. onBlur handlers\n if (Browser.isSafari) {\n document.addEventListener('mousedown', this.onMouseDownDocument, true /*useCapture*/);\n document.addEventListener('keydown', this.onKeyDownDocument);\n document.defaultView?.addEventListener('blur', this.cacheSelection);\n } else {\n eventHandlers[Browser.isIEOrEdge ? 'beforedeactivate' : 'blur'] = this.cacheSelection;\n }\n\n this.disposer = editor.addDomEventHandler(eventHandlers);\n\n // 8. Scroll event\n this.state.scrollContainer.addEventListener('scroll', this.onScroll);\n document.defaultView?.addEventListener('scroll', this.onScroll);\n document.defaultView?.addEventListener('resize', this.onScroll);\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n const document = this.editor.getDocument();\n if (Browser.isSafari) {\n document.removeEventListener(\n 'mousedown',\n this.onMouseDownDocument,\n true /*useCapture*/\n );\n document.removeEventListener('keydown', this.onKeyDownDocument);\n document.defaultView?.removeEventListener('blur', this.cacheSelection);\n }\n\n document.defaultView?.removeEventListener('resize', this.onScroll);\n document.defaultView?.removeEventListener('scroll', this.onScroll);\n this.state.scrollContainer.removeEventListener('scroll', this.onScroll);\n this.disposer();\n this.disposer = null;\n this.editor = null;\n }\n\n /**\n * Get plugin state object\n */\n getState() {\n return this.state;\n }\n\n private onDrop = (e: UIEvent) => {\n this.editor.runAsync(editor => {\n editor.addUndoSnapshot(() => {}, ChangeSource.Drop);\n });\n };\n\n private onFocus = () => {\n this.editor.select(this.state.selectionRange);\n this.state.selectionRange = null;\n };\n private onKeyDownDocument = (event: KeyboardEvent) => {\n if (event.which == Keys.TAB && !event.defaultPrevented) {\n this.cacheSelection();\n }\n };\n\n private onMouseDownDocument = (event: MouseEvent) => {\n if (!this.state.selectionRange && !this.editor.contains(event.target as Node)) {\n this.cacheSelection();\n }\n };\n\n private cacheSelection = () => {\n if (!this.state.selectionRange) {\n this.state.selectionRange = this.editor.getSelectionRange(false /*tryGetFromCache*/);\n }\n };\n private onScroll = (e: UIEvent) => {\n this.editor.triggerPluginEvent(PluginEventType.Scroll, {\n rawEvent: e,\n scrollContainer: this.state.scrollContainer,\n });\n };\n\n private getEventHandler(eventType: PluginEventType): DOMEventHandler {\n return this.state.stopPrintableKeyboardEventPropagation\n ? {\n pluginEventType: eventType,\n beforeDispatch:\n eventType == PluginEventType.Input ? this.onInputEvent : this.onKeyboardEvent,\n }\n : eventType;\n }\n\n private onKeyboardEvent = (event: KeyboardEvent) => {\n if (isCharacterValue(event)) {\n event.stopPropagation();\n }\n };\n\n private onInputEvent = (event: InputEvent) => {\n event.stopPropagation();\n };\n\n private onContextMenuEvent = (event: MouseEvent) => {\n const allItems: any[] = [];\n const searcher = this.editor.getContentSearcherOfCursor();\n const elementBeforeCursor = searcher?.getInlineElementBefore();\n\n let eventTargetNode = event.target as Node;\n if (event.button != 2) {\n eventTargetNode = elementBeforeCursor?.getContainerNode();\n }\n this.state.contextMenuProviders.forEach(provider => {\n const items = provider.getContextMenuItems(eventTargetNode);\n if (items?.length > 0) {\n if (allItems.length > 0) {\n allItems.push(null);\n }\n arrayPush(allItems, items);\n }\n });\n this.editor.triggerPluginEvent(PluginEventType.ContextMenu, {\n rawEvent: event,\n items: allItems,\n });\n };\n}\n\nfunction isContextMenuProvider(source: EditorPlugin): source is ContextMenuProvider {\n return !!(>source)?.getContextMenuItems;\n}\n","import { isCtrlOrMetaPressed } from 'roosterjs-editor-dom';\nimport {\n EditPluginState,\n GenericContentEditFeature,\n IEditor,\n Keys,\n PluginEvent,\n PluginEventType,\n PluginWithState,\n} from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Edit Component helps handle Content edit features\n */\nexport default class EditPlugin implements PluginWithState {\n private editor: IEditor;\n private state: EditPluginState;\n\n /**\n * Construct a new instance of EditPlugin\n * @param options The editor options\n */\n constructor() {\n this.state = {\n features: {},\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'Edit';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.editor = null;\n }\n\n /**\n * Get plugin state object\n */\n getState() {\n return this.state;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n let hasFunctionKey = false;\n let features: GenericContentEditFeature[];\n let ctrlOrMeta = false;\n\n if (event.eventType == PluginEventType.KeyDown) {\n const rawEvent = event.rawEvent;\n const range = this.editor.getSelectionRange();\n\n ctrlOrMeta = isCtrlOrMetaPressed(rawEvent);\n hasFunctionKey = ctrlOrMeta || rawEvent.altKey;\n features =\n this.state.features[rawEvent.which] ||\n (range && !range.collapsed && this.state.features[Keys.RANGE]);\n } else if (event.eventType == PluginEventType.ContentChanged) {\n features = this.state.features[Keys.CONTENTCHANGED];\n }\n\n for (let i = 0; i < features?.length; i++) {\n const feature = features[i];\n if (\n (feature.allowFunctionKeys || !hasFunctionKey) &&\n feature.shouldHandleEvent(event, this.editor, ctrlOrMeta)\n ) {\n feature.handleEvent(event, this.editor);\n break;\n }\n }\n }\n}\n","import {\n Browser,\n commitEntity,\n getEntityFromElement,\n getEntitySelector,\n isCharacterValue,\n toArray,\n arrayPush,\n} from 'roosterjs-editor-dom';\nimport {\n ChangeSource,\n ContentPosition,\n Entity,\n EntityClasses,\n EntityOperation,\n EntityPluginState,\n HtmlSanitizerOptions,\n IEditor,\n Keys,\n PluginEvent,\n PluginEventType,\n PluginWithState,\n QueryScope,\n} from 'roosterjs-editor-types';\n\nconst ENTITY_ID_REGEX = /_(\\d{1,8})$/;\n\nconst ENTITY_CSS_REGEX = '^' + EntityClasses.ENTITY_INFO_NAME + '$';\nconst ENTITY_ID_CSS_REGEX = '^' + EntityClasses.ENTITY_ID_PREFIX;\nconst ENTITY_TYPE_CSS_REGEX = '^' + EntityClasses.ENTITY_TYPE_PREFIX;\nconst ENTITY_READONLY_CSS_REGEX = '^' + EntityClasses.ENTITY_READONLY_PREFIX;\nconst ALLOWED_CSS_CLASSES = [\n ENTITY_CSS_REGEX,\n ENTITY_ID_CSS_REGEX,\n ENTITY_TYPE_CSS_REGEX,\n ENTITY_READONLY_CSS_REGEX,\n];\n\n/**\n * @internal\n * Entity Plugin helps handle all operations related to an entity and generate entity specified events\n */\nexport default class EntityPlugin implements PluginWithState {\n private editor: IEditor;\n private state: EntityPluginState;\n\n /**\n * Construct a new instance of EntityPlugin\n */\n constructor() {\n this.state = {\n clickingPoint: null,\n knownEntityElements: [],\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'Entity';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.editor = null;\n this.state.knownEntityElements = [];\n this.state.clickingPoint = null;\n }\n\n /**\n * Get plugin state object\n */\n getState() {\n return this.state;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n switch (event.eventType) {\n case PluginEventType.MouseDown:\n this.handleMouseDownEvent(event.rawEvent);\n break;\n case PluginEventType.MouseUp:\n this.handleMouseUpEvent(event.rawEvent);\n break;\n case PluginEventType.KeyDown:\n this.handleKeyDownEvent(event.rawEvent);\n break;\n case PluginEventType.BeforeCutCopy:\n if (event.isCut) {\n this.handleCutEvent(event.rawEvent);\n }\n break;\n case PluginEventType.BeforePaste:\n this.handleBeforePasteEvent(event.fragment, event.sanitizingOption);\n break;\n case PluginEventType.ContentChanged:\n this.handleContentChangedEvent(event.source == ChangeSource.SetContent);\n break;\n case PluginEventType.EditorReady:\n this.handleContentChangedEvent(true /*resetAll*/);\n break;\n case PluginEventType.ExtractContentWithDom:\n this.handleExtractContentWithDomEvent(event.clonedRoot);\n break;\n case PluginEventType.ContextMenu:\n this.handleContextMenuEvent(event.rawEvent);\n break;\n }\n }\n\n private handleContextMenuEvent(event: UIEvent) {\n const node = event.target as Node;\n const entityElement = node && this.editor.getElementAtCursor(getEntitySelector(), node);\n\n if (entityElement) {\n event.preventDefault();\n this.triggerEvent(entityElement, EntityOperation.ContextMenu, event);\n }\n }\n\n private handleCutEvent = (event: ClipboardEvent) => {\n const range = this.editor.getSelectionRange();\n if (range && !range.collapsed) {\n this.checkRemoveEntityForRange(event);\n }\n };\n\n private handleMouseDownEvent(event: MouseEvent) {\n const { target, pageX, pageY } = event;\n const node = target as Node;\n const entityElement = node && this.editor.getElementAtCursor(getEntitySelector(), node);\n if (entityElement && !entityElement.isContentEditable) {\n event.preventDefault();\n this.state.clickingPoint = { pageX, pageY };\n }\n }\n\n private handleMouseUpEvent(event: MouseEvent) {\n const { target, pageX, pageY } = event;\n const node = target as Node;\n let entityElement: HTMLElement;\n\n if (\n this.state.clickingPoint &&\n this.state.clickingPoint.pageX == pageX &&\n this.state.clickingPoint.pageY == pageY &&\n node &&\n !!(entityElement = this.editor.getElementAtCursor(getEntitySelector(), node))\n ) {\n event.preventDefault();\n this.triggerEvent(entityElement, EntityOperation.Click, event);\n\n workaroundSelectionIssueForIE(this.editor);\n }\n\n this.state.clickingPoint = null;\n }\n\n private handleKeyDownEvent(event: KeyboardEvent) {\n if (\n isCharacterValue(event) ||\n event.which == Keys.BACKSPACE ||\n event.which == Keys.DELETE\n ) {\n const range = this.editor.getSelectionRange();\n if (range && !range.collapsed) {\n this.checkRemoveEntityForRange(event);\n }\n }\n }\n\n private handleBeforePasteEvent(\n fragment: DocumentFragment,\n sanitizingOption: HtmlSanitizerOptions\n ) {\n const range = this.editor.getSelectionRange();\n\n if (!range.collapsed) {\n this.checkRemoveEntityForRange(null /*rawEvent*/);\n }\n\n arrayPush(sanitizingOption.additionalAllowedCssClasses, ALLOWED_CSS_CLASSES);\n }\n\n private handleContentChangedEvent(resetAll: boolean) {\n this.state.knownEntityElements = resetAll\n ? []\n : this.state.knownEntityElements.filter(node => this.editor.contains(node));\n const allId = this.state.knownEntityElements\n .map(e => getEntityFromElement(e)?.id)\n .filter(x => !!x);\n\n this.editor.queryElements(getEntitySelector(), element => {\n if (this.state.knownEntityElements.indexOf(element) < 0) {\n this.state.knownEntityElements.push(element);\n\n const entity = getEntityFromElement(element);\n\n this.hydrateEntity(entity, allId);\n }\n });\n }\n\n private handleExtractContentWithDomEvent(root: HTMLElement) {\n toArray(root.querySelectorAll(getEntitySelector())).forEach(element => {\n element.removeAttribute('contentEditable');\n\n this.triggerEvent(element as HTMLElement, EntityOperation.ReplaceTemporaryContent);\n });\n }\n\n private checkRemoveEntityForRange(event: Event) {\n const editableEntityElements: HTMLElement[] = [];\n const selector = getEntitySelector();\n this.editor.queryElements(selector, QueryScope.OnSelection, element => {\n if (element.isContentEditable) {\n editableEntityElements.push(element);\n } else {\n this.triggerEvent(element, EntityOperation.Overwrite, event);\n }\n });\n\n // For editable entities, we need to check if it is fully or partially covered by current selection,\n // and trigger different events;\n if (editableEntityElements.length > 0) {\n const inSelectionEntityElements = this.editor.queryElements(\n selector,\n QueryScope.InSelection\n );\n editableEntityElements.forEach(element => {\n const isFullyCovered = inSelectionEntityElements.indexOf(element) >= 0;\n this.triggerEvent(\n element,\n isFullyCovered ? EntityOperation.Overwrite : EntityOperation.PartialOverwrite,\n event\n );\n });\n }\n }\n\n private hydrateEntity(entity: Entity, knownIds: string[]) {\n const { id, type, wrapper, isReadonly } = entity;\n const match = ENTITY_ID_REGEX.exec(id);\n const baseId = (match ? id.substr(0, id.length - match[0].length) : id) || type;\n\n // Make sure entity id is unique\n let newId = '';\n\n for (let num = (match && parseInt(match[1])) || 0; ; num++) {\n newId = num > 0 ? `${baseId}_${num}` : baseId;\n\n if (knownIds.indexOf(newId) < 0) {\n knownIds.push(newId);\n break;\n }\n }\n\n commitEntity(wrapper, type, isReadonly, newId);\n\n this.triggerEvent(wrapper, EntityOperation.NewEntity);\n }\n\n private triggerEvent(element: HTMLElement, operation: EntityOperation, rawEvent?: Event) {\n const entity = element && getEntityFromElement(element);\n\n if (entity) {\n this.editor.triggerPluginEvent(PluginEventType.EntityOperation, {\n operation,\n rawEvent,\n entity,\n });\n }\n }\n}\n\n/**\n * IE will show a resize border around the readonly content within content editable DIV\n * This is a workaround to remove it by temporarily move focus out of editor\n */\nconst workaroundSelectionIssueForIE = Browser.isIE\n ? (editor: IEditor) => {\n editor.runAsync(editor => {\n const workaroundButton = editor.getCustomData('ENTITY_IE_FOCUS_BUTTON', () => {\n const button = editor.getDocument().createElement('button');\n button.style.overflow = 'hidden';\n button.style.position = 'fixed';\n button.style.width = '0';\n button.style.height = '0';\n button.style.left = '0';\n button.style.top = '-1000px';\n button.onblur = () => {\n button.style.display = 'none';\n };\n\n editor.insertNode(button, {\n position: ContentPosition.Outside,\n });\n\n return button;\n });\n\n workaroundButton.style.display = '';\n const range = editor.getDocument().createRange();\n range.setStart(workaroundButton, 0);\n try {\n window.getSelection().removeAllRanges();\n window.getSelection().addRange(range);\n } catch {}\n });\n }\n : () => {};\n","import { Browser, getComputedStyles, setColor } from 'roosterjs-editor-dom';\nimport {\n DefaultFormat,\n DocumentCommand,\n EditorOptions,\n IEditor,\n LifecyclePluginState,\n PluginEventType,\n PluginWithState,\n PluginEvent,\n ChangeSource,\n} from 'roosterjs-editor-types';\n\nconst CONTENT_EDITABLE_ATTRIBUTE_NAME = 'contenteditable';\nconst COMMANDS: {\n [command: string]: any;\n} = Browser.isFirefox\n ? {\n /**\n * Disable these object resizing for firefox since other browsers don't have these behaviors\n */\n [DocumentCommand.EnableObjectResizing]: false,\n [DocumentCommand.EnableInlineTableEditing]: false,\n }\n : Browser.isIE\n ? {\n /**\n * Change the default paragraph separator to DIV. This is mainly for IE since its default setting is P\n */\n [DocumentCommand.DefaultParagraphSeparator]: 'div',\n\n /**\n * Disable auto link feature in IE since we have our own implementation\n */\n [DocumentCommand.AutoUrlDetect]: false,\n }\n : {};\n\nconst DARK_MODE_DEFAULT_FORMAT = {\n backgroundColors: {\n darkModeColor: 'rgb(51,51,51)',\n lightModeColor: 'rgb(255,255,255)',\n },\n textColors: {\n darkModeColor: 'rgb(255,255,255)',\n lightModeColor: 'rgb(0,0,0)',\n },\n};\n\n/**\n * @internal\n * Lifecycle plugin handles editor initialization and disposing\n */\nexport default class LifecyclePlugin implements PluginWithState {\n private editor: IEditor;\n private state: LifecyclePluginState;\n private initialContent: string;\n private contentDivFormat: string[];\n private initializer: () => void;\n private disposer: () => void;\n private adjustColor: () => void;\n\n /**\n * Construct a new instance of LifecyclePlugin\n * @param options The editor options\n * @param contentDiv The editor content DIV\n */\n constructor(options: EditorOptions, contentDiv: HTMLDivElement) {\n this.initialContent = options.initialContent || contentDiv.innerHTML || '';\n this.contentDivFormat = getComputedStyles(contentDiv);\n\n // Make the container editable and set its selection styles\n if (contentDiv.getAttribute(CONTENT_EDITABLE_ATTRIBUTE_NAME) === null) {\n this.initializer = () => {\n contentDiv.contentEditable = 'true';\n this.setSelectStyle(contentDiv, 'text');\n };\n this.disposer = () => {\n this.setSelectStyle(contentDiv, '');\n contentDiv.removeAttribute(CONTENT_EDITABLE_ATTRIBUTE_NAME);\n };\n }\n this.adjustColor = options.doNotAdjustEditorColor\n ? () => {}\n : () => {\n const { textColors, backgroundColors } = DARK_MODE_DEFAULT_FORMAT;\n const { isDarkMode } = this.state;\n setColor(contentDiv, textColors, false /*isBackground*/, isDarkMode);\n setColor(contentDiv, backgroundColors, true /*isBackground*/, isDarkMode);\n };\n\n this.state = {\n customData: {},\n defaultFormat: options.defaultFormat || null,\n isDarkMode: !!options.inDarkMode,\n getDarkColor: options.getDarkColor || ((color: string) => color),\n onExternalContentTransform: options.onExternalContentTransform,\n experimentalFeatures: options.experimentalFeatures || [],\n shadowEditFragment: null,\n shadowEditSelectionPath: null,\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'Lifecycle';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n\n // Calculate default format\n this.recalculateDefaultFormat();\n\n // Ensure initial content and its format\n this.editor.setContent(this.initialContent, false /*triggerContentChangedEvent*/);\n\n // Set content DIV to be editable\n this.initializer?.();\n\n // Set editor background color for dark mode\n this.adjustColor();\n\n // Do proper change for browsers to disable some browser-specified behaviors.\n this.adjustBrowserBehavior();\n\n // Let other plugins know that we are ready\n this.editor.triggerPluginEvent(PluginEventType.EditorReady, {}, true /*broadcast*/);\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.editor.triggerPluginEvent(PluginEventType.BeforeDispose, {}, true /*broadcast*/);\n\n Object.keys(this.state.customData).forEach(key => {\n const data = this.state.customData[key];\n\n if (data && data.disposer) {\n data.disposer(data.value);\n }\n\n delete this.state.customData[key];\n });\n\n if (this.disposer) {\n this.disposer();\n this.disposer = null;\n this.initializer = null;\n }\n\n this.editor = null;\n }\n\n /**\n * Get plugin state object\n */\n getState() {\n return this.state;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n if (\n event.eventType == PluginEventType.ContentChanged &&\n (event.source == ChangeSource.SwitchToDarkMode ||\n event.source == ChangeSource.SwitchToLightMode)\n ) {\n this.state.isDarkMode = event.source == ChangeSource.SwitchToDarkMode;\n this.recalculateDefaultFormat();\n this.adjustColor();\n }\n }\n\n private adjustBrowserBehavior() {\n Object.keys(COMMANDS).forEach(command => {\n // Catch any possible exception since this should not block the initialization of editor\n try {\n this.editor.getDocument().execCommand(command, false, COMMANDS[command]);\n } catch {}\n });\n }\n\n private setSelectStyle(node: HTMLElement, value: string) {\n node.style.userSelect = value;\n node.style.msUserSelect = value;\n node.style.webkitUserSelect = value;\n }\n\n private recalculateDefaultFormat() {\n const { defaultFormat: baseFormat, isDarkMode } = this.state;\n\n if (isDarkMode && baseFormat) {\n if (!baseFormat.backgroundColors) {\n baseFormat.backgroundColors = DARK_MODE_DEFAULT_FORMAT.backgroundColors;\n }\n if (!baseFormat.textColors) {\n baseFormat.textColors = DARK_MODE_DEFAULT_FORMAT.textColors;\n }\n }\n\n if (baseFormat && Object.keys(baseFormat).length === 0) {\n return;\n }\n\n const {\n fontFamily,\n fontSize,\n textColor,\n textColors,\n backgroundColor,\n backgroundColors,\n bold,\n italic,\n underline,\n } = baseFormat || {};\n const defaultFormat = this.contentDivFormat;\n\n this.state.defaultFormat = {\n fontFamily: fontFamily || defaultFormat[0],\n fontSize: fontSize || defaultFormat[1],\n get textColor() {\n return textColors\n ? isDarkMode\n ? textColors.darkModeColor\n : textColors.lightModeColor\n : textColor || defaultFormat[2];\n },\n textColors: textColors,\n get backgroundColor() {\n return backgroundColors\n ? isDarkMode\n ? backgroundColors.darkModeColor\n : backgroundColors.lightModeColor\n : backgroundColor || '';\n },\n backgroundColors: backgroundColors,\n bold: bold,\n italic: italic,\n underline: underline,\n };\n }\n}\n","import { EditorPlugin, IEditor, PluginEvent, PluginEventType } from 'roosterjs-editor-types';\n\n/**\n * @internal\n * MouseUpPlugin help trigger MouseUp event even when mouse up happens outside editor\n * as long as the mouse was pressed within Editor before\n */\nexport default class MouseUpPlugin implements EditorPlugin {\n private editor: IEditor;\n private mouseUpEventListerAdded: boolean;\n private mouseDownX: number;\n private mouseDownY: number;\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'MouseUp';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.removeMouseUpEventListener();\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n if (event.eventType == PluginEventType.MouseDown && !this.mouseUpEventListerAdded) {\n this.editor\n .getDocument()\n .addEventListener('mouseup', this.onMouseUp, true /*setCapture*/);\n this.mouseUpEventListerAdded = true;\n this.mouseDownX = event.rawEvent.pageX;\n this.mouseDownY = event.rawEvent.pageY;\n }\n }\n private removeMouseUpEventListener() {\n if (this.mouseUpEventListerAdded) {\n this.mouseUpEventListerAdded = false;\n this.editor.getDocument().removeEventListener('mouseup', this.onMouseUp, true);\n }\n }\n\n private onMouseUp = (rawEvent: MouseEvent) => {\n if (this.editor) {\n this.removeMouseUpEventListener();\n this.editor.triggerPluginEvent(PluginEventType.MouseUp, {\n rawEvent,\n isClicking: this.mouseDownX == rawEvent.pageX && this.mouseDownY == rawEvent.pageY,\n });\n }\n };\n}\n","import { Position } from 'roosterjs-editor-dom';\nimport {\n IEditor,\n PendingFormatStatePluginState,\n PluginEvent,\n PluginEventType,\n PluginWithState,\n} from 'roosterjs-editor-types';\n\n/**\n * @internal\n * PendingFormatStatePlugin handles pending format state management\n */\nexport default class PendingFormatStatePlugin\n implements PluginWithState {\n private editor: IEditor;\n private state: PendingFormatStatePluginState;\n\n /**\n * Construct a new instance of PendingFormatStatePlugin\n * @param options The editor options\n * @param contentDiv The editor content DIV\n */\n constructor() {\n this.state = {\n pendableFormatPosition: null,\n pendableFormatState: null,\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'PendingFormatState';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.editor = null;\n this.clear();\n }\n\n /**\n * Get plugin state object\n */\n getState() {\n return this.state;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n switch (event.eventType) {\n case PluginEventType.PendingFormatStateChanged:\n // Got PendingFormatStateChanged event, cache current position and pending format\n this.state.pendableFormatPosition = this.getCurrentPosition();\n this.state.pendableFormatState = event.formatState;\n break;\n case PluginEventType.KeyDown:\n case PluginEventType.MouseDown:\n case PluginEventType.ContentChanged:\n // If content or position is changed (by keyboard, mouse, or code),\n // check if current position is still the same with the cached one (if exist),\n // and clear cached format if position is changed since it is out-of-date now\n if (\n this.state.pendableFormatPosition &&\n !this.state.pendableFormatPosition.equalTo(this.getCurrentPosition())\n ) {\n this.clear();\n }\n break;\n }\n }\n\n private clear() {\n this.state.pendableFormatPosition = null;\n this.state.pendableFormatState = null;\n }\n\n private getCurrentPosition() {\n let range = this.editor.getSelectionRange();\n return range && Position.getStart(range).normalize();\n }\n}\n","import { findClosestElementAncestor, Position } from 'roosterjs-editor-dom';\nimport {\n EditorPlugin,\n ExperimentalFeatures,\n IEditor,\n PluginEvent,\n PluginEventType,\n} from 'roosterjs-editor-types';\n\n/**\n * @internal\n * Typing Component helps to ensure typing is always happening under a DOM container\n */\nexport default class TypeInContainerPlugin implements EditorPlugin {\n private editor: IEditor;\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'TypeInContainer';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n if (event.eventType == PluginEventType.KeyPress) {\n // If normalization was not possible before the keypress,\n // check again after the keyboard event has been processed by browser native behavior.\n //\n // This handles the case where the keyboard event that first inserts content happens when\n // there is already content under the selection (e.g. Ctrl+a -> type new content).\n //\n // Only schedule when the range is not collapsed to catch this edge case.\n let range = this.editor.getSelectionRange();\n let shouldAlwaysApplyDefaultFormat = this.editor.isFeatureEnabled(\n ExperimentalFeatures.AlwaysApplyDefaultFormat\n );\n\n if (\n !range ||\n this.editor.contains(\n findClosestElementAncestor(\n range.startContainer,\n null /* root */,\n shouldAlwaysApplyDefaultFormat ? '[style]' : null /*selector*/\n )\n )\n ) {\n return;\n }\n\n if (range.collapsed) {\n this.editor.ensureTypeInContainer(Position.getStart(range), event.rawEvent);\n } else {\n this.editor.runAsync(editor => {\n editor.ensureTypeInContainer(editor.getFocusedPosition(), event.rawEvent);\n });\n }\n }\n }\n}\n","import {\n EditorOptions,\n IEditor,\n Keys,\n PluginEvent,\n PluginEventType,\n PluginWithState,\n UndoPluginState,\n UndoSnapshotsService,\n} from 'roosterjs-editor-types';\nimport {\n addSnapshot,\n canMoveCurrentSnapshot,\n clearProceedingSnapshots,\n createSnapshots,\n isCtrlOrMetaPressed,\n moveCurrentSnapshot,\n canUndoAutoComplete,\n} from 'roosterjs-editor-dom';\n\n// Max stack size that cannot be exceeded. When exceeded, old undo history will be dropped\n// to keep size under limit. This is kept at 10MB\nconst MAX_SIZE_LIMIT = 1e7;\n\n/**\n * @internal\n * Provides snapshot based undo service for Editor\n */\nexport default class UndoPlugin implements PluginWithState {\n private editor: IEditor;\n private lastKeyPress: number;\n private state: UndoPluginState;\n\n /**\n * Construct a new instance of UndoPlugin\n * @param options The wrapper of the state object\n */\n constructor(options: EditorOptions) {\n this.state = {\n snapshotsService: options.undoSnapshotService || createUndoSnapshots(),\n isRestoring: false,\n hasNewContent: false,\n isNested: false,\n autoCompletePosition: null,\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'Undo';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor): void {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.editor = null;\n }\n\n /**\n * Get plugin state object\n */\n getState() {\n return this.state;\n }\n\n /**\n * Check if the plugin should handle the given event exclusively.\n * @param event The event to check\n */\n willHandleEventExclusively(event: PluginEvent) {\n return (\n event.eventType == PluginEventType.KeyDown &&\n event.rawEvent.which == Keys.BACKSPACE &&\n this.canUndoAutoComplete()\n );\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent): void {\n // if editor is in IME, don't do anything\n if (!this.editor || this.editor.isInIME()) {\n return;\n }\n\n switch (event.eventType) {\n case PluginEventType.EditorReady:\n const undoState = this.editor.getUndoState();\n if (!undoState.canUndo && !undoState.canRedo) {\n // Only add initial snapshot when there is no existing snapshot\n // Otherwise preserved undo/redo state may be ruined\n this.addUndoSnapshot();\n }\n break;\n case PluginEventType.KeyDown:\n this.onKeyDown(event.rawEvent);\n break;\n case PluginEventType.KeyPress:\n this.onKeyPress(event.rawEvent);\n break;\n case PluginEventType.CompositionEnd:\n this.clearRedoForInput();\n this.addUndoSnapshot();\n break;\n case PluginEventType.ContentChanged:\n if (!this.state.isRestoring) {\n this.clearRedoForInput();\n }\n break;\n }\n }\n\n private onKeyDown(evt: KeyboardEvent): void {\n // Handle backspace/delete when there is a selection to take a snapshot\n // since we want the state prior to deletion restorable\n if (evt.which == Keys.BACKSPACE || evt.which == Keys.DELETE) {\n if (evt.which == Keys.BACKSPACE && this.canUndoAutoComplete()) {\n evt.preventDefault();\n this.editor.undo();\n this.state.autoCompletePosition = null;\n this.lastKeyPress = evt.which;\n } else {\n let selectionRange = this.editor.getSelectionRange();\n\n // Add snapshot when\n // 1. Something has been selected (not collapsed), or\n // 2. It has a different key code from the last keyDown event (to prevent adding too many snapshot when keeping press the same key), or\n // 3. Ctrl/Meta key is pressed so that a whole word will be deleted\n if (\n selectionRange &&\n (!selectionRange.collapsed ||\n this.lastKeyPress != evt.which ||\n isCtrlOrMetaPressed(evt))\n ) {\n this.addUndoSnapshot();\n }\n\n // Since some content is deleted, always set hasNewContent to true so that we will take undo snapshot next time\n this.state.hasNewContent = true;\n this.lastKeyPress = evt.which;\n }\n } else if (evt.which >= Keys.PAGEUP && evt.which <= Keys.DOWN) {\n // PageUp, PageDown, Home, End, Left, Right, Up, Down\n if (this.state.hasNewContent) {\n this.addUndoSnapshot();\n }\n this.lastKeyPress = 0;\n }\n }\n\n private onKeyPress(evt: KeyboardEvent): void {\n if (evt.metaKey) {\n // if metaKey is pressed, simply return since no actual effect will be taken on the editor.\n // this is to prevent changing hasNewContent to true when meta + v to paste on Safari.\n return;\n }\n\n let range = this.editor.getSelectionRange();\n if (\n (range && !range.collapsed) ||\n (evt.which == Keys.SPACE && this.lastKeyPress != Keys.SPACE) ||\n evt.which == Keys.ENTER\n ) {\n this.addUndoSnapshot();\n if (evt.which == Keys.ENTER) {\n // Treat ENTER as new content so if there is no input after ENTER and undo,\n // we restore the snapshot before ENTER\n this.state.hasNewContent = true;\n }\n } else {\n this.clearRedoForInput();\n }\n\n this.lastKeyPress = evt.which;\n }\n\n private clearRedoForInput() {\n this.state.snapshotsService.clearRedo();\n this.lastKeyPress = 0;\n this.state.hasNewContent = true;\n }\n\n private canUndoAutoComplete() {\n return (\n this.state.snapshotsService.canUndoAutoComplete() &&\n this.state.autoCompletePosition?.equalTo(this.editor.getFocusedPosition())\n );\n }\n\n private addUndoSnapshot() {\n this.editor.addUndoSnapshot();\n this.state.autoCompletePosition = null;\n }\n}\n\nfunction createUndoSnapshots(): UndoSnapshotsService {\n const snapshots = createSnapshots(MAX_SIZE_LIMIT);\n\n return {\n canMove: (delta: number): boolean => canMoveCurrentSnapshot(snapshots, delta),\n move: (delta: number): string => moveCurrentSnapshot(snapshots, delta),\n addSnapshot: (snapshot: string, isAutoCompleteSnapshot: boolean) =>\n addSnapshot(snapshots, snapshot, isAutoCompleteSnapshot),\n clearRedo: () => clearProceedingSnapshots(snapshots),\n canUndoAutoComplete: () => canUndoAutoComplete(snapshots),\n };\n}\n","import * as Color from 'color';\n\nconst baseLValue = 21.247; // L-value of #333333\n\n/**\n * Get dark mode color for a given color\n * @param color The color to calculate from\n */\nexport default function getDarkColor(color: string): string {\n try {\n const computedColor = Color(color || undefined);\n const colorLab = computedColor.lab().array();\n const newLValue = (100 - colorLab[0]) * ((100 - baseLValue) / 100) + baseLValue;\n color = Color.lab(newLValue, colorLab[1], colorLab[2])\n .rgb()\n .alpha(computedColor.alpha())\n .toString();\n } catch {}\n\n return color;\n}\n","'use strict';\n\nvar colorString = require('color-string');\nvar convert = require('color-convert');\n\nvar _slice = [].slice;\n\nvar skippedModels = [\n\t// to be honest, I don't really feel like keyword belongs in color convert, but eh.\n\t'keyword',\n\n\t// gray conflicts with some method names, and has its own method defined.\n\t'gray',\n\n\t// shouldn't really be in color-convert either...\n\t'hex'\n];\n\nvar hashedModelKeys = {};\nObject.keys(convert).forEach(function (model) {\n\thashedModelKeys[_slice.call(convert[model].labels).sort().join('')] = model;\n});\n\nvar limiters = {};\n\nfunction Color(obj, model) {\n\tif (!(this instanceof Color)) {\n\t\treturn new Color(obj, model);\n\t}\n\n\tif (model && model in skippedModels) {\n\t\tmodel = null;\n\t}\n\n\tif (model && !(model in convert)) {\n\t\tthrow new Error('Unknown model: ' + model);\n\t}\n\n\tvar i;\n\tvar channels;\n\n\tif (obj == null) { // eslint-disable-line no-eq-null,eqeqeq\n\t\tthis.model = 'rgb';\n\t\tthis.color = [0, 0, 0];\n\t\tthis.valpha = 1;\n\t} else if (obj instanceof Color) {\n\t\tthis.model = obj.model;\n\t\tthis.color = obj.color.slice();\n\t\tthis.valpha = obj.valpha;\n\t} else if (typeof obj === 'string') {\n\t\tvar result = colorString.get(obj);\n\t\tif (result === null) {\n\t\t\tthrow new Error('Unable to parse color from string: ' + obj);\n\t\t}\n\n\t\tthis.model = result.model;\n\t\tchannels = convert[this.model].channels;\n\t\tthis.color = result.value.slice(0, channels);\n\t\tthis.valpha = typeof result.value[channels] === 'number' ? result.value[channels] : 1;\n\t} else if (obj.length) {\n\t\tthis.model = model || 'rgb';\n\t\tchannels = convert[this.model].channels;\n\t\tvar newArr = _slice.call(obj, 0, channels);\n\t\tthis.color = zeroArray(newArr, channels);\n\t\tthis.valpha = typeof obj[channels] === 'number' ? obj[channels] : 1;\n\t} else if (typeof obj === 'number') {\n\t\t// this is always RGB - can be converted later on.\n\t\tobj &= 0xFFFFFF;\n\t\tthis.model = 'rgb';\n\t\tthis.color = [\n\t\t\t(obj >> 16) & 0xFF,\n\t\t\t(obj >> 8) & 0xFF,\n\t\t\tobj & 0xFF\n\t\t];\n\t\tthis.valpha = 1;\n\t} else {\n\t\tthis.valpha = 1;\n\n\t\tvar keys = Object.keys(obj);\n\t\tif ('alpha' in obj) {\n\t\t\tkeys.splice(keys.indexOf('alpha'), 1);\n\t\t\tthis.valpha = typeof obj.alpha === 'number' ? obj.alpha : 0;\n\t\t}\n\n\t\tvar hashedKeys = keys.sort().join('');\n\t\tif (!(hashedKeys in hashedModelKeys)) {\n\t\t\tthrow new Error('Unable to parse color from object: ' + JSON.stringify(obj));\n\t\t}\n\n\t\tthis.model = hashedModelKeys[hashedKeys];\n\n\t\tvar labels = convert[this.model].labels;\n\t\tvar color = [];\n\t\tfor (i = 0; i < labels.length; i++) {\n\t\t\tcolor.push(obj[labels[i]]);\n\t\t}\n\n\t\tthis.color = zeroArray(color);\n\t}\n\n\t// perform limitations (clamping, etc.)\n\tif (limiters[this.model]) {\n\t\tchannels = convert[this.model].channels;\n\t\tfor (i = 0; i < channels; i++) {\n\t\t\tvar limit = limiters[this.model][i];\n\t\t\tif (limit) {\n\t\t\t\tthis.color[i] = limit(this.color[i]);\n\t\t\t}\n\t\t}\n\t}\n\n\tthis.valpha = Math.max(0, Math.min(1, this.valpha));\n\n\tif (Object.freeze) {\n\t\tObject.freeze(this);\n\t}\n}\n\nColor.prototype = {\n\ttoString: function () {\n\t\treturn this.string();\n\t},\n\n\ttoJSON: function () {\n\t\treturn this[this.model]();\n\t},\n\n\tstring: function (places) {\n\t\tvar self = this.model in colorString.to ? this : this.rgb();\n\t\tself = self.round(typeof places === 'number' ? places : 1);\n\t\tvar args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);\n\t\treturn colorString.to[self.model](args);\n\t},\n\n\tpercentString: function (places) {\n\t\tvar self = this.rgb().round(typeof places === 'number' ? places : 1);\n\t\tvar args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);\n\t\treturn colorString.to.rgb.percent(args);\n\t},\n\n\tarray: function () {\n\t\treturn this.valpha === 1 ? this.color.slice() : this.color.concat(this.valpha);\n\t},\n\n\tobject: function () {\n\t\tvar result = {};\n\t\tvar channels = convert[this.model].channels;\n\t\tvar labels = convert[this.model].labels;\n\n\t\tfor (var i = 0; i < channels; i++) {\n\t\t\tresult[labels[i]] = this.color[i];\n\t\t}\n\n\t\tif (this.valpha !== 1) {\n\t\t\tresult.alpha = this.valpha;\n\t\t}\n\n\t\treturn result;\n\t},\n\n\tunitArray: function () {\n\t\tvar rgb = this.rgb().color;\n\t\trgb[0] /= 255;\n\t\trgb[1] /= 255;\n\t\trgb[2] /= 255;\n\n\t\tif (this.valpha !== 1) {\n\t\t\trgb.push(this.valpha);\n\t\t}\n\n\t\treturn rgb;\n\t},\n\n\tunitObject: function () {\n\t\tvar rgb = this.rgb().object();\n\t\trgb.r /= 255;\n\t\trgb.g /= 255;\n\t\trgb.b /= 255;\n\n\t\tif (this.valpha !== 1) {\n\t\t\trgb.alpha = this.valpha;\n\t\t}\n\n\t\treturn rgb;\n\t},\n\n\tround: function (places) {\n\t\tplaces = Math.max(places || 0, 0);\n\t\treturn new Color(this.color.map(roundToPlace(places)).concat(this.valpha), this.model);\n\t},\n\n\talpha: function (val) {\n\t\tif (arguments.length) {\n\t\t\treturn new Color(this.color.concat(Math.max(0, Math.min(1, val))), this.model);\n\t\t}\n\n\t\treturn this.valpha;\n\t},\n\n\t// rgb\n\tred: getset('rgb', 0, maxfn(255)),\n\tgreen: getset('rgb', 1, maxfn(255)),\n\tblue: getset('rgb', 2, maxfn(255)),\n\n\thue: getset(['hsl', 'hsv', 'hsl', 'hwb', 'hcg'], 0, function (val) { return ((val % 360) + 360) % 360; }), // eslint-disable-line brace-style\n\n\tsaturationl: getset('hsl', 1, maxfn(100)),\n\tlightness: getset('hsl', 2, maxfn(100)),\n\n\tsaturationv: getset('hsv', 1, maxfn(100)),\n\tvalue: getset('hsv', 2, maxfn(100)),\n\n\tchroma: getset('hcg', 1, maxfn(100)),\n\tgray: getset('hcg', 2, maxfn(100)),\n\n\twhite: getset('hwb', 1, maxfn(100)),\n\twblack: getset('hwb', 2, maxfn(100)),\n\n\tcyan: getset('cmyk', 0, maxfn(100)),\n\tmagenta: getset('cmyk', 1, maxfn(100)),\n\tyellow: getset('cmyk', 2, maxfn(100)),\n\tblack: getset('cmyk', 3, maxfn(100)),\n\n\tx: getset('xyz', 0, maxfn(100)),\n\ty: getset('xyz', 1, maxfn(100)),\n\tz: getset('xyz', 2, maxfn(100)),\n\n\tl: getset('lab', 0, maxfn(100)),\n\ta: getset('lab', 1),\n\tb: getset('lab', 2),\n\n\tkeyword: function (val) {\n\t\tif (arguments.length) {\n\t\t\treturn new Color(val);\n\t\t}\n\n\t\treturn convert[this.model].keyword(this.color);\n\t},\n\n\thex: function (val) {\n\t\tif (arguments.length) {\n\t\t\treturn new Color(val);\n\t\t}\n\n\t\treturn colorString.to.hex(this.rgb().round().color);\n\t},\n\n\trgbNumber: function () {\n\t\tvar rgb = this.rgb().color;\n\t\treturn ((rgb[0] & 0xFF) << 16) | ((rgb[1] & 0xFF) << 8) | (rgb[2] & 0xFF);\n\t},\n\n\tluminosity: function () {\n\t\t// http://www.w3.org/TR/WCAG20/#relativeluminancedef\n\t\tvar rgb = this.rgb().color;\n\n\t\tvar lum = [];\n\t\tfor (var i = 0; i < rgb.length; i++) {\n\t\t\tvar chan = rgb[i] / 255;\n\t\t\tlum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);\n\t\t}\n\n\t\treturn 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];\n\t},\n\n\tcontrast: function (color2) {\n\t\t// http://www.w3.org/TR/WCAG20/#contrast-ratiodef\n\t\tvar lum1 = this.luminosity();\n\t\tvar lum2 = color2.luminosity();\n\n\t\tif (lum1 > lum2) {\n\t\t\treturn (lum1 + 0.05) / (lum2 + 0.05);\n\t\t}\n\n\t\treturn (lum2 + 0.05) / (lum1 + 0.05);\n\t},\n\n\tlevel: function (color2) {\n\t\tvar contrastRatio = this.contrast(color2);\n\t\tif (contrastRatio >= 7.1) {\n\t\t\treturn 'AAA';\n\t\t}\n\n\t\treturn (contrastRatio >= 4.5) ? 'AA' : '';\n\t},\n\n\tisDark: function () {\n\t\t// YIQ equation from http://24ways.org/2010/calculating-color-contrast\n\t\tvar rgb = this.rgb().color;\n\t\tvar yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\n\t\treturn yiq < 128;\n\t},\n\n\tisLight: function () {\n\t\treturn !this.isDark();\n\t},\n\n\tnegate: function () {\n\t\tvar rgb = this.rgb();\n\t\tfor (var i = 0; i < 3; i++) {\n\t\t\trgb.color[i] = 255 - rgb.color[i];\n\t\t}\n\t\treturn rgb;\n\t},\n\n\tlighten: function (ratio) {\n\t\tvar hsl = this.hsl();\n\t\thsl.color[2] += hsl.color[2] * ratio;\n\t\treturn hsl;\n\t},\n\n\tdarken: function (ratio) {\n\t\tvar hsl = this.hsl();\n\t\thsl.color[2] -= hsl.color[2] * ratio;\n\t\treturn hsl;\n\t},\n\n\tsaturate: function (ratio) {\n\t\tvar hsl = this.hsl();\n\t\thsl.color[1] += hsl.color[1] * ratio;\n\t\treturn hsl;\n\t},\n\n\tdesaturate: function (ratio) {\n\t\tvar hsl = this.hsl();\n\t\thsl.color[1] -= hsl.color[1] * ratio;\n\t\treturn hsl;\n\t},\n\n\twhiten: function (ratio) {\n\t\tvar hwb = this.hwb();\n\t\thwb.color[1] += hwb.color[1] * ratio;\n\t\treturn hwb;\n\t},\n\n\tblacken: function (ratio) {\n\t\tvar hwb = this.hwb();\n\t\thwb.color[2] += hwb.color[2] * ratio;\n\t\treturn hwb;\n\t},\n\n\tgrayscale: function () {\n\t\t// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale\n\t\tvar rgb = this.rgb().color;\n\t\tvar val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;\n\t\treturn Color.rgb(val, val, val);\n\t},\n\n\tfade: function (ratio) {\n\t\treturn this.alpha(this.valpha - (this.valpha * ratio));\n\t},\n\n\topaquer: function (ratio) {\n\t\treturn this.alpha(this.valpha + (this.valpha * ratio));\n\t},\n\n\trotate: function (degrees) {\n\t\tvar hsl = this.hsl();\n\t\tvar hue = hsl.color[0];\n\t\thue = (hue + degrees) % 360;\n\t\thue = hue < 0 ? 360 + hue : hue;\n\t\thsl.color[0] = hue;\n\t\treturn hsl;\n\t},\n\n\tmix: function (mixinColor, weight) {\n\t\t// ported from sass implementation in C\n\t\t// https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209\n\t\tif (!mixinColor || !mixinColor.rgb) {\n\t\t\tthrow new Error('Argument to \"mix\" was not a Color instance, but rather an instance of ' + typeof mixinColor);\n\t\t}\n\t\tvar color1 = mixinColor.rgb();\n\t\tvar color2 = this.rgb();\n\t\tvar p = weight === undefined ? 0.5 : weight;\n\n\t\tvar w = 2 * p - 1;\n\t\tvar a = color1.alpha() - color2.alpha();\n\n\t\tvar w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;\n\t\tvar w2 = 1 - w1;\n\n\t\treturn Color.rgb(\n\t\t\t\tw1 * color1.red() + w2 * color2.red(),\n\t\t\t\tw1 * color1.green() + w2 * color2.green(),\n\t\t\t\tw1 * color1.blue() + w2 * color2.blue(),\n\t\t\t\tcolor1.alpha() * p + color2.alpha() * (1 - p));\n\t}\n};\n\n// model conversion methods and static constructors\nObject.keys(convert).forEach(function (model) {\n\tif (skippedModels.indexOf(model) !== -1) {\n\t\treturn;\n\t}\n\n\tvar channels = convert[model].channels;\n\n\t// conversion methods\n\tColor.prototype[model] = function () {\n\t\tif (this.model === model) {\n\t\t\treturn new Color(this);\n\t\t}\n\n\t\tif (arguments.length) {\n\t\t\treturn new Color(arguments, model);\n\t\t}\n\n\t\tvar newAlpha = typeof arguments[channels] === 'number' ? channels : this.valpha;\n\t\treturn new Color(assertArray(convert[this.model][model].raw(this.color)).concat(newAlpha), model);\n\t};\n\n\t// 'static' construction methods\n\tColor[model] = function (color) {\n\t\tif (typeof color === 'number') {\n\t\t\tcolor = zeroArray(_slice.call(arguments), channels);\n\t\t}\n\t\treturn new Color(color, model);\n\t};\n});\n\nfunction roundTo(num, places) {\n\treturn Number(num.toFixed(places));\n}\n\nfunction roundToPlace(places) {\n\treturn function (num) {\n\t\treturn roundTo(num, places);\n\t};\n}\n\nfunction getset(model, channel, modifier) {\n\tmodel = Array.isArray(model) ? model : [model];\n\n\tmodel.forEach(function (m) {\n\t\t(limiters[m] || (limiters[m] = []))[channel] = modifier;\n\t});\n\n\tmodel = model[0];\n\n\treturn function (val) {\n\t\tvar result;\n\n\t\tif (arguments.length) {\n\t\t\tif (modifier) {\n\t\t\t\tval = modifier(val);\n\t\t\t}\n\n\t\t\tresult = this[model]();\n\t\t\tresult.color[channel] = val;\n\t\t\treturn result;\n\t\t}\n\n\t\tresult = this[model]().color[channel];\n\t\tif (modifier) {\n\t\t\tresult = modifier(result);\n\t\t}\n\n\t\treturn result;\n\t};\n}\n\nfunction maxfn(max) {\n\treturn function (v) {\n\t\treturn Math.max(0, Math.min(max, v));\n\t};\n}\n\nfunction assertArray(val) {\n\treturn Array.isArray(val) ? val : [val];\n}\n\nfunction zeroArray(arr, length) {\n\tfor (var i = 0; i < length; i++) {\n\t\tif (typeof arr[i] !== 'number') {\n\t\t\tarr[i] = 0;\n\t\t}\n\t}\n\n\treturn arr;\n}\n\nmodule.exports = Color;\n","/* MIT license */\nvar colorNames = require('color-name');\nvar swizzle = require('simple-swizzle');\n\nvar reverseNames = {};\n\n// create a list of reverse color names\nfor (var name in colorNames) {\n\tif (colorNames.hasOwnProperty(name)) {\n\t\treverseNames[colorNames[name]] = name;\n\t}\n}\n\nvar cs = module.exports = {\n\tto: {},\n\tget: {}\n};\n\ncs.get = function (string) {\n\tvar prefix = string.substring(0, 3).toLowerCase();\n\tvar val;\n\tvar model;\n\tswitch (prefix) {\n\t\tcase 'hsl':\n\t\t\tval = cs.get.hsl(string);\n\t\t\tmodel = 'hsl';\n\t\t\tbreak;\n\t\tcase 'hwb':\n\t\t\tval = cs.get.hwb(string);\n\t\t\tmodel = 'hwb';\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tval = cs.get.rgb(string);\n\t\t\tmodel = 'rgb';\n\t\t\tbreak;\n\t}\n\n\tif (!val) {\n\t\treturn null;\n\t}\n\n\treturn {model: model, value: val};\n};\n\ncs.get.rgb = function (string) {\n\tif (!string) {\n\t\treturn null;\n\t}\n\n\tvar abbr = /^#([a-f0-9]{3,4})$/i;\n\tvar hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i;\n\tvar rgba = /^rgba?\\(\\s*([+-]?\\d+)\\s*,\\s*([+-]?\\d+)\\s*,\\s*([+-]?\\d+)\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/;\n\tvar per = /^rgba?\\(\\s*([+-]?[\\d\\.]+)\\%\\s*,\\s*([+-]?[\\d\\.]+)\\%\\s*,\\s*([+-]?[\\d\\.]+)\\%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/;\n\tvar keyword = /(\\D+)/;\n\n\tvar rgb = [0, 0, 0, 1];\n\tvar match;\n\tvar i;\n\tvar hexAlpha;\n\n\tif (match = string.match(hex)) {\n\t\thexAlpha = match[2];\n\t\tmatch = match[1];\n\n\t\tfor (i = 0; i < 3; i++) {\n\t\t\t// https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19\n\t\t\tvar i2 = i * 2;\n\t\t\trgb[i] = parseInt(match.slice(i2, i2 + 2), 16);\n\t\t}\n\n\t\tif (hexAlpha) {\n\t\t\trgb[3] = parseInt(hexAlpha, 16) / 255;\n\t\t}\n\t} else if (match = string.match(abbr)) {\n\t\tmatch = match[1];\n\t\thexAlpha = match[3];\n\n\t\tfor (i = 0; i < 3; i++) {\n\t\t\trgb[i] = parseInt(match[i] + match[i], 16);\n\t\t}\n\n\t\tif (hexAlpha) {\n\t\t\trgb[3] = parseInt(hexAlpha + hexAlpha, 16) / 255;\n\t\t}\n\t} else if (match = string.match(rgba)) {\n\t\tfor (i = 0; i < 3; i++) {\n\t\t\trgb[i] = parseInt(match[i + 1], 0);\n\t\t}\n\n\t\tif (match[4]) {\n\t\t\trgb[3] = parseFloat(match[4]);\n\t\t}\n\t} else if (match = string.match(per)) {\n\t\tfor (i = 0; i < 3; i++) {\n\t\t\trgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);\n\t\t}\n\n\t\tif (match[4]) {\n\t\t\trgb[3] = parseFloat(match[4]);\n\t\t}\n\t} else if (match = string.match(keyword)) {\n\t\tif (match[1] === 'transparent') {\n\t\t\treturn [0, 0, 0, 0];\n\t\t}\n\n\t\trgb = colorNames[match[1]];\n\n\t\tif (!rgb) {\n\t\t\treturn null;\n\t\t}\n\n\t\trgb[3] = 1;\n\n\t\treturn rgb;\n\t} else {\n\t\treturn null;\n\t}\n\n\tfor (i = 0; i < 3; i++) {\n\t\trgb[i] = clamp(rgb[i], 0, 255);\n\t}\n\trgb[3] = clamp(rgb[3], 0, 1);\n\n\treturn rgb;\n};\n\ncs.get.hsl = function (string) {\n\tif (!string) {\n\t\treturn null;\n\t}\n\n\tvar hsl = /^hsla?\\(\\s*([+-]?(?:\\d{0,3}\\.)?\\d+)(?:deg)?\\s*,\\s*([+-]?[\\d\\.]+)%\\s*,\\s*([+-]?[\\d\\.]+)%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/;\n\tvar match = string.match(hsl);\n\n\tif (match) {\n\t\tvar alpha = parseFloat(match[4]);\n\t\tvar h = (parseFloat(match[1]) + 360) % 360;\n\t\tvar s = clamp(parseFloat(match[2]), 0, 100);\n\t\tvar l = clamp(parseFloat(match[3]), 0, 100);\n\t\tvar a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);\n\n\t\treturn [h, s, l, a];\n\t}\n\n\treturn null;\n};\n\ncs.get.hwb = function (string) {\n\tif (!string) {\n\t\treturn null;\n\t}\n\n\tvar hwb = /^hwb\\(\\s*([+-]?\\d{0,3}(?:\\.\\d+)?)(?:deg)?\\s*,\\s*([+-]?[\\d\\.]+)%\\s*,\\s*([+-]?[\\d\\.]+)%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/;\n\tvar match = string.match(hwb);\n\n\tif (match) {\n\t\tvar alpha = parseFloat(match[4]);\n\t\tvar h = ((parseFloat(match[1]) % 360) + 360) % 360;\n\t\tvar w = clamp(parseFloat(match[2]), 0, 100);\n\t\tvar b = clamp(parseFloat(match[3]), 0, 100);\n\t\tvar a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);\n\t\treturn [h, w, b, a];\n\t}\n\n\treturn null;\n};\n\ncs.to.hex = function () {\n\tvar rgba = swizzle(arguments);\n\n\treturn (\n\t\t'#' +\n\t\thexDouble(rgba[0]) +\n\t\thexDouble(rgba[1]) +\n\t\thexDouble(rgba[2]) +\n\t\t(rgba[3] < 1\n\t\t\t? (hexDouble(Math.round(rgba[3] * 255)))\n\t\t\t: '')\n\t);\n};\n\ncs.to.rgb = function () {\n\tvar rgba = swizzle(arguments);\n\n\treturn rgba.length < 4 || rgba[3] === 1\n\t\t? 'rgb(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ')'\n\t\t: 'rgba(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ', ' + rgba[3] + ')';\n};\n\ncs.to.rgb.percent = function () {\n\tvar rgba = swizzle(arguments);\n\n\tvar r = Math.round(rgba[0] / 255 * 100);\n\tvar g = Math.round(rgba[1] / 255 * 100);\n\tvar b = Math.round(rgba[2] / 255 * 100);\n\n\treturn rgba.length < 4 || rgba[3] === 1\n\t\t? 'rgb(' + r + '%, ' + g + '%, ' + b + '%)'\n\t\t: 'rgba(' + r + '%, ' + g + '%, ' + b + '%, ' + rgba[3] + ')';\n};\n\ncs.to.hsl = function () {\n\tvar hsla = swizzle(arguments);\n\treturn hsla.length < 4 || hsla[3] === 1\n\t\t? 'hsl(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%)'\n\t\t: 'hsla(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%, ' + hsla[3] + ')';\n};\n\n// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax\n// (hwb have alpha optional & 1 is default value)\ncs.to.hwb = function () {\n\tvar hwba = swizzle(arguments);\n\n\tvar a = '';\n\tif (hwba.length >= 4 && hwba[3] !== 1) {\n\t\ta = ', ' + hwba[3];\n\t}\n\n\treturn 'hwb(' + hwba[0] + ', ' + hwba[1] + '%, ' + hwba[2] + '%' + a + ')';\n};\n\ncs.to.keyword = function (rgb) {\n\treturn reverseNames[rgb.slice(0, 3)];\n};\n\n// helpers\nfunction clamp(num, min, max) {\n\treturn Math.min(Math.max(min, num), max);\n}\n\nfunction hexDouble(num) {\n\tvar str = num.toString(16).toUpperCase();\n\treturn (str.length < 2) ? '0' + str : str;\n}\n","'use strict';\n\nvar isArrayish = require('is-arrayish');\n\nvar concat = Array.prototype.concat;\nvar slice = Array.prototype.slice;\n\nvar swizzle = module.exports = function swizzle(args) {\n\tvar results = [];\n\n\tfor (var i = 0, len = args.length; i < len; i++) {\n\t\tvar arg = args[i];\n\n\t\tif (isArrayish(arg)) {\n\t\t\t// http://jsperf.com/javascript-array-concat-vs-push/98\n\t\t\tresults = concat.call(results, slice.call(arg));\n\t\t} else {\n\t\t\tresults.push(arg);\n\t\t}\n\t}\n\n\treturn results;\n};\n\nswizzle.wrap = function (fn) {\n\treturn function () {\n\t\treturn fn(swizzle(arguments));\n\t};\n};\n","module.exports = function isArrayish(obj) {\n\tif (!obj || typeof obj === 'string') {\n\t\treturn false;\n\t}\n\n\treturn obj instanceof Array || Array.isArray(obj) ||\n\t\t(obj.length >= 0 && (obj.splice instanceof Function ||\n\t\t\t(Object.getOwnPropertyDescriptor(obj, (obj.length - 1)) && obj.constructor.name !== 'String')));\n};\n","var conversions = require('./conversions');\nvar route = require('./route');\n\nvar convert = {};\n\nvar models = Object.keys(conversions);\n\nfunction wrapRaw(fn) {\n\tvar wrappedFn = function (args) {\n\t\tif (args === undefined || args === null) {\n\t\t\treturn args;\n\t\t}\n\n\t\tif (arguments.length > 1) {\n\t\t\targs = Array.prototype.slice.call(arguments);\n\t\t}\n\n\t\treturn fn(args);\n\t};\n\n\t// preserve .conversion property if there is one\n\tif ('conversion' in fn) {\n\t\twrappedFn.conversion = fn.conversion;\n\t}\n\n\treturn wrappedFn;\n}\n\nfunction wrapRounded(fn) {\n\tvar wrappedFn = function (args) {\n\t\tif (args === undefined || args === null) {\n\t\t\treturn args;\n\t\t}\n\n\t\tif (arguments.length > 1) {\n\t\t\targs = Array.prototype.slice.call(arguments);\n\t\t}\n\n\t\tvar result = fn(args);\n\n\t\t// we're assuming the result is an array here.\n\t\t// see notice in conversions.js; don't use box types\n\t\t// in conversion functions.\n\t\tif (typeof result === 'object') {\n\t\t\tfor (var len = result.length, i = 0; i < len; i++) {\n\t\t\t\tresult[i] = Math.round(result[i]);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t};\n\n\t// preserve .conversion property if there is one\n\tif ('conversion' in fn) {\n\t\twrappedFn.conversion = fn.conversion;\n\t}\n\n\treturn wrappedFn;\n}\n\nmodels.forEach(function (fromModel) {\n\tconvert[fromModel] = {};\n\n\tObject.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});\n\tObject.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});\n\n\tvar routes = route(fromModel);\n\tvar routeModels = Object.keys(routes);\n\n\trouteModels.forEach(function (toModel) {\n\t\tvar fn = routes[toModel];\n\n\t\tconvert[fromModel][toModel] = wrapRounded(fn);\n\t\tconvert[fromModel][toModel].raw = wrapRaw(fn);\n\t});\n});\n\nmodule.exports = convert;\n","var conversions = require('./conversions');\n\n/*\n\tthis function routes a model to all other models.\n\n\tall functions that are routed have a property `.conversion` attached\n\tto the returned synthetic function. This property is an array\n\tof strings, each with the steps in between the 'from' and 'to'\n\tcolor models (inclusive).\n\n\tconversions that are not possible simply are not included.\n*/\n\nfunction buildGraph() {\n\tvar graph = {};\n\t// https://jsperf.com/object-keys-vs-for-in-with-closure/3\n\tvar models = Object.keys(conversions);\n\n\tfor (var len = models.length, i = 0; i < len; i++) {\n\t\tgraph[models[i]] = {\n\t\t\t// http://jsperf.com/1-vs-infinity\n\t\t\t// micro-opt, but this is simple.\n\t\t\tdistance: -1,\n\t\t\tparent: null\n\t\t};\n\t}\n\n\treturn graph;\n}\n\n// https://en.wikipedia.org/wiki/Breadth-first_search\nfunction deriveBFS(fromModel) {\n\tvar graph = buildGraph();\n\tvar queue = [fromModel]; // unshift -> queue -> pop\n\n\tgraph[fromModel].distance = 0;\n\n\twhile (queue.length) {\n\t\tvar current = queue.pop();\n\t\tvar adjacents = Object.keys(conversions[current]);\n\n\t\tfor (var len = adjacents.length, i = 0; i < len; i++) {\n\t\t\tvar adjacent = adjacents[i];\n\t\t\tvar node = graph[adjacent];\n\n\t\t\tif (node.distance === -1) {\n\t\t\t\tnode.distance = graph[current].distance + 1;\n\t\t\t\tnode.parent = current;\n\t\t\t\tqueue.unshift(adjacent);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn graph;\n}\n\nfunction link(from, to) {\n\treturn function (args) {\n\t\treturn to(from(args));\n\t};\n}\n\nfunction wrapConversion(toModel, graph) {\n\tvar path = [graph[toModel].parent, toModel];\n\tvar fn = conversions[graph[toModel].parent][toModel];\n\n\tvar cur = graph[toModel].parent;\n\twhile (graph[cur].parent) {\n\t\tpath.unshift(graph[cur].parent);\n\t\tfn = link(conversions[graph[cur].parent][cur], fn);\n\t\tcur = graph[cur].parent;\n\t}\n\n\tfn.conversion = path;\n\treturn fn;\n}\n\nmodule.exports = function (fromModel) {\n\tvar graph = deriveBFS(fromModel);\n\tvar conversion = {};\n\n\tvar models = Object.keys(graph);\n\tfor (var len = models.length, i = 0; i < len; i++) {\n\t\tvar toModel = models[i];\n\t\tvar node = graph[toModel];\n\n\t\tif (node.parent === null) {\n\t\t\t// no possible conversion, or this node is the source model.\n\t\t\tcontinue;\n\t\t}\n\n\t\tconversion[toModel] = wrapConversion(toModel, graph);\n\t}\n\n\treturn conversion;\n};\n\n","export * from './plugins/ContentEdit/index';\n","export { default as ContentEdit } from './ContentEdit';\nexport { default as getAllFeatures } from './getAllFeatures';\n","import getAllFeatures from './getAllFeatures';\nimport {\n ContentEditFeatureSettings,\n EditorPlugin,\n GenericContentEditFeature,\n IEditor,\n PluginEvent,\n} from 'roosterjs-editor-types';\n\n/**\n * An editor plugin to handle content edit event.\n * The following cases are included:\n * 1. Auto increase/decrease indentation on Tab, Shift+tab\n * 2. Enter, Backspace on empty list item\n * 3. Enter, Backspace on empty blockquote line\n * 4. Auto bullet/numbering\n * 5. Auto link\n * 6. Tab in table\n * 7. Up/Down in table\n * 8. Manage list style\n */\nexport default class ContentEdit implements EditorPlugin {\n /**\n * Create instance of ContentEdit plugin\n * @param settingsOverride An optional feature set to override default feature settings\n * @param additionalFeatures Optional. More features to add\n */\n constructor(\n private settingsOverride?: Partial,\n private additionalFeatures?: GenericContentEditFeature[]\n ) {}\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'ContentEdit';\n }\n\n /**\n * Initialize this plugin\n * @param editor The editor instance\n */\n initialize(editor: IEditor): void {\n const features: GenericContentEditFeature[] = [];\n const allFeatures = getAllFeatures();\n\n Object.keys(allFeatures).forEach((key: keyof typeof allFeatures) => {\n const feature = allFeatures[key];\n const hasSettingForKey =\n this.settingsOverride && this.settingsOverride[key] !== undefined;\n\n if (\n (hasSettingForKey && this.settingsOverride[key]) ||\n (!hasSettingForKey && !feature.defaultDisabled)\n ) {\n features.push(feature);\n }\n });\n\n features\n .concat(this.additionalFeatures || [])\n .forEach(feature => editor.addContentEditFeature(feature));\n }\n\n /**\n * Dispose this plugin\n */\n dispose(): void {}\n}\n","import { removeLink, replaceWithNode } from 'roosterjs-editor-api';\nimport {\n AutoLinkFeatureSettings,\n BuildInEditFeature,\n ChangeSource,\n ClipboardData,\n IEditor,\n Keys,\n LinkData,\n PluginEvent,\n PluginEventType,\n PluginKeyboardEvent,\n} from 'roosterjs-editor-types';\nimport {\n cacheGetEventData,\n clearEventDataCache,\n LinkInlineElement,\n matchLink,\n} from 'roosterjs-editor-dom';\n\n/**\n * When user type, they may end a link with a punctuation, i.e. www.bing.com;\n * we need to trim off the trailing punctuation before turning it to link match\n */\nconst TRAILING_PUNCTUATION_REGEX = /[.+=\\s:;\"',>]+$/i;\nconst MINIMUM_LENGTH = 5;\n\n/**\n * AutoLink edit feature, provides the ability to automatically convert text user typed or pasted\n * in hyperlink format into a real hyperlink\n */\nconst AutoLink: BuildInEditFeature = {\n keys: [Keys.ENTER, Keys.SPACE, Keys.CONTENTCHANGED],\n shouldHandleEvent: cacheGetLinkData,\n handleEvent: autoLink,\n};\n\n/**\n * UnlinkWhenBackspaceAfterLink edit feature, provides the ability to convert a hyperlink back into text\n * if user presses BACKSPACE right after a hyperlink\n */\nconst UnlinkWhenBackspaceAfterLink: BuildInEditFeature = {\n keys: [Keys.BACKSPACE],\n shouldHandleEvent: hasLinkBeforeCursor,\n handleEvent: (event, editor) => {\n event.rawEvent.preventDefault();\n removeLink(editor);\n },\n defaultDisabled: true,\n};\n\nfunction cacheGetLinkData(event: PluginEvent, editor: IEditor): LinkData {\n return event.eventType == PluginEventType.KeyDown ||\n (event.eventType == PluginEventType.ContentChanged && event.source == ChangeSource.Paste)\n ? cacheGetEventData(event, 'LINK_DATA', () => {\n // First try to match link from the whole paste string from the plain text in clipboard.\n // This helps when we paste a link next to some existing character, and the text we got\n // from clipboard will only contain what we pasted, any existing characters will not\n // be included.\n let clipboardData =\n event.eventType == PluginEventType.ContentChanged &&\n event.source == ChangeSource.Paste &&\n (event.data as ClipboardData);\n let link = matchLink((clipboardData.text || '').trim());\n let searcher = editor.getContentSearcherOfCursor(event);\n\n // In case the matched link is already inside a
tag, we do a range search.\n // getRangeFromText will return null if the given text is already in a LinkInlineElement\n if (link && searcher.getRangeFromText(link.originalUrl, false /*exactMatch*/)) {\n return link;\n }\n\n let word = searcher && searcher.getWordBefore();\n if (word && word.length > MINIMUM_LENGTH) {\n // Check for trailing punctuation\n let trailingPunctuations = word.match(TRAILING_PUNCTUATION_REGEX);\n let trailingPunctuation = (trailingPunctuations || [])[0] || '';\n let candidate = word.substring(0, word.length - trailingPunctuation.length);\n\n // Do special handling for ')', '}', ']'\n ['()', '{}', '[]'].forEach(str => {\n if (\n candidate[candidate.length - 1] == str[1] &&\n candidate.indexOf(str[0]) < 0\n ) {\n candidate = candidate.substr(0, candidate.length - 1);\n }\n });\n\n // Match and replace in editor\n return matchLink(candidate);\n }\n return null;\n })\n : null;\n}\n\nfunction hasLinkBeforeCursor(event: PluginKeyboardEvent, editor: IEditor): boolean {\n let contentSearcher = editor.getContentSearcherOfCursor(event);\n let inline = contentSearcher.getInlineElementBefore();\n return inline instanceof LinkInlineElement;\n}\n\nfunction autoLink(event: PluginEvent, editor: IEditor) {\n let anchor = editor.getDocument().createElement('a');\n let linkData = cacheGetLinkData(event, editor);\n\n // Need to get searcher before we enter the async callback since the callback can happen when cursor is moved to next line\n // and at that time a new searcher won't be able to find the link text to replace\n let searcher = editor.getContentSearcherOfCursor();\n anchor.textContent = linkData.originalUrl;\n anchor.href = linkData.normalizedUrl;\n\n editor.runAsync(editor => {\n editor.addUndoSnapshot(\n () => {\n replaceWithNode(\n editor,\n linkData.originalUrl,\n anchor,\n false /* exactMatch */,\n searcher\n );\n\n // The content at cursor has changed. Should also clear the cursor data cache\n clearEventDataCache(event);\n return anchor;\n },\n ChangeSource.AutoLink,\n true /*canUndoByBackspace*/\n );\n });\n}\n\n/**\n * @internal\n */\nexport const AutoLinkFeatures: Record<\n keyof AutoLinkFeatureSettings,\n BuildInEditFeature\n> = {\n autoLink: AutoLink,\n unlinkWhenBackspaceAfterLink: UnlinkWhenBackspaceAfterLink,\n};\n","import applyInlineStyle from '../utils/applyInlineStyle';\r\nimport { FontSizeChange, IEditor } from 'roosterjs-editor-types';\r\nimport { getComputedStyle } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Default font size sequence, in pt. Suggest editor UI use this sequence as your font size list,\r\n * So that when increase/decrease font size, the font size can match the sequence of your font size picker\r\n */\r\nexport const FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72];\r\nconst MIN_FONT_SIZE = 1;\r\nconst MAX_FONT_SIZE = 1000;\r\n\r\n/**\r\n * Increase or decrease font size in selection\r\n * @param editor The editor instance\r\n * @param change Whether increase or decrease font size\r\n * @param fontSizes A sorted font size array, in pt. Default value is FONT_SIZES\r\n */\r\nexport default function changeFontSize(\r\n editor: IEditor,\r\n change: FontSizeChange,\r\n fontSizes: number[] = FONT_SIZES\r\n) {\r\n let changeBase: 1 | -1 = change == FontSizeChange.Increase ? 1 : -1;\r\n applyInlineStyle(editor, element => {\r\n let pt = parseFloat(getComputedStyle(element, 'font-size'));\r\n element.style.fontSize = getNewFontSize(pt, changeBase, fontSizes) + 'pt';\r\n let lineHeight = getComputedStyle(element, 'line-height');\r\n if (lineHeight != 'normal') {\r\n element.style.lineHeight = 'normal';\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Get new font size after increase/decrease. This is exported for test only\r\n * @param pt Original font size, in Point\r\n * @param changeBase Changed value, 1 means increase, -1 means decrease\r\n * @param fontSizes Known font size array\r\n */\r\nexport function getNewFontSize(pt: number, changeBase: 1 | -1, fontSizes: number[]): number {\r\n pt = changeBase == 1 ? Math.floor(pt) : Math.ceil(pt);\r\n let last = fontSizes[fontSizes.length - 1];\r\n if (pt <= fontSizes[0]) {\r\n pt = Math.max(pt + changeBase, MIN_FONT_SIZE);\r\n } else if (pt > last || (pt == last && changeBase == 1)) {\r\n pt = pt / 10;\r\n pt = changeBase == 1 ? Math.floor(pt) : Math.ceil(pt);\r\n pt = Math.min(Math.max((pt + changeBase) * 10, last), MAX_FONT_SIZE);\r\n } else if (changeBase == 1) {\r\n for (let i = 0; i < fontSizes.length; i++) {\r\n if (pt < fontSizes[i]) {\r\n pt = fontSizes[i];\r\n break;\r\n }\r\n }\r\n } else {\r\n for (let i = fontSizes.length - 1; i >= 0; i--) {\r\n if (pt > fontSizes[i]) {\r\n pt = fontSizes[i];\r\n break;\r\n }\r\n }\r\n }\r\n return pt;\r\n}\r\n","import clearFormat from './clearFormat';\r\nimport { ClearFormatMode, IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * @deprecated Use clearFormat instead and pass the ClearFormatMode.Block as parameter\r\n * @param editor The editor instance\r\n */\r\nexport default function clearBlockFormat(editor: IEditor) {\r\n clearFormat(editor, ClearFormatMode.Block);\r\n}\r\n","import { ChangeSource, DocumentCommand, IEditor, QueryScope } from 'roosterjs-editor-types';\r\nimport { HtmlSanitizer, matchLink } from 'roosterjs-editor-dom';\r\n\r\n// Regex matching Uri scheme\r\nconst URI_REGEX = /^[a-zA-Z]+:/i;\r\n// Regex matching begin of email address\r\nconst MAILTO_REGEX = /^[\\w.%+-]+@/i;\r\n// Regex matching begin of ftp, i.e. ftp.microsoft.com\r\nconst FTP_REGEX = /^ftp\\./i;\r\nconst TEMP_TITLE = 'istemptitle';\r\n\r\nfunction applyLinkPrefix(url: string): string {\r\n if (!url) {\r\n return url;\r\n }\r\n\r\n // Add link prefix per rule:\r\n // (a) if the url always starts with a URI scheme, leave it as it is\r\n // (b) if the url is an email address, xxx@... add mailto: prefix\r\n // (c) if the url starts with ftp., add ftp:// prefix\r\n // (d) rest, add http:// prefix\r\n let prefix = '';\r\n if (url.search(URI_REGEX) < 0) {\r\n if (url.search(MAILTO_REGEX) == 0) {\r\n prefix = 'mailto:';\r\n } else if (url.search(FTP_REGEX) == 0) {\r\n prefix = 'ftp://';\r\n } else {\r\n // fallback to http://\r\n prefix = 'http://';\r\n }\r\n }\r\n\r\n return prefix + url;\r\n}\r\n\r\n/**\r\n * Insert a hyperlink at cursor.\r\n * When there is a selection, hyperlink will be applied to the selection,\r\n * otherwise a hyperlink will be inserted to the cursor position.\r\n * @param editor Editor object\r\n * @param link Link address, can be http(s), mailto, notes, file, unc, ftp, news, telnet, gopher, wais.\r\n * When protocol is not specified, a best matched protocol will be predicted.\r\n * @param altText Optional alt text of the link, will be shown when hover on the link\r\n * @param displayText Optional display text for the link.\r\n * If specified, the display text of link will be replaced with this text.\r\n * If not specified and there wasn't a link, the link url will be used as display text.\r\n */\r\nexport default function createLink(\r\n editor: IEditor,\r\n link: string,\r\n altText?: string,\r\n displayText?: string\r\n) {\r\n editor.focus();\r\n let url = (checkXss(link) || '').trim();\r\n if (url) {\r\n let linkData = matchLink(url);\r\n // matchLink can match most links, but not all, i.e. if you pass link a link as \"abc\", it won't match\r\n // we know in that case, users will want to insert a link like http://abc\r\n // so we have separate logic in applyLinkPrefix to add link prefix depending on the format of the link\r\n // i.e. if the link starts with something like abc@xxx, we will add mailto: prefix\r\n // if the link starts with ftp.xxx, we will add ftp:// link. For more, see applyLinkPrefix\r\n let normalizedUrl = linkData ? linkData.normalizedUrl : applyLinkPrefix(url);\r\n let originalUrl = linkData ? linkData.originalUrl : url;\r\n\r\n editor.addUndoSnapshot(() => {\r\n let range = editor.getSelectionRange();\r\n let anchor: HTMLAnchorElement = null;\r\n if (range && range.collapsed) {\r\n anchor = getAnchorNodeAtCursor(editor);\r\n\r\n // If there is already a link, just change its href\r\n if (anchor) {\r\n anchor.href = normalizedUrl;\r\n // Change text content if it is specified\r\n updateAnchorDisplayText(anchor, displayText);\r\n } else {\r\n anchor = editor.getDocument().createElement('A') as HTMLAnchorElement;\r\n anchor.textContent = displayText || originalUrl;\r\n anchor.href = normalizedUrl;\r\n editor.insertNode(anchor);\r\n }\r\n } else {\r\n // the selection is not collapsed, use browser execCommand\r\n editor.getDocument().execCommand(DocumentCommand.CreateLink, false, normalizedUrl);\r\n anchor = getAnchorNodeAtCursor(editor);\r\n updateAnchorDisplayText(anchor, displayText);\r\n }\r\n if (altText && anchor) {\r\n // Hack: Ideally this should be done by HyperLink plugin.\r\n // We make a hack here since we don't have an event to notify HyperLink plugin\r\n // before we apply the link.\r\n anchor.removeAttribute(TEMP_TITLE);\r\n anchor.title = altText;\r\n }\r\n return anchor;\r\n }, ChangeSource.CreateLink);\r\n }\r\n}\r\n\r\nfunction getAnchorNodeAtCursor(editor: IEditor): HTMLAnchorElement {\r\n return editor.queryElements('a[href]', QueryScope.OnSelection)[0] as HTMLAnchorElement;\r\n}\r\n\r\nfunction updateAnchorDisplayText(anchor: HTMLAnchorElement, displayText: string) {\r\n if (displayText && anchor.textContent != displayText) {\r\n anchor.textContent = displayText;\r\n }\r\n}\r\n\r\nfunction checkXss(link: string): string {\r\n const sanitizer = new HtmlSanitizer();\r\n const a = document.createElement('a');\r\n\r\n a.href = link || '';\r\n sanitizer.sanitize(a);\r\n // We use getAttribute because some browsers will try to make the href property a valid link.\r\n // This has unintended side effects when the link lacks a protocol.\r\n return a.getAttribute('href');\r\n}\r\n","import { getTagOfNode } from 'roosterjs-editor-dom';\r\nimport {\r\n ElementBasedFormatState,\r\n FormatState,\r\n IEditor,\r\n PluginEvent,\r\n QueryScope,\r\n} from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Get element based Format State at cursor\r\n * @param editor The editor instance\r\n * @param event (Optional) The plugin event, it stores the event cached data for looking up.\r\n * In this function the event cache is used to get list state and header level. If not passed,\r\n * it will query the node within selection to get the info\r\n * @returns An ElementBasedFormatState object\r\n */\r\nexport function getElementBasedFormatState(\r\n editor: IEditor,\r\n event?: PluginEvent\r\n): ElementBasedFormatState {\r\n let listTag = getTagOfNode(editor.getElementAtCursor('OL,UL', null /*startFrom*/, event));\r\n let headerTag = getTagOfNode(\r\n editor.getElementAtCursor('H1,H2,H3,H4,H5,H6', null /*startFrom*/, event)\r\n );\r\n\r\n return {\r\n isBullet: listTag == 'UL',\r\n isNumbering: listTag == 'OL',\r\n headerLevel: (headerTag && parseInt(headerTag[1])) || 0,\r\n\r\n canUnlink: !!editor.queryElements('a[href]', QueryScope.OnSelection)[0],\r\n canAddImageAltText: !!editor.queryElements('img', QueryScope.OnSelection)[0],\r\n isBlockQuote: !!editor.queryElements('blockquote', QueryScope.OnSelection)[0],\r\n };\r\n}\r\n\r\n/**\r\n * Get format state at cursor\r\n * A format state is a collection of all format related states, e.g.,\r\n * bold, italic, underline, font name, font size, etc.\r\n * @param editor The editor instance\r\n * @param event (Optional) The plugin event, it stores the event cached data for looking up.\r\n * In this function the event cache is used to get list state and header level. If not passed,\r\n * it will query the node within selection to get the info\r\n * @returns The format state at cursor\r\n */\r\nexport default function getFormatState(editor: IEditor, event?: PluginEvent): FormatState {\r\n return {\r\n ...editor.getPendableFormatState(false /* forceGetStateFromDom */),\r\n ...getElementBasedFormatState(editor, event),\r\n ...editor.getStyleBasedFormatState(),\r\n ...editor.getUndoState(),\r\n };\r\n}\r\n","import {\n commitEntity,\n getEntityFromElement,\n getEntitySelector,\n Position,\n wrap,\n} from 'roosterjs-editor-dom';\nimport {\n ChangeSource,\n ContentPosition,\n Entity,\n IEditor,\n NodePosition,\n PositionType,\n} from 'roosterjs-editor-types';\n\n/**\n * Insert an entity into editor.\n * @param editor The editor to insert entity into.\n * @param type Type of the entity\n * @param contentNode Root element of the entity\n * @param isBlock Whether the entity will be shown as a block\n * @param isReadonly Whether the entity will be a readonly entity\n * @param position (Optional) The position to insert into. If not specified, current position will be used.\n * If isBlock is true, entity will be insert below this position\n */\nexport default function insertEntity(\n editor: IEditor,\n type: string,\n contentNode: Node,\n isBlock: boolean,\n isReadonly: boolean,\n position?: NodePosition | ContentPosition.Begin | ContentPosition.End | ContentPosition.DomEnd\n): Entity {\n const wrapper = wrap(contentNode, isBlock ? 'DIV' : 'SPAN');\n\n // For inline & readonly entity, we need to set display to \"inline-block\" otherwise\n // there will be some weird behavior when move cursor around the entity node.\n // And we should only do this for readonly entity since \"inline-block\" has some side effect\n // in IE that there will be a resize border around the inline-block element. We made some\n // workaround for readonly entity for this issue but for editable entity, keep it as \"inline\"\n // will just work fine.\n if (!isBlock && isReadonly) {\n wrapper.style.display = 'inline-block';\n }\n\n commitEntity(wrapper, type, isReadonly);\n\n if (!editor.contains(wrapper)) {\n let currentRange: Range;\n let contentPosition:\n | ContentPosition.Begin\n | ContentPosition.End\n | ContentPosition.DomEnd\n | ContentPosition.SelectionStart;\n\n if (typeof position == 'number') {\n contentPosition = position;\n } else if (position) {\n currentRange = editor.getSelectionRange();\n const node = position.normalize().node;\n const existingEntity = node && editor.getElementAtCursor(getEntitySelector(), node);\n\n // Do not insert entity into another entity\n if (existingEntity) {\n position = new Position(existingEntity, PositionType.After);\n }\n\n editor.select(position);\n contentPosition = ContentPosition.SelectionStart;\n } else {\n editor.focus();\n contentPosition = ContentPosition.SelectionStart;\n }\n\n editor.insertNode(wrapper, {\n updateCursor: false,\n insertOnNewLine: isBlock,\n replaceSelection: true,\n position: contentPosition,\n });\n\n if (contentPosition == ContentPosition.SelectionStart) {\n if (currentRange) {\n editor.select(currentRange);\n } else if (!isBlock) {\n editor.select(wrapper, PositionType.After);\n }\n }\n }\n\n if (isBlock) {\n // Insert an extra empty line for block entity to make sure\n // user can still put cursor below the entity.\n const br = editor.getDocument().createElement('BR');\n wrapper.parentNode.insertBefore(br, wrapper.nextSibling);\n }\n\n const entity = getEntityFromElement(wrapper);\n editor.triggerContentChangedEvent(ChangeSource.InsertEntity, entity);\n\n return entity;\n}\n","import { ChangeSource, IEditor } from 'roosterjs-editor-types';\r\nimport { readFile } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Insert an image to editor at current selection\r\n * @param editor The editor instance\r\n * @param imageFile The image file. There are at least 3 ways to obtain the file object:\r\n * From local file, from clipboard data, from drag-and-drop\r\n */\r\nexport default function insertImage(editor: IEditor, imageFile: File): void;\r\n\r\n/**\r\n * Insert an image to editor at current selection\r\n * @param editor The editor instance\r\n * @param imageFile The image link.\r\n */\r\nexport default function insertImage(editor: IEditor, url: string): void;\r\n\r\nexport default function insertImage(editor: IEditor, imageFile: File | string): void {\r\n if (typeof imageFile == 'string') {\r\n insertImageWithSrc(editor, imageFile);\r\n } else {\r\n readFile(imageFile, dataUrl => {\r\n if (dataUrl && !editor.isDisposed()) {\r\n insertImageWithSrc(editor, dataUrl);\r\n }\r\n });\r\n }\r\n}\r\n\r\nfunction insertImageWithSrc(editor: IEditor, src: string) {\r\n editor.addUndoSnapshot(() => {\r\n const image = editor.getDocument().createElement('img');\r\n image.src = src;\r\n image.style.maxWidth = '100%';\r\n editor.insertNode(image);\r\n }, ChangeSource.Format);\r\n}\r\n","import { ChangeSource, IEditor, PositionType, TableFormat } from 'roosterjs-editor-types';\r\nimport { Position, VTable } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Insert table into editor at current selection\r\n * @param editor The editor instance\r\n * @param columns Number of columns in table, it also controls the default table cell width:\r\n * if columns <= 4, width = 120px; if columns <= 6, width = 100px; else width = 70px\r\n * @param rows Number of rows in table\r\n * @param format (Optional) The table format. If not passed, the default format will be applied:\r\n * background color: #FFF; border color: #ABABAB\r\n */\r\nexport default function insertTable(\r\n editor: IEditor,\r\n columns: number,\r\n rows: number,\r\n format?: TableFormat\r\n) {\r\n let document = editor.getDocument();\r\n let fragment = document.createDocumentFragment();\r\n let table = document.createElement('table') as HTMLTableElement;\r\n fragment.appendChild(table);\r\n table.cellSpacing = '0';\r\n table.cellPadding = '1';\r\n for (let i = 0; i < rows; i++) {\r\n let tr = document.createElement('tr') as HTMLTableRowElement;\r\n table.appendChild(tr);\r\n for (let j = 0; j < columns; j++) {\r\n let td = document.createElement('td') as HTMLTableCellElement;\r\n tr.appendChild(td);\r\n td.appendChild(document.createElement('br'));\r\n td.style.width = getTableCellWidth(columns);\r\n }\r\n }\r\n\r\n editor.focus();\r\n editor.addUndoSnapshot(() => {\r\n let vtable = new VTable(table);\r\n vtable.applyFormat(\r\n format || {\r\n bgColorEven: '#FFF',\r\n bgColorOdd: '#FFF',\r\n topBorderColor: '#ABABAB',\r\n bottomBorderColor: '#ABABAB',\r\n verticalBorderColor: '#ABABAB',\r\n }\r\n );\r\n vtable.writeBack();\r\n editor.insertNode(fragment);\r\n editor.runAsync(editor =>\r\n editor.select(new Position(table, PositionType.Begin).normalize())\r\n );\r\n }, ChangeSource.Format);\r\n}\r\n\r\nfunction getTableCellWidth(columns: number): string {\r\n if (columns <= 4) {\r\n return '120px';\r\n } else if (columns <= 6) {\r\n return '100px';\r\n } else {\r\n return '70px';\r\n }\r\n}\r\n","import { ChangeSource, IEditor, PositionType, TableOperation } from 'roosterjs-editor-types';\r\nimport { VTable } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Edit table with given operation. If there is no table at cursor then no op.\r\n * @param editor The editor instance\r\n * @param operation Table operation\r\n */\r\nexport default function editTable(editor: IEditor, operation: TableOperation) {\r\n let td = editor.getElementAtCursor('TD,TH') as HTMLTableCellElement;\r\n if (td) {\r\n editor.addUndoSnapshot((start, end) => {\r\n let vtable = new VTable(td);\r\n vtable.edit(operation);\r\n vtable.writeBack();\r\n editor.focus();\r\n\r\n let cellToSelect = calculateCellToSelect(operation, vtable.row, vtable.col);\r\n editor.select(\r\n vtable.getCell(cellToSelect.newRow, cellToSelect.newCol).td,\r\n PositionType.Begin\r\n );\r\n }, ChangeSource.Format);\r\n }\r\n}\r\n\r\nfunction calculateCellToSelect(operation: TableOperation, currentRow: number, currentCol: number) {\r\n let newRow = currentRow;\r\n let newCol = currentCol;\r\n switch (operation) {\r\n case TableOperation.InsertAbove:\r\n newCol = 0;\r\n break;\r\n case TableOperation.InsertBelow:\r\n newRow += 1;\r\n newCol = 0;\r\n break;\r\n case TableOperation.InsertLeft:\r\n newRow = 0;\r\n break;\r\n case TableOperation.InsertRight:\r\n newRow = 0;\r\n newCol += 1;\r\n break;\r\n }\r\n\r\n return {\r\n newRow,\r\n newCol,\r\n };\r\n}\r\n","import { ChangeSource, IEditor, TableFormat } from 'roosterjs-editor-types';\r\nimport { VTable } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Format table\r\n * @param editor The editor which contains the table to format\r\n * @param format A TableFormat object contains format information we want to apply to the table\r\n * @param table The table to format. This is optional. When not passed, the current table (if any) will be formatted\r\n */\r\nexport default function formatTable(\r\n editor: IEditor,\r\n format: Partial,\r\n table?: HTMLTableElement\r\n) {\r\n table = table || (editor.getElementAtCursor('TABLE') as HTMLTableElement);\r\n if (table) {\r\n editor.addUndoSnapshot((start, end) => {\r\n let vtable = new VTable(table);\r\n vtable.applyFormat(format);\r\n vtable.writeBack();\r\n editor.focus();\r\n editor.select(start, end);\r\n }, ChangeSource.Format);\r\n }\r\n}\r\n","import { ChangeSource, IEditor, QueryScope } from 'roosterjs-editor-types';\r\nimport { unwrap } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Remove link at selection. If no links at selection, do nothing.\r\n * If selection contains multiple links, all of the link styles will be removed.\r\n * If only part of a link is selected, the whole link style will be removed.\r\n * @param editor The editor instance\r\n */\r\nexport default function removeLink(editor: IEditor) {\r\n editor.focus();\r\n editor.addUndoSnapshot((start, end) => {\r\n editor.queryElements('a[href]', QueryScope.OnSelection, unwrap);\r\n editor.select(start, end);\r\n }, ChangeSource.Format);\r\n}\r\n","import { ContentPosition, IEditor, IPositionContentSearcher } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Replace text before current selection with a node, current selection will be kept if possible\r\n * @param editor The editor instance\r\n * @param text The text for matching. We will try to match the text with the text before cursor\r\n * @param node The node to replace the text with\r\n * @param exactMatch True if the text must appear exactly before selection,\r\n * otherwise there can be some text between the target text and selection\r\n * @param searcher Optional PositionContentSearcher of current selection to help search text\r\n */\r\nexport default function replaceWithNode(\r\n editor: IEditor,\r\n text: string,\r\n node: Node,\r\n exactMatch: boolean,\r\n searcher?: IPositionContentSearcher\r\n): boolean;\r\n\r\n/**\r\n * Replace a given range with a node, current selection will be kept if possible\r\n * @param editor The editor instance\r\n * @param range The range to replace from\r\n * @param node The node to replace the text with\r\n * @param exactMatch True if the text must appear exactly before selection,\r\n * otherwise there can be some text between the target text and selection\r\n */\r\nexport default function replaceWithNode(\r\n editor: IEditor,\r\n range: Range,\r\n node: Node,\r\n exactMatch: boolean\r\n): boolean;\r\n\r\nexport default function replaceWithNode(\r\n editor: IEditor,\r\n textOrRange: string | Range,\r\n node: Node,\r\n exactMatch: boolean,\r\n searcher?: IPositionContentSearcher\r\n): boolean {\r\n // Make sure the text and node is valid\r\n if (!textOrRange || !node) {\r\n return false;\r\n }\r\n\r\n let range: Range;\r\n\r\n if (typeof textOrRange == 'string') {\r\n searcher = searcher || editor.getContentSearcherOfCursor();\r\n range = searcher && searcher.getRangeFromText(textOrRange, exactMatch);\r\n } else {\r\n range = textOrRange;\r\n }\r\n\r\n if (range) {\r\n const backupRange = editor.getSelectionRange();\r\n\r\n // If the range to replace is right before current cursor, it is actually an exact match\r\n if (\r\n backupRange.collapsed &&\r\n range.endContainer == backupRange.startContainer &&\r\n range.endOffset == backupRange.startOffset\r\n ) {\r\n exactMatch = true;\r\n }\r\n\r\n editor.insertNode(node, {\r\n position: ContentPosition.Range,\r\n updateCursor: exactMatch,\r\n replaceSelection: true,\r\n insertOnNewLine: false,\r\n range: range,\r\n });\r\n\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n","import { ChangeSource, IEditor } from 'roosterjs-editor-types';\n\n/**\n * Rotate an element visually\n * @param editor The editor instance\n * @param element The element that should be rotated\n * @param angle The degree at which to rotate the element from it's center\n */\nexport default function rotateElement(editor: IEditor, element: HTMLElement, angle: number): void {\n if (element) {\n editor.addUndoSnapshot(() => {\n element.style.transform = `rotate(${angle}deg)`;\n }, ChangeSource.Format);\n }\n}\n","import execCommand from '../utils/execCommand';\r\nimport {\r\n Alignment,\r\n ChangeSource,\r\n DocumentCommand,\r\n IEditor,\r\n QueryScope,\r\n} from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Set content alignment\r\n * @param editor The editor instance\r\n * @param alignment The alignment option:\r\n * Alignment.Center, Alignment.Left, Alignment.Right\r\n */\r\nexport default function setAlignment(editor: IEditor, alignment: Alignment) {\r\n let command = DocumentCommand.JustifyLeft;\r\n let align = 'left';\r\n\r\n if (alignment == Alignment.Center) {\r\n command = DocumentCommand.JustifyCenter;\r\n align = 'center';\r\n } else if (alignment == Alignment.Right) {\r\n command = DocumentCommand.JustifyRight;\r\n align = 'right';\r\n }\r\n\r\n editor.addUndoSnapshot(() => {\r\n execCommand(editor, command);\r\n editor.queryElements(\r\n '[align]',\r\n QueryScope.OnSelection,\r\n node => (node.style.textAlign = align)\r\n );\r\n }, ChangeSource.Format);\r\n}\r\n","import collapseSelectedBlocks from '../utils/collapseSelectedBlocks';\r\nimport { ChangeSource, Direction, IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Change direction for the blocks/paragraph at selection\r\n * @param editor The editor instance\r\n * @param direction The direction option:\r\n * Direction.LeftToRight refers to 'ltr', Direction.RightToLeft refers to 'rtl'\r\n */\r\nexport default function setDirection(editor: IEditor, direction: Direction) {\r\n editor.focus();\r\n editor.addUndoSnapshot((start, end) => {\r\n collapseSelectedBlocks(editor, element => {\r\n element.setAttribute('dir', direction == Direction.LeftToRight ? 'ltr' : 'rtl');\r\n element.style.textAlign = direction == Direction.LeftToRight ? 'left' : 'right';\r\n });\r\n editor.select(start, end);\r\n }, ChangeSource.Format);\r\n}\r\n","import { BlockElement, IEditor, NodeType } from 'roosterjs-editor-types';\r\nimport { getTagOfNode } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * @internal\r\n * Collapse all selected blocks, return single HTML elements for each block\r\n * @param editor The editor instance\r\n * @param forEachCallback A callback function to invoke for each of the collapsed element\r\n */\r\nexport default function collapseSelectedBlocks(\r\n editor: IEditor,\r\n forEachCallback: (element: HTMLElement) => any\r\n) {\r\n let traverser = editor.getSelectionTraverser();\r\n let block = traverser && traverser.currentBlockElement;\r\n let blocks: BlockElement[] = [];\r\n while (block) {\r\n if (!isEmptyBlockUnderTR(block)) {\r\n blocks.push(block);\r\n }\r\n block = traverser.getNextBlockElement();\r\n }\r\n\r\n blocks.forEach(block => {\r\n let element = block.collapseToSingleElement();\r\n forEachCallback(element);\r\n });\r\n}\r\n\r\nfunction isEmptyBlockUnderTR(block: BlockElement): boolean {\r\n let startNode = block.getStartNode();\r\n\r\n return (\r\n startNode == block.getEndNode() &&\r\n startNode.nodeType == NodeType.Text &&\r\n ['TR', 'TABLE'].indexOf(getTagOfNode(startNode.parentNode)) >= 0\r\n );\r\n}\r\n","import { ChangeSource, IEditor, QueryScope } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Set image alt text for all selected images at selection. If no images is contained\r\n * in selection, do nothing.\r\n * The alt attribute provides alternative information for an image if a user for some reason\r\n * cannot view it (because of slow connection, an error in the src attribute, or if the user\r\n * uses a screen reader). See https://www.w3schools.com/tags/att_img_alt.asp\r\n * @param editor The editor instance\r\n * @param altText The image alt text\r\n */\r\nexport default function setImageAltText(editor: IEditor, altText: string) {\r\n editor.focus();\r\n editor.addUndoSnapshot(() => {\r\n editor.queryElements('img', QueryScope.OnSelection, node =>\r\n node.setAttribute('alt', altText)\r\n );\r\n }, ChangeSource.Format);\r\n}\r\n","import blockFormat from '../utils/blockFormat';\r\nimport {\r\n BlockElement,\r\n IEditor,\r\n Indentation,\r\n KnownCreateElementDataIndex,\r\n RegionBase,\r\n} from 'roosterjs-editor-types';\r\nimport {\r\n collapseNodesInRegion,\r\n createVListFromRegion,\r\n findClosestElementAncestor,\r\n getSelectedBlockElementsInRegion,\r\n getTagOfNode,\r\n isNodeInRegion,\r\n splitBalancedNodeRange,\r\n toArray,\r\n unwrap,\r\n wrap,\r\n} from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Set indentation at selection\r\n * If selection contains bullet/numbering list, increase/decrease indentation will\r\n * increase/decrease the list level by one.\r\n * @param editor The editor instance\r\n * @param indentation The indentation option:\r\n * Indentation.Increase to increase indentation or Indentation.Decrease to decrease indentation\r\n */\r\nexport default function setIndentation(editor: IEditor, indentation: Indentation) {\r\n const handler = indentation == Indentation.Increase ? indent : outdent;\r\n\r\n blockFormat(editor, (region, start, end) => {\r\n const blocks = getSelectedBlockElementsInRegion(region, true /*createBlockIfEmpty*/);\r\n const blockGroups: BlockElement[][] = [[]];\r\n\r\n for (let i = 0; i < blocks.length; i++) {\r\n const startNode = blocks[i].getStartNode();\r\n const vList = createVListFromRegion(region, true /*includeSiblingLists*/, startNode);\r\n\r\n if (vList) {\r\n blockGroups.push([]);\r\n while (blocks[i + 1] && vList.contains(blocks[i + 1].getStartNode())) {\r\n i++;\r\n }\r\n vList.setIndentation(start, end, indentation);\r\n vList.writeBack();\r\n } else {\r\n blockGroups[blockGroups.length - 1].push(blocks[i]);\r\n }\r\n }\r\n\r\n blockGroups.forEach(group => handler(region, group));\r\n });\r\n}\r\n\r\nfunction indent(region: RegionBase, blocks: BlockElement[]) {\r\n const nodes = collapseNodesInRegion(region, blocks);\r\n wrap(nodes, KnownCreateElementDataIndex.BlockquoteWrapper);\r\n}\r\n\r\nfunction outdent(region: RegionBase, blocks: BlockElement[]) {\r\n blocks.forEach(blockElement => {\r\n let node = blockElement.collapseToSingleElement();\r\n const quote = findClosestElementAncestor(node, region.rootNode, 'blockquote');\r\n if (quote) {\r\n if (node == quote) {\r\n node = wrap(toArray(node.childNodes));\r\n }\r\n\r\n while (isNodeInRegion(region, node) && getTagOfNode(node) != 'BLOCKQUOTE') {\r\n node = splitBalancedNodeRange(node);\r\n }\r\n\r\n if (isNodeInRegion(region, node)) {\r\n unwrap(node);\r\n }\r\n }\r\n });\r\n}\r\n","import { IEditor } from 'roosterjs-editor-types';\nimport { Capitalization, NodeType } from 'roosterjs-editor-types';\nimport applyInlineStyle from '../utils/applyInlineStyle';\nimport { getFirstLeafNode, getNextLeafSibling } from 'roosterjs-editor-dom';\n\n/**\n * Change the capitalization of text in the selection\n * @param editor The editor instance\n * @param capitalization The case option\n * @param language Optional parameter for language string that should comply to \"IETF BCP 47 Tags for\n * Identifying Languages\". For example: 'en' or 'en-US' for English, 'tr' for Turkish.\n * Default is the host environment’s current locale.\n */\nexport default function changeCapitalization(\n editor: IEditor,\n capitalization: Capitalization,\n language?: string\n) {\n applyInlineStyle(editor, element => {\n for (let node = getFirstLeafNode(element); node; node = getNextLeafSibling(element, node)) {\n if (node.nodeType == NodeType.Text) {\n try {\n node.textContent = getCapitalizedText(node.textContent, language);\n } catch {\n node.textContent = getCapitalizedText(node.textContent, undefined);\n }\n }\n }\n });\n\n function getCapitalizedText(originalText: string, language: string): string {\n switch (capitalization) {\n case Capitalization.Lowercase:\n return originalText.toLocaleLowerCase(language);\n case Capitalization.Uppercase:\n return originalText.toLocaleUpperCase(language);\n case Capitalization.CapitalizeEachWord:\n const wordArray = originalText.toLocaleLowerCase(language).split(' ');\n for (let i = 0; i < wordArray.length; i++) {\n wordArray[i] =\n wordArray[i].charAt(0).toLocaleUpperCase(language) + wordArray[i].slice(1);\n }\n return wordArray.join(' ');\n case Capitalization.Sentence:\n // TODO: Add rules on punctuation for internationalization - TASK 104769\n const punctuationMarks = '[\\\\.\\\\!\\\\?]';\n // Find a match of a word character either:\n // - At the beginning of a string with or without preceding whitespace, for\n // example: ' hello world' and 'hello world' strings would both match 'h'.\n // - Or preceded by a punctuation mark and at least one whitespace, for\n // example 'yes. hello world' would match 'y' and 'h'.\n const regex = new RegExp('^\\\\s*\\\\w|' + punctuationMarks + '\\\\s+\\\\w', 'g');\n return originalText.toLocaleLowerCase(language).replace(regex, match => {\n return match.toLocaleUpperCase(language);\n });\n }\n }\n}\n","import toggleListType from '../utils/toggleListType';\r\nimport { IEditor, ListType } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Toggle bullet at selection\r\n * If selection contains bullet in deep level, toggle bullet will decrease the bullet level by one\r\n * If selection contains number list, toggle bullet will convert the number list into bullet list\r\n * If selection contains both bullet/numbering and normal text, the behavior is decided by corresponding\r\n * browser execCommand API\r\n * @param editor The editor instance\r\n */\r\nexport default function toggleBullet(editor: IEditor) {\r\n toggleListType(editor, ListType.Unordered);\r\n}\r\n","import toggleListType from '../utils/toggleListType';\r\nimport { IEditor, ListType } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Toggle numbering at selection\r\n * If selection contains numbering in deep level, toggle numbering will decrease the numbering level by one\r\n * If selection contains bullet list, toggle numbering will convert the bullet list into number list\r\n * If selection contains both bullet/numbering and normal text, the behavior is decided by corresponding\r\n * realization of browser execCommand API\r\n * @param editor The editor instance\r\n * @param startNumber (Optional) Start number of the list\r\n */\r\nexport default function toggleNumbering(editor: IEditor, startNumber?: number) {\r\n toggleListType(editor, ListType.Ordered, startNumber);\r\n}\r\n","import { ChangeSource, IEditor } from 'roosterjs-editor-types';\nimport { createVListFromRegion } from 'roosterjs-editor-dom';\n\n/**\n * Resets Ordered List Numbering back to the value of the parameter startNumber\n * @param editor The editor instance\n * @param separator The HTML element that indicates when to split the VList\n * @param startNumber The number of that the splitted list should start\n */\nexport default function setOrderedListNumbering(\n editor: IEditor,\n separator: HTMLLIElement,\n startNumber: number = 1\n) {\n editor.addUndoSnapshot(() => {\n editor.focus();\n const regions = editor.getSelectedRegions();\n if (regions[0]) {\n const vList = createVListFromRegion(\n regions[0],\n false /*includeSiblingLists*/,\n separator\n );\n if (vList) {\n vList.split(separator, startNumber);\n vList.writeBack();\n }\n }\n }, ChangeSource.Format);\n}\n","import blockWrap from '../utils/blockWrap';\r\nimport { IEditor, QueryScope } from 'roosterjs-editor-types';\r\nimport { unwrap, wrap } from 'roosterjs-editor-dom';\r\n\r\nconst BLOCKQUOTE_TAG = 'blockquote';\r\nconst DEFAULT_STYLER = (element: HTMLElement): void => {\r\n element.style.borderLeft = '3px solid';\r\n element.style.borderColor = '#C8C8C8';\r\n element.style.paddingLeft = '10px';\r\n element.style.color = '#666666';\r\n};\r\n\r\n/**\r\n * Toggle blockquote at selection, if selection already contains any blockquote elements,\r\n * the blockquote elements will be unquote and other elements will take no effect\r\n * @param editor The editor instance\r\n * @param styler (Optional) The custom styler for setting the style for the blockquote element\r\n */\r\nexport default function toggleBlockQuote(editor: IEditor, styler?: (element: HTMLElement) => void) {\r\n blockWrap(\r\n editor,\r\n nodes => {\r\n const wrapper = wrap(nodes, BLOCKQUOTE_TAG);\r\n (styler || DEFAULT_STYLER)(wrapper);\r\n },\r\n () => editor.queryElements('blockquote', QueryScope.OnSelection, unwrap).length == 0\r\n );\r\n}\r\n","import blockWrap from '../utils/blockWrap';\r\nimport { IEditor, QueryScope } from 'roosterjs-editor-types';\r\nimport { unwrap, wrap } from 'roosterjs-editor-dom';\r\n\r\nconst PRE_TAG = 'pre';\r\nconst CODE_TAG = 'code';\r\nconst SELECTOR = `${PRE_TAG}>${CODE_TAG}`;\r\n\r\n/**\r\n * Toggle code block at selection, if selection already contains any code blocked elements,\r\n * the code block elements will be no longer be code blocked and other elements will take no affect\r\n * @param editor The editor instance\r\n * @param styler (Optional) The custom styler for setting the style for the code block element\r\n */\r\nexport default function toggleCodeBlock(\r\n editor: IEditor,\r\n styler?: (element: HTMLElement) => void\r\n): void {\r\n blockWrap(\r\n editor,\r\n nodes => {\r\n const code = wrap(nodes, CODE_TAG);\r\n const pre = wrap(code, PRE_TAG);\r\n styler?.(pre);\r\n },\r\n () =>\r\n editor.queryElements(SELECTOR, QueryScope.OnSelection, code => {\r\n if (!code.previousSibling && !code.nextSibling) {\r\n const parent = code.parentNode;\r\n unwrap(code);\r\n unwrap(parent);\r\n }\r\n }).length == 0\r\n );\r\n}\r\n","import execCommand from '../utils/execCommand';\r\nimport { DocumentCommand, IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Toggle strikethrough at selection\r\n * If selection is collapsed, it will only affect the input after caret\r\n * If selection contains only strikethrough text, the strikethrough style will be removed\r\n * If selection contains only normal text, strikethrough style will be added to the whole selected text\r\n * If selection contains both strikethrough and normal text, strikethrough style will be added to the whole selected text\r\n * @param editor The editor instance\r\n */\r\nexport default function toggleStrikethrough(editor: IEditor) {\r\n execCommand(editor, DocumentCommand.StrikeThrough);\r\n}\r\n","import execCommand from '../utils/execCommand';\r\nimport { DocumentCommand, IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Toggle subscript at selection\r\n * If selection is collapsed, it will only affect the input after caret\r\n * If selection contains only subscript text, the subscript style will be removed\r\n * If selection contains only normal text, subscript style will be added to the whole selected text\r\n * If selection contains both subscript and normal text, the subscript style will be removed from whole selected text\r\n * If selection contains any superscript text, the behavior is determined by corresponding realization of browser\r\n * execCommand API\r\n * @param editor The editor instance\r\n */\r\nexport default function toggleSubscript(editor: IEditor) {\r\n execCommand(editor, DocumentCommand.Subscript);\r\n}\r\n","import execCommand from '../utils/execCommand';\r\nimport { DocumentCommand, IEditor } from 'roosterjs-editor-types';\r\n\r\n/**\r\n * Toggle superscript at selection\r\n * If selection is collapsed, it will only affect the input after caret\r\n * If selection contains only superscript text, the superscript style will be removed\r\n * If selection contains only normal text, superscript style will be added to the whole selected text\r\n * If selection contains both superscript and normal text, the superscript style will be removed from whole selected text\r\n * If selection contains any subscript text, the behavior is determined by corresponding realization of browser\r\n * execCommand API\r\n * @param editor The editor instance\r\n */\r\nexport default function toggleSuperscript(editor: IEditor) {\r\n execCommand(editor, DocumentCommand.Superscript);\r\n}\r\n","import { ChangeSource, DocumentCommand, IEditor, QueryScope } from 'roosterjs-editor-types';\r\nimport { HtmlSanitizer, moveChildNodes } from 'roosterjs-editor-dom';\r\n\r\n/**\r\n * Toggle header at selection\r\n * @param editor The editor instance\r\n * @param level The header level, can be a number from 0 to 6, in which 1 ~ 6 refers to\r\n * the HTML header element <H1> to <H6>, 0 means no header\r\n * if passed in param is outside the range, will be rounded to nearest number in the range\r\n */\r\nexport default function toggleHeader(editor: IEditor, level: number) {\r\n level = Math.min(Math.max(Math.round(level), 0), 6);\r\n\r\n editor.addUndoSnapshot(() => {\r\n editor.focus();\r\n\r\n let wrapped = false;\r\n editor.queryElements('H1,H2,H3,H4,H5,H6', QueryScope.OnSelection, header => {\r\n if (!wrapped) {\r\n editor.getDocument().execCommand(DocumentCommand.FormatBlock, false, '
');\r\n wrapped = true;\r\n }\r\n\r\n const div = editor.getDocument().createElement('div');\r\n moveChildNodes(div, header);\r\n editor.replaceNode(header, div);\r\n });\r\n\r\n if (level > 0) {\r\n let traverser = editor.getSelectionTraverser();\r\n let blockElement = traverser ? traverser.currentBlockElement : null;\r\n let sanitizer = new HtmlSanitizer({\r\n cssStyleCallbacks: {\r\n 'font-size': () => false,\r\n },\r\n });\r\n while (blockElement) {\r\n let element = blockElement.collapseToSingleElement();\r\n sanitizer.sanitize(element);\r\n blockElement = traverser.getNextBlockElement();\r\n }\r\n editor.getDocument().execCommand(DocumentCommand.FormatBlock, false, ``);\r\n }\r\n }, ChangeSource.Format);\r\n}\r\n","import { Browser, getComputedStyle, Position } from 'roosterjs-editor-dom';\nimport {\n BuildInEditFeature,\n CursorFeatureSettings,\n Keys,\n PluginKeyboardEvent,\n} from 'roosterjs-editor-types';\n\nconst NoCycleCursorMove: BuildInEditFeature = {\n keys: [Keys.LEFT, Keys.RIGHT],\n allowFunctionKeys: true,\n shouldHandleEvent: (event, editor, ctrlOrMeta) => {\n let range: Range;\n let position: Position;\n\n if (\n !ctrlOrMeta ||\n !(range = editor.getSelectionRange()) ||\n !range.collapsed ||\n !(position = Position.getStart(range)) ||\n !editor.isPositionAtBeginning(position)\n ) {\n return false;\n }\n\n let rtl = getComputedStyle(position.element, 'direction') == 'rtl';\n let rawEvent = event.rawEvent;\n\n return (!rtl && rawEvent.which == Keys.LEFT) || (rtl && rawEvent.which == Keys.RIGHT);\n },\n handleEvent: event => {\n event.rawEvent.preventDefault();\n },\n defaultDisabled: !Browser.isChrome,\n};\n\n/**\n * @internal\n */\nexport const CursorFeatures: Record<\n keyof CursorFeatureSettings,\n BuildInEditFeature\n> = {\n noCycleCursorMove: NoCycleCursorMove,\n};\n","import {\n cacheGetEventData,\n getEntityFromElement,\n getEntitySelector,\n Position,\n} from 'roosterjs-editor-dom';\nimport {\n BuildInEditFeature,\n EntityFeatureSettings,\n EntityOperation,\n IEditor,\n Keys,\n PluginKeyboardEvent,\n PositionType,\n PluginEventType,\n} from 'roosterjs-editor-types';\n\n/**\n * A content edit feature to trigger EntityOperation event with operation \"Click\" when user\n * clicks on a readonly entity.\n */\nconst ClickOnEntityFeature: BuildInEditFeature = {\n keys: [Keys.ENTER],\n shouldHandleEvent: (event, editor) => cacheGetReadonlyEntityElement(event, editor),\n handleEvent: (event, editor) => {\n cacheGetReadonlyEntityElement(event, editor, EntityOperation.Click);\n },\n};\n\n/**\n * A content edit feature to trigger EntityOperation event with operation \"Escape\" when user\n * presses ESC on a readonly entity.\n */\nconst EscapeFromEntityFeature: BuildInEditFeature = {\n keys: [Keys.ESCAPE],\n shouldHandleEvent: (event, editor) => cacheGetReadonlyEntityElement(event, editor),\n handleEvent: (event, editor) => {\n cacheGetReadonlyEntityElement(event, editor, EntityOperation.Escape);\n },\n};\n\nfunction cacheGetReadonlyEntityElement(\n event: PluginKeyboardEvent,\n editor: IEditor,\n operation?: EntityOperation\n) {\n const element = cacheGetEventData(event, 'READONLY_ENTITY_ELEMENT', () => {\n const node = event.rawEvent.target as Node;\n const entityElement = node && editor.getElementAtCursor(getEntitySelector(), node);\n return entityElement && !entityElement.isContentEditable ? entityElement : null;\n });\n\n if (element && operation !== undefined) {\n editor.triggerPluginEvent(PluginEventType.EntityOperation, {\n operation,\n rawEvent: event.rawEvent,\n entity: getEntityFromElement(element),\n });\n }\n\n return element;\n}\n\n/**\n * A content edit feature to split current line into two lines at the cursor when user presses\n * ENTER right before a readonly entity.\n * Browser's default behavior will insert an extra BR tag before the entity which causes an extra\n * empty line. So we override the default behavior here.\n */\nconst EnterBeforeReadonlyEntityFeature: BuildInEditFeature = {\n keys: [Keys.ENTER],\n shouldHandleEvent: (event, editor) =>\n cacheGetNeighborEntityElement(event, editor, true /*isNext*/, false /*collapseOnly*/),\n handleEvent: (event, editor) => {\n event.rawEvent.preventDefault();\n\n const range = editor.getSelectionRange();\n const node = Position.getEnd(range).normalize().node;\n const br = editor.getDocument().createElement('BR');\n node.parentNode.insertBefore(br, node.nextSibling);\n\n const block = editor.getBlockElementAtNode(node);\n let newContainer: HTMLElement;\n\n if (block) {\n newContainer = block.collapseToSingleElement();\n br.parentNode?.removeChild(br);\n }\n\n editor.getSelectionRange().deleteContents();\n\n if (newContainer.nextSibling) {\n editor.select(newContainer.nextSibling, PositionType.Begin);\n }\n },\n};\n\n/**\n * A content edit feature to trigger EntityOperation event with operation \"RemoveFromEnd\" when user\n * press BACKSPACE right after an entity\n */\nconst BackspaceAfterEntityFeature: BuildInEditFeature = {\n keys: [Keys.BACKSPACE],\n shouldHandleEvent: (event, editor) =>\n cacheGetNeighborEntityElement(event, editor, false /*isNext*/, true /*collapseOnly*/),\n handleEvent: (event, editor) => {\n cacheGetNeighborEntityElement(\n event,\n editor,\n false /*isNext*/,\n true /*collapseOnly*/,\n EntityOperation.RemoveFromEnd\n );\n },\n};\n\n/**\n * A content edit feature to trigger EntityOperation event with operation \"RemoveFromStart\" when user\n * press DELETE right after an entity\n */\nconst DeleteBeforeEntityFeature: BuildInEditFeature = {\n keys: [Keys.DELETE],\n shouldHandleEvent: (event, editor) =>\n cacheGetNeighborEntityElement(event, editor, true /*isNext*/, true /*collapseOnly*/),\n handleEvent: (event, editor) => {\n cacheGetNeighborEntityElement(\n event,\n editor,\n true /*isNext*/,\n true /*collapseOnly*/,\n EntityOperation.RemoveFromStart\n );\n },\n};\n\nfunction cacheGetNeighborEntityElement(\n event: PluginKeyboardEvent,\n editor: IEditor,\n isNext: boolean,\n collapseOnly: boolean,\n operation?: EntityOperation\n): HTMLElement {\n const element = cacheGetEventData(\n event,\n 'NEIGHBOR_ENTITY_ELEMENT_' + isNext + '_' + collapseOnly,\n () => {\n const range = editor.getSelectionRange();\n\n if (collapseOnly && !range.collapsed) {\n return null;\n }\n\n const pos = Position.getEnd(range).normalize();\n const isAtBeginOrEnd = pos.offset == 0 || pos.isAtEnd;\n let entityNode: HTMLElement = null;\n\n if (isAtBeginOrEnd) {\n const traverser = editor.getBodyTraverser(pos.node);\n const sibling = isNext\n ? pos.offset == 0\n ? traverser.currentInlineElement\n : traverser.getNextInlineElement()\n : pos.isAtEnd\n ? traverser.currentInlineElement\n : traverser.getPreviousInlineElement();\n let node = sibling && sibling.getContainerNode();\n\n if (!collapseOnly) {\n const block = editor.getBlockElementAtNode(pos.node);\n if (!block || !block.contains(node)) {\n node = null;\n }\n }\n\n entityNode = node && editor.getElementAtCursor(getEntitySelector(), node);\n }\n\n return entityNode;\n }\n );\n\n if (element && operation !== undefined) {\n editor.triggerPluginEvent(PluginEventType.EntityOperation, {\n operation,\n rawEvent: event.rawEvent,\n entity: getEntityFromElement(element),\n });\n }\n\n return element;\n}\n\n/**\n * @internal\n */\nexport const EntityFeatures: Record<\n keyof EntityFeatureSettings,\n BuildInEditFeature\n> = {\n clickOnEntity: ClickOnEntityFeature,\n escapeFromEntity: EscapeFromEntityFeature,\n enterBeforeReadonlyEntity: EnterBeforeReadonlyEntityFeature,\n backspaceAfterEntity: BackspaceAfterEntityFeature,\n deleteBeforeEntity: DeleteBeforeEntityFeature,\n};\n","import {\n blockFormat,\n experimentCommitListChains,\n setIndentation,\n toggleBullet,\n toggleNumbering,\n} from 'roosterjs-editor-api';\nimport {\n Browser,\n getTagOfNode,\n isNodeEmpty,\n isPositionAtBeginningOf,\n Position,\n VListChain,\n createVListFromRegion,\n isBlockElement,\n cacheGetEventData,\n} from 'roosterjs-editor-dom';\nimport {\n BuildInEditFeature,\n IEditor,\n Indentation,\n ListFeatureSettings,\n Keys,\n PluginKeyboardEvent,\n QueryScope,\n RegionBase,\n} from 'roosterjs-editor-types';\n\n/**\n * IndentWhenTab edit feature, provides the ability to indent current list when user press TAB\n */\nconst IndentWhenTab: BuildInEditFeature = {\n keys: [Keys.TAB],\n shouldHandleEvent: (event, editor) =>\n !event.rawEvent.shiftKey && cacheGetListElement(event, editor),\n handleEvent: (event, editor) => {\n setIndentation(editor, Indentation.Increase);\n event.rawEvent.preventDefault();\n },\n};\n\n/**\n * OutdentWhenShiftTab edit feature, provides the ability to outdent current list when user press Shift+TAB\n */\nconst OutdentWhenShiftTab: BuildInEditFeature = {\n keys: [Keys.TAB],\n shouldHandleEvent: (event, editor) =>\n event.rawEvent.shiftKey && cacheGetListElement(event, editor),\n handleEvent: (event, editor) => {\n setIndentation(editor, Indentation.Decrease);\n event.rawEvent.preventDefault();\n },\n};\n\n/**\n * MergeInNewLine edit feature, provides the ability to merge current line into a new line when user press\n * BACKSPACE at beginning of a list item\n */\nconst MergeInNewLine: BuildInEditFeature = {\n keys: [Keys.BACKSPACE],\n shouldHandleEvent: (event, editor) => {\n let li = editor.getElementAtCursor('LI', null /*startFrom*/, event);\n let range = editor.getSelectionRange();\n return li && range?.collapsed && isPositionAtBeginningOf(Position.getStart(range), li);\n },\n handleEvent: (event, editor) => {\n let li = editor.getElementAtCursor('LI', null /*startFrom*/, event);\n if (li.previousSibling) {\n blockFormat(editor, (region, start, end) => {\n const vList = createVListFromRegion(region, false /*includeSiblingList*/, li);\n vList.setIndentation(start, end, Indentation.Decrease, true /*softOutdent*/);\n vList.writeBack();\n event.rawEvent.preventDefault();\n });\n } else {\n toggleListAndPreventDefault(event, editor);\n }\n },\n defaultDisabled: true,\n};\n\n/**\n * OutdentWhenBackOn1stEmptyLine edit feature, provides the ability to outdent current item if user press\n * BACKSPACE at the first and empty line of a list\n */\nconst OutdentWhenBackOn1stEmptyLine: BuildInEditFeature = {\n keys: [Keys.BACKSPACE],\n shouldHandleEvent: (event, editor) => {\n let li = editor.getElementAtCursor('LI', null /*startFrom*/, event);\n return li && isNodeEmpty(li) && !li.previousSibling;\n },\n handleEvent: toggleListAndPreventDefault,\n};\n\n/**\n * MaintainListChainWhenDelete edit feature, provides the ability to indent the list if user press\n * DELETE before the first item of a list\n */\nconst MaintainListChainWhenDelete: BuildInEditFeature = {\n keys: [Keys.DELETE],\n shouldHandleEvent: (event, editor) => {\n const li = editor.getElementAtCursor('LI', null /*startFrom*/, event);\n if (li) {\n return false;\n }\n const isAtEnd = Position.getEnd(editor.getSelectionRange()).isAtEnd;\n const nextSibiling = isAtEnd ? getCacheNextSibiling(event, editor) : null;\n const isAtEndAndBeforeLI = editor.getElementAtCursor('LI', nextSibiling, event);\n return isAtEndAndBeforeLI;\n },\n handleEvent: (event, editor) => {\n const chains = getListChains(editor);\n editor.runAsync(editor => experimentCommitListChains(editor, chains));\n },\n};\n\n/**\n * OutdentWhenEnterOnEmptyLine edit feature, provides the ability to outdent current item if user press\n * ENTER at the beginning of an empty line of a list\n */\nconst OutdentWhenEnterOnEmptyLine: BuildInEditFeature = {\n keys: [Keys.ENTER],\n shouldHandleEvent: (event, editor) => {\n let li = editor.getElementAtCursor('LI', null /*startFrom*/, event);\n return !event.rawEvent.shiftKey && li && isNodeEmpty(li);\n },\n handleEvent: (event, editor) => {\n editor.addUndoSnapshot(\n () => toggleListAndPreventDefault(event, editor),\n null /*changeSource*/,\n true /*canUndoByBackspace*/\n );\n },\n defaultDisabled: !Browser.isIE && !Browser.isChrome,\n};\n\n/**\n * AutoBullet edit feature, provides the ability to automatically convert current line into a list.\n * When user input \"1. \", convert into a numbering list\n * When user input \"- \" or \"* \", convert into a bullet list\n */\nconst AutoBullet: BuildInEditFeature = {\n keys: [Keys.SPACE],\n shouldHandleEvent: (event, editor) => {\n if (!cacheGetListElement(event, editor)) {\n let searcher = editor.getContentSearcherOfCursor(event);\n let textBeforeCursor = searcher.getSubStringBefore(4);\n\n // Auto list is triggered if:\n // 1. Text before cursor exactly matches '*', '-' or '1.'\n // 2. There's no non-text inline entities before cursor\n return isAListPattern(textBeforeCursor) && !searcher.getNearestNonTextInlineElement();\n }\n return false;\n },\n handleEvent: (event, editor) => {\n editor.insertContent(' ');\n event.rawEvent.preventDefault();\n editor.addUndoSnapshot(\n () => {\n let regions: RegionBase[];\n let searcher = editor.getContentSearcherOfCursor();\n let textBeforeCursor = searcher.getSubStringBefore(4);\n let rangeToDelete = searcher.getRangeFromText(\n textBeforeCursor,\n true /*exactMatch*/\n );\n\n if (!rangeToDelete) {\n // no op if the range can't be found\n } else if (\n textBeforeCursor.indexOf('*') == 0 ||\n textBeforeCursor.indexOf('-') == 0\n ) {\n prepareAutoBullet(editor, rangeToDelete);\n toggleBullet(editor);\n } else if (isAListPattern(textBeforeCursor)) {\n prepareAutoBullet(editor, rangeToDelete);\n toggleNumbering(editor);\n } else if ((regions = editor.getSelectedRegions()) && regions.length == 1) {\n const num = parseInt(textBeforeCursor);\n prepareAutoBullet(editor, rangeToDelete);\n toggleNumbering(editor, num);\n }\n },\n null /*changeSource*/,\n true /*canUndoByBackspace*/\n );\n },\n};\n\n/**\n * Maintain the list numbers in list chain\n * e.g. we have two lists:\n * 1, 2, 3 and 4, 5, 6\n * Now we delete list item 2, so the first one becomes \"1, 2\".\n * This edit feature can maintain the list number of the second list to become \"3, 4, 5\"\n */\nconst MaintainListChain: BuildInEditFeature = {\n keys: [Keys.ENTER, Keys.TAB, Keys.DELETE, Keys.BACKSPACE, Keys.RANGE],\n shouldHandleEvent: (event, editor) =>\n editor.queryElements('li', QueryScope.OnSelection).length > 0,\n handleEvent: (event, editor) => {\n const chains = getListChains(editor);\n editor.runAsync(editor => experimentCommitListChains(editor, chains));\n },\n};\n\n/**\n * Validate if a block of text is considered a list pattern\n * The regex expression will look for patterns of the form:\n * 1. 1> 1) 1- (1)\n * @returns if a text is considered a list pattern\n */\nfunction isAListPattern(textBeforeCursor: string) {\n const REGEX: RegExp = /^(\\*|-|[0-9]{1,2}\\.|[0-9]{1,2}\\>|[0-9]{1,2}\\)|[0-9]{1,2}\\-|\\([0-9]{1,2}\\))$/;\n return REGEX.test(textBeforeCursor);\n}\n\nfunction getListChains(editor: IEditor) {\n return VListChain.createListChains(editor.getSelectedRegions());\n}\n\nfunction getCacheNextSibiling(event: PluginKeyboardEvent, editor: IEditor): Node | undefined {\n const element = cacheGetEventData(event, 'nextSibiling', () => {\n const range = editor.getSelectionRange();\n const pos = Position.getEnd(range).normalize();\n const traverser = editor.getBodyTraverser(pos.node);\n return traverser?.getNextBlockElement()?.getStartNode();\n });\n return element;\n}\n\nfunction prepareAutoBullet(editor: IEditor, range: Range) {\n range.deleteContents();\n\n const block = editor.getBlockElementAtNode(range.startContainer);\n const endNode = block?.getEndNode();\n if (endNode && getTagOfNode(endNode) != 'BR' && block?.getTextContent().trim() === '') {\n const br = editor.getDocument().createElement('BR');\n if (isBlockElement(endNode)) {\n endNode.appendChild(br);\n } else {\n endNode.parentNode.insertBefore(br, endNode.nextSibling);\n }\n editor.select(range.startContainer, range.startOffset);\n }\n}\n\nfunction toggleListAndPreventDefault(event: PluginKeyboardEvent, editor: IEditor) {\n let listInfo = cacheGetListElement(event, editor);\n if (listInfo) {\n let listElement = listInfo[0];\n let tag = getTagOfNode(listElement);\n if (tag == 'UL') {\n toggleBullet(editor);\n } else if (tag == 'OL') {\n toggleNumbering(editor);\n }\n editor.focus();\n event.rawEvent.preventDefault();\n }\n}\n\nfunction cacheGetListElement(event: PluginKeyboardEvent, editor: IEditor) {\n let li = editor.getElementAtCursor('LI,TABLE', null /*startFrom*/, event);\n let listElement = li && getTagOfNode(li) == 'LI' && editor.getElementAtCursor('UL,OL', li);\n return listElement ? [listElement, li] : null;\n}\n\n/**\n * @internal\n */\nexport const ListFeatures: Record<\n keyof ListFeatureSettings,\n BuildInEditFeature\n> = {\n autoBullet: AutoBullet,\n indentWhenTab: IndentWhenTab,\n outdentWhenShiftTab: OutdentWhenShiftTab,\n outdentWhenBackspaceOnEmptyFirstLine: OutdentWhenBackOn1stEmptyLine,\n outdentWhenEnterOnEmptyLine: OutdentWhenEnterOnEmptyLine,\n mergeInNewLineWhenBackspaceOnFirstChar: MergeInNewLine,\n maintainListChain: MaintainListChain,\n maintainListChainWhenDelete: MaintainListChainWhenDelete,\n};\n","import { cacheGetEventData, createRange } from 'roosterjs-editor-dom';\nimport {\n BuildInEditFeature,\n ChangeSource,\n IEditor,\n Keys,\n MarkdownFeatureSettings,\n NodePosition,\n PluginKeyboardEvent,\n PositionType,\n} from 'roosterjs-editor-types';\n\nconst ZERO_WIDTH_SPACE = '\\u200B';\n\nfunction generateBasicMarkdownFeature(\n key: Keys,\n triggerCharacter: string,\n elementTag: string,\n useShiftKey: boolean\n): BuildInEditFeature {\n return {\n keys: [key],\n shouldHandleEvent: (event, editor) =>\n event.rawEvent.shiftKey === useShiftKey &&\n !!cacheGetRangeForMarkdownOperation(event, editor, triggerCharacter),\n handleEvent: (event, editor) => {\n // runAsync is here to allow the event to complete so autocomplete will present the trigger character.\n editor.runAsync(editor => {\n handleMarkdownEvent(event, editor, triggerCharacter, elementTag);\n });\n },\n };\n}\n\nfunction cacheGetRangeForMarkdownOperation(\n event: PluginKeyboardEvent,\n editor: IEditor,\n triggerCharacter: string\n): Range {\n return cacheGetEventData(event, 'MARKDOWN_RANGE', () => {\n const searcher = editor.getContentSearcherOfCursor(event);\n\n let startPosition: NodePosition;\n let endPosition: NodePosition;\n searcher.forEachTextInlineElement(textInlineElement => {\n if (endPosition && startPosition) {\n return true;\n }\n const inlineTextContent = textInlineElement.getTextContent();\n\n // special case for immediately preceding character being whitespace\n if (inlineTextContent[inlineTextContent.length - 1].trim().length == 0) {\n return false;\n }\n\n // special case for consecutive trigger characters\n if (inlineTextContent[inlineTextContent.length - 1] === triggerCharacter) {\n return false;\n }\n\n if (!endPosition) {\n endPosition = textInlineElement.getStartPosition().move(inlineTextContent.length);\n }\n if (inlineTextContent[0] == triggerCharacter) {\n startPosition = textInlineElement.getStartPosition();\n } else {\n let contentIndex = inlineTextContent.length - 1;\n for (; contentIndex > 0; contentIndex--) {\n if (startPosition) {\n return true;\n }\n if (\n inlineTextContent[contentIndex] == triggerCharacter &&\n inlineTextContent[contentIndex - 1].trim().length == 0\n ) {\n startPosition = textInlineElement.getStartPosition().move(contentIndex);\n return true;\n }\n }\n }\n });\n return !!startPosition && !!endPosition && createRange(startPosition, endPosition);\n });\n}\n\nfunction handleMarkdownEvent(\n event: PluginKeyboardEvent,\n editor: IEditor,\n triggerCharacter: string,\n elementTag: string\n) {\n editor.addUndoSnapshot(\n () => {\n const range = cacheGetRangeForMarkdownOperation(event, editor, triggerCharacter);\n if (!!range) {\n // get the text content range\n const textContentRange = range.cloneRange();\n textContentRange.setStart(\n textContentRange.startContainer,\n textContentRange.startOffset + 1\n );\n\n // set the removal range to include the typed last character.\n range.setEnd(range.endContainer, range.endOffset + 1);\n\n // extract content and put it into a new element.\n const elementToWrap = editor.getDocument().createElement(elementTag);\n elementToWrap.appendChild(textContentRange.extractContents());\n range.deleteContents();\n\n // ZWS here ensures we don't end up inside the newly created node.\n const nonPrintedSpaceTextNode = editor\n .getDocument()\n .createTextNode(ZERO_WIDTH_SPACE);\n range.insertNode(nonPrintedSpaceTextNode);\n range.insertNode(elementToWrap);\n editor.select(nonPrintedSpaceTextNode, PositionType.End);\n }\n },\n ChangeSource.Format,\n true /*canUndoByBackspace*/\n );\n}\n\n/**\n * Markdown bold feature. Make bold text with markdown shortcuts.\n */\nconst MarkdownBold: BuildInEditFeature = generateBasicMarkdownFeature(\n Keys.EIGHT_ASTIRISK,\n '*',\n 'b',\n true\n);\n\n/**\n * Markdown italics feature. Make italic text with markdown shortcuts.\n */\nconst MarkdownItalic: BuildInEditFeature = generateBasicMarkdownFeature(\n Keys.DASH_UNDERSCORE,\n '_',\n 'i',\n true\n);\n\n/**\n * Markdown strikethrough feature. MAke strikethrough text with markdown shortcuts.\n */\nconst MarkdownStrikethrough: BuildInEditFeature = generateBasicMarkdownFeature(\n Keys.GRAVE_TILDE,\n '~',\n 's',\n true\n);\n\n/**\n * Markdown inline code feature. Marks specific text as inline code with markdown shortcuts.\n */\nconst MarkdownInlineCode: BuildInEditFeature = generateBasicMarkdownFeature(\n Keys.GRAVE_TILDE,\n '`',\n 'code',\n false\n);\n\n/**\n * @internal\n */\nexport const MarkdownFeatures: Record<\n keyof MarkdownFeatureSettings,\n BuildInEditFeature\n> = {\n markdownBold: MarkdownBold,\n markdownItalic: MarkdownItalic,\n markdownStrikethru: MarkdownStrikethrough,\n markdownInlineCode: MarkdownInlineCode,\n};\n","import {\n BuildInEditFeature,\n IEditor,\n Keys,\n PluginKeyboardEvent,\n PositionType,\n QuoteFeatureSettings,\n} from 'roosterjs-editor-types';\nimport {\n cacheGetEventData,\n getTagOfNode,\n isNodeEmpty,\n splitBalancedNodeRange,\n toArray,\n unwrap,\n wrap,\n} from 'roosterjs-editor-dom';\n\nconst QUOTE_TAG = 'BLOCKQUOTE';\nconst STRUCTURED_TAGS = [QUOTE_TAG, 'LI', 'TD', 'TH'].join(',');\n\n/**\n * UnquoteWhenBackOnEmpty1stLine edit feature, provides the ability to Unquote current line when\n * user press BACKSPACE on first and empty line of a BLOCKQUOTE\n */\nconst UnquoteWhenBackOnEmpty1stLine: BuildInEditFeature = {\n keys: [Keys.BACKSPACE],\n shouldHandleEvent: (event, editor) => {\n let childOfQuote = cacheGetQuoteChild(event, editor);\n return childOfQuote && isNodeEmpty(childOfQuote) && !childOfQuote.previousSibling;\n },\n handleEvent: splitQuote,\n};\n\n/**\n * UnquoteWhenEnterOnEmptyLine edit feature, provides the ability to Unquote current line when\n * user press ENTER on an empty line of a BLOCKQUOTE\n */\nconst UnquoteWhenEnterOnEmptyLine: BuildInEditFeature = {\n keys: [Keys.ENTER],\n shouldHandleEvent: (event, editor) => {\n let childOfQuote = cacheGetQuoteChild(event, editor);\n let shift = event.rawEvent.shiftKey;\n return !shift && childOfQuote && isNodeEmpty(childOfQuote);\n },\n handleEvent: (event, editor) =>\n editor.addUndoSnapshot(\n () => splitQuote(event, editor),\n null /*changeSource*/,\n true /*canUndoByBackspace*/\n ),\n};\n\nfunction cacheGetQuoteChild(event: PluginKeyboardEvent, editor: IEditor): Node {\n return cacheGetEventData(event, 'QUOTE_CHILD', () => {\n let quote = editor.getElementAtCursor(STRUCTURED_TAGS);\n if (quote && getTagOfNode(quote) == QUOTE_TAG) {\n let pos = editor.getFocusedPosition();\n let block = pos && editor.getBlockElementAtNode(pos.normalize().node);\n if (block) {\n let node =\n block.getStartNode() == quote\n ? block.getStartNode()\n : block.collapseToSingleElement();\n return isNodeEmpty(node) ? node : null;\n }\n }\n\n return null;\n });\n}\n\nfunction splitQuote(event: PluginKeyboardEvent, editor: IEditor) {\n editor.addUndoSnapshot(() => {\n let childOfQuote = cacheGetQuoteChild(event, editor);\n let parent: Node;\n if (getTagOfNode(childOfQuote) == QUOTE_TAG) {\n childOfQuote = wrap(toArray(childOfQuote.childNodes));\n }\n parent = splitBalancedNodeRange(childOfQuote);\n unwrap(parent);\n editor.select(childOfQuote, PositionType.Begin);\n });\n event.rawEvent.preventDefault();\n}\n\n/**\n * @internal\n */\nexport const QuoteFeatures: Record<\n keyof QuoteFeatureSettings,\n BuildInEditFeature\n> = {\n unquoteWhenBackspaceOnEmptyFirstLine: UnquoteWhenBackOnEmpty1stLine,\n unquoteWhenEnterOnEmptyLine: UnquoteWhenEnterOnEmptyLine,\n};\n","import { Browser, cacheGetEventData } from 'roosterjs-editor-dom';\nimport {\n BuildInEditFeature,\n FontSizeChange,\n IEditor,\n Keys,\n PluginEventType,\n PluginKeyboardEvent,\n ShortcutFeatureSettings,\n} from 'roosterjs-editor-types';\nimport {\n changeFontSize,\n toggleBold,\n toggleItalic,\n toggleUnderline,\n toggleBullet,\n toggleNumbering,\n} from 'roosterjs-editor-api';\n\ninterface ShortcutCommand {\n winKey: number;\n macKey: number;\n action: (editor: IEditor) => any;\n}\n\nfunction createCommand(winKey: number, macKey: number, action: (editor: IEditor) => any) {\n return {\n winKey,\n macKey,\n action,\n };\n}\n\nconst commands: ShortcutCommand[] = [\n createCommand(Keys.Ctrl | Keys.B, Keys.Meta | Keys.B, toggleBold),\n createCommand(Keys.Ctrl | Keys.I, Keys.Meta | Keys.I, toggleItalic),\n createCommand(Keys.Ctrl | Keys.U, Keys.Meta | Keys.U, toggleUnderline),\n createCommand(Keys.Ctrl | Keys.Z, Keys.Meta | Keys.Z, editor => editor.undo()),\n createCommand(Keys.Ctrl | Keys.Y, Keys.Meta | Keys.Shift | Keys.Z, editor => editor.redo()),\n createCommand(Keys.Ctrl | Keys.PERIOD, Keys.Meta | Keys.PERIOD, toggleBullet),\n createCommand(Keys.Ctrl | Keys.FORWARDSLASH, Keys.Meta | Keys.FORWARDSLASH, toggleNumbering),\n createCommand(\n Keys.Ctrl | Keys.Shift | Keys.PERIOD,\n Keys.Meta | Keys.Shift | Keys.PERIOD,\n editor => changeFontSize(editor, FontSizeChange.Increase)\n ),\n createCommand(\n Keys.Ctrl | Keys.Shift | Keys.COMMA,\n Keys.Meta | Keys.Shift | Keys.COMMA,\n editor => changeFontSize(editor, FontSizeChange.Decrease)\n ),\n];\n\n/**\n * DefaultShortcut edit feature, provides shortcuts for the following features:\n * Ctrl/Meta+B: toggle bold style\n * Ctrl/Meta+I: toggle italic style\n * Ctrl/Meta+U: toggle underline style\n * Ctrl/Meta+Z: undo\n * Ctrl+Y/Meta+Shift+Z: redo\n * Ctrl/Meta+PERIOD: toggle bullet list\n * Ctrl/Meta+/: toggle numbering list\n * Ctrl/Meta+Shift+>: increase font size\n * Ctrl/Meta+Shift+<: decrease font size\n */\nconst DefaultShortcut: BuildInEditFeature = {\n allowFunctionKeys: true,\n keys: [Keys.B, Keys.I, Keys.U, Keys.Y, Keys.Z, Keys.COMMA, Keys.PERIOD, Keys.FORWARDSLASH],\n shouldHandleEvent: cacheGetCommand,\n handleEvent: (event, editor) => {\n let command = cacheGetCommand(event);\n if (command) {\n command.action(editor);\n event.rawEvent.preventDefault();\n event.rawEvent.stopPropagation();\n }\n },\n};\n\nfunction cacheGetCommand(event: PluginKeyboardEvent) {\n return cacheGetEventData(event, 'DEFAULT_SHORT_COMMAND', () => {\n let e = event.rawEvent;\n let key =\n // Need to check ALT key to be false since in some language (e.g. Polski) uses AltGr to input some special characters\n // In that case, ctrlKey and altKey are both true in Edge, but we should not trigger any shortcut function here\n event.eventType == PluginEventType.KeyDown && !e.altKey\n ? e.which |\n (e.metaKey && Keys.Meta) |\n (e.shiftKey && Keys.Shift) |\n (e.ctrlKey && Keys.Ctrl)\n : 0;\n return key && commands.filter(cmd => (Browser.isMac ? cmd.macKey : cmd.winKey) == key)[0];\n });\n}\n\n/**\n * @internal\n */\nexport const ShortcutFeatures: Record<\n keyof ShortcutFeatureSettings,\n BuildInEditFeature\n> = {\n defaultShortcut: DefaultShortcut,\n};\n","import {\n BuildInEditFeature,\n IEditor,\n Keys,\n KnownCreateElementDataIndex,\n PluginKeyboardEvent,\n PositionType,\n StructuredNodeFeatureSettings,\n} from 'roosterjs-editor-types';\nimport {\n cacheGetEventData,\n isPositionAtBeginningOf,\n Position,\n getTagOfNode,\n createElement,\n} from 'roosterjs-editor-dom';\n\nconst CHILD_PARENT_TAG_MAP: { [childTag: string]: string } = {\n TD: 'TABLE',\n TH: 'TABLE',\n LI: 'OL,UL',\n};\nconst CHILD_SELECTOR = Object.keys(CHILD_PARENT_TAG_MAP).join(',');\n\n/**\n * InsertLineBeforeStructuredNode edit feature, provides the ability to insert an empty line before\n * a structured element (bullet/numbering list, blockquote, table) if the element is at beginning of\n * document\n */\nconst InsertLineBeforeStructuredNodeFeature: BuildInEditFeature = {\n keys: [Keys.ENTER],\n shouldHandleEvent: cacheGetStructuredElement,\n handleEvent: (event, editor) => {\n let element = cacheGetStructuredElement(event, editor);\n let div = createElement(\n KnownCreateElementDataIndex.EmptyLine,\n editor.getDocument()\n ) as HTMLElement;\n editor.addUndoSnapshot(() => {\n element.parentNode.insertBefore(div, element);\n // Select the new line when we are in table. This is the same behavior with Word\n if (getTagOfNode(element) == 'TABLE') {\n editor.select(new Position(div, PositionType.Begin).normalize());\n }\n });\n event.rawEvent.preventDefault();\n },\n defaultDisabled: true,\n};\n\nfunction cacheGetStructuredElement(event: PluginKeyboardEvent, editor: IEditor) {\n return cacheGetEventData(event, 'FIRST_STRUCTURE', () => {\n // Provide a chance to keep browser default behavior by pressing SHIFT\n let element = event.rawEvent.shiftKey ? null : editor.getElementAtCursor(CHILD_SELECTOR);\n\n if (element) {\n let range = editor.getSelectionRange();\n if (\n range &&\n range.collapsed &&\n isPositionAtBeginningOf(Position.getStart(range), element) &&\n !editor.getBodyTraverser(element).getPreviousBlockElement()\n ) {\n return editor.getElementAtCursor(CHILD_PARENT_TAG_MAP[getTagOfNode(element)]);\n }\n }\n\n return null;\n });\n}\n\n/**\n * @internal\n */\nexport const StructuredNodeFeatures: Record<\n keyof StructuredNodeFeatureSettings,\n BuildInEditFeature\n> = {\n insertLineBeforeStructuredNodeFeature: InsertLineBeforeStructuredNodeFeature,\n};\n","import { editTable } from 'roosterjs-editor-api';\nimport {\n BuildInEditFeature,\n IEditor,\n Keys,\n NodeType,\n PluginEvent,\n PositionType,\n TableFeatureSettings,\n TableOperation,\n PluginKeyboardEvent,\n} from 'roosterjs-editor-types';\nimport {\n Browser,\n cacheGetEventData,\n contains,\n getTagOfNode,\n isVoidHtmlElement,\n Position,\n VTable,\n} from 'roosterjs-editor-dom';\n\n/**\n * TabInTable edit feature, provides the ability to jump between cells when user press TAB in table\n */\nconst TabInTable: BuildInEditFeature = {\n keys: [Keys.TAB],\n shouldHandleEvent: cacheGetTableCell,\n handleEvent: (event, editor) => {\n let shift = event.rawEvent.shiftKey;\n let td = cacheGetTableCell(event, editor);\n for (\n let vtable = new VTable(td),\n step = shift ? -1 : 1,\n row = vtable.row,\n col = vtable.col + step;\n ;\n col += step\n ) {\n if (col < 0 || col >= vtable.cells[row].length) {\n row += step;\n if (row < 0) {\n editor.select(vtable.table, PositionType.Before);\n break;\n } else if (row >= vtable.cells.length) {\n editTable(editor, TableOperation.InsertBelow);\n break;\n }\n col = shift ? vtable.cells[row].length - 1 : 0;\n }\n let cell = vtable.getCell(row, col);\n if (cell.td) {\n editor.select(cell.td, PositionType.Begin);\n break;\n }\n }\n event.rawEvent.preventDefault();\n },\n};\n\n/**\n * UpDownInTable edit feature, provides the ability to jump to cell above/below when user press UP/DOWN\n * in table\n */\nconst UpDownInTable: BuildInEditFeature = {\n keys: [Keys.UP, Keys.DOWN],\n shouldHandleEvent: cacheGetTableCell,\n handleEvent: (event, editor) => {\n const td = cacheGetTableCell(event, editor);\n const vtable = new VTable(td);\n const isUp = event.rawEvent.which == Keys.UP;\n const step = isUp ? -1 : 1;\n const hasShiftKey = event.rawEvent.shiftKey;\n const selection = editor.getDocument().defaultView?.getSelection();\n let targetTd: HTMLTableCellElement = null;\n\n if (selection) {\n let { anchorNode, anchorOffset } = selection;\n\n for (let row = vtable.row; row >= 0 && row < vtable.cells.length; row += step) {\n let cell = vtable.getCell(row, vtable.col);\n if (cell.td && cell.td != td) {\n targetTd = cell.td;\n break;\n }\n }\n\n editor.runAsync(editor => {\n let newContainer = editor.getElementAtCursor();\n if (\n contains(vtable.table, newContainer) &&\n !contains(td, newContainer, true /*treatSameNodeAsContain*/)\n ) {\n let newPos = targetTd\n ? new Position(targetTd, PositionType.Begin)\n : new Position(\n vtable.table,\n isUp ? PositionType.Before : PositionType.After\n );\n if (hasShiftKey) {\n newPos =\n newPos.node.nodeType == NodeType.Element &&\n isVoidHtmlElement(newPos.node)\n ? new Position(\n newPos.node,\n newPos.isAtEnd ? PositionType.After : PositionType.Before\n )\n : newPos;\n const selection = editor.getDocument().defaultView?.getSelection();\n selection?.setBaseAndExtent(\n anchorNode,\n anchorOffset,\n newPos.node,\n newPos.offset\n );\n } else {\n editor.select(newPos);\n }\n }\n });\n }\n },\n defaultDisabled: !Browser.isChrome && !Browser.isSafari,\n};\n\nfunction cacheGetTableCell(event: PluginEvent, editor: IEditor): HTMLTableCellElement {\n return cacheGetEventData(event, 'TABLE_CELL_FOR_TABLE_FEATURES', () => {\n let pos = editor.getFocusedPosition();\n let firstTd = pos && editor.getElementAtCursor('TD,TH,LI', pos.node);\n return (\n firstTd && (getTagOfNode(firstTd) == 'LI' ? null : (firstTd as HTMLTableCellElement))\n );\n });\n}\n\n/**\n * @internal\n */\nexport const TableFeatures: Record<\n keyof TableFeatureSettings,\n BuildInEditFeature\n> = {\n tabInTable: TabInTable,\n upDownInTable: UpDownInTable,\n};\n","export * from './plugins/ContextMenu/index';\n","export { default as ContextMenu, ContextMenuOptions } from './ContextMenu';\n","import { createElement } from 'roosterjs-editor-dom';\nimport {\n ContentPosition,\n EditorPlugin,\n IEditor,\n KnownCreateElementDataIndex,\n PluginEvent,\n PluginEventType,\n} from 'roosterjs-editor-types';\n\nexport interface ContextMenuOptions {\n render: (container: HTMLElement, items: (T | null)[], onDismiss: () => void) => void;\n\n dismiss?: (container: HTMLElement) => void;\n\n allowDefaultMenu?: boolean;\n}\n\n/**\n * An editor plugin that support showing a context menu using render() function from options parameter\n */\nexport default class ContextMenu implements EditorPlugin {\n private container: HTMLElement;\n private editor: IEditor;\n private isMenuShowing: boolean;\n\n /**\n * Create a new instance of ContextMenu class\n * @param options An options object to determine how to show/hide the context menu\n */\n constructor(private options: ContextMenuOptions) {}\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'ContextMenu';\n }\n\n /**\n * Initialize this plugin\n * @param editor The editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.onDismiss();\n\n if (this.container) {\n this.container.parentNode.removeChild(this.container);\n this.container = null;\n }\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n if (event.eventType == PluginEventType.ContextMenu && event.items.length > 0) {\n const { rawEvent, items } = event;\n\n this.onDismiss();\n\n if (!this.options.allowDefaultMenu) {\n rawEvent.preventDefault();\n }\n\n this.initContainer(rawEvent.pageX, rawEvent.pageY);\n this.options.render(this.container, items as T[], this.onDismiss);\n this.isMenuShowing = true;\n }\n }\n\n private initContainer(x: number, y: number) {\n if (!this.container) {\n this.container = createElement(\n KnownCreateElementDataIndex.ContextMenuWrapper,\n this.editor.getDocument()\n ) as HTMLElement;\n this.editor.insertNode(this.container, {\n position: ContentPosition.Outside,\n });\n }\n this.container.style.left = x + 'px';\n this.container.style.top = y + 'px';\n }\n\n private onDismiss = () => {\n if (this.container && this.isMenuShowing) {\n this.options.dismiss?.(this.container);\n this.isMenuShowing = false;\n }\n };\n}\n","export * from './plugins/CustomReplace/index';\n","export { default as CustomReplace } from './CustomReplace';\n","import {\n CustomReplacement,\n EditorPlugin,\n IEditor,\n PluginEvent,\n PluginEventType,\n PositionType,\n} from 'roosterjs-editor-types';\n\nconst makeReplacement = (\n sourceString: string,\n replacementHTML: string,\n matchSourceCaseSensitive: boolean\n): CustomReplacement => ({ sourceString, replacementHTML, matchSourceCaseSensitive });\n\nconst defaultReplacements: CustomReplacement[] = [\n makeReplacement(':)', '🙂', true),\n makeReplacement(';)', '😉', true),\n makeReplacement(':O', '😲', true),\n makeReplacement(':o', '😯', true),\n makeReplacement('<3', '❤️', true),\n];\n\n/**\n * Wrapper for CustomReplaceContentEditFeature that provides an API for updating the\n * content edit feature\n */\nexport default class CustomReplacePlugin implements EditorPlugin {\n private longestReplacementLength: number;\n private editor: IEditor;\n private replacements: CustomReplacement[];\n private replacementEndCharacters: Set;\n\n /**\n * Create instance of CustomReplace plugin\n * @param replacements Replacement rules. If not passed, a default replacement rule set will be applied\n */\n constructor(replacements: CustomReplacement[] = defaultReplacements) {\n this.updateReplacements(replacements);\n }\n\n /**\n * Set the replacements that this plugin is looking for.\n * @param newReplacements new set of replacements for this plugin\n */\n updateReplacements(newReplacements: CustomReplacement[]) {\n this.replacements = newReplacements;\n this.longestReplacementLength = getLongestReplacementSourceLength(this.replacements);\n this.replacementEndCharacters = getReplacementEndCharacters(this.replacements);\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'CustomReplace';\n }\n\n /**\n * Initialize this plugin\n * @param editor The editor instance\n */\n public initialize(editor: IEditor): void {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n public dispose(): void {\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n public onPluginEvent(event: PluginEvent) {\n if (this.editor.isInIME() || event.eventType != PluginEventType.Input) {\n return;\n }\n\n // Exit early on input events that do not insert a replacement's final character.\n if (!event.rawEvent.data || !this.replacementEndCharacters.has(event.rawEvent.data)) {\n return;\n }\n\n // Get the matching replacement\n const range = this.editor.getSelectionRange();\n if (range == null) {\n return;\n }\n const searcher = this.editor.getContentSearcherOfCursor(event);\n const stringToSearch = searcher.getSubStringBefore(this.longestReplacementLength);\n\n const replacement = this.getMatchingReplacement(stringToSearch);\n if (replacement == null) {\n return;\n }\n\n // Reconstruct a selection of the text on the document that matches the\n // replacement we selected.\n const matchingText = searcher.getSubStringBefore(replacement.sourceString.length);\n const matchingRange = searcher.getRangeFromText(matchingText, true /* exactMatch */);\n\n // parse the html string off the dom and inline the resulting element.\n const document = this.editor.getDocument();\n const parsingSpan = document.createElement('span');\n parsingSpan.innerHTML = this.editor.getTrustedHTMLHandler()(replacement.replacementHTML);\n const nodeToInsert =\n parsingSpan.childNodes.length == 1 ? parsingSpan.childNodes[0] : parsingSpan;\n\n // Switch the node for the selection range\n this.editor.addUndoSnapshot(\n () => {\n matchingRange.deleteContents();\n matchingRange.insertNode(nodeToInsert);\n this.editor.select(nodeToInsert, PositionType.End);\n },\n null /*changeSource*/,\n true /*canUndoByBackspace*/\n );\n }\n\n private getMatchingReplacement(stringToSearch: string): CustomReplacement | null {\n if (stringToSearch.length == 0) {\n return null;\n }\n const lowerCaseStringToSearch = stringToSearch.toLocaleLowerCase();\n for (const replacement of this.replacements) {\n const [sourceMatch, replacementMatch] = replacement.matchSourceCaseSensitive\n ? [stringToSearch, replacement.sourceString]\n : [lowerCaseStringToSearch, replacement.sourceString.toLocaleLowerCase()];\n\n if (\n sourceMatch.substring(sourceMatch.length - replacementMatch.length) ==\n replacementMatch\n ) {\n return replacement;\n }\n }\n return null;\n }\n}\n\nfunction getLongestReplacementSourceLength(replacements: CustomReplacement[]): number {\n return Math.max.apply(\n null,\n replacements.map(replacement => replacement.sourceString.length)\n );\n}\n\nfunction getReplacementEndCharacters(replacements: CustomReplacement[]): Set {\n const endChars = new Set();\n for (let replacement of replacements) {\n const sourceString = replacement.sourceString;\n if (sourceString.length == 0) {\n continue;\n }\n const lastChar = sourceString[sourceString.length - 1];\n if (!replacement.matchSourceCaseSensitive) {\n endChars.add(lastChar.toLocaleLowerCase());\n endChars.add(lastChar.toLocaleUpperCase());\n } else {\n endChars.add(lastChar);\n }\n }\n return endChars;\n}\n","export * from './plugins/CutPasteListChain/index';\n","export { default as CutPasteListChain } from './CutPasteListChain';\n","import { experimentCommitListChains } from 'roosterjs-editor-api';\nimport { VListChain } from 'roosterjs-editor-dom';\nimport {\n ChangeSource,\n EditorPlugin,\n IEditor,\n PluginEvent,\n PluginEventType,\n} from 'roosterjs-editor-types';\n\n/**\n * Maintain list numbers of list chain when content is modified by cut/paste/drag&drop\n */\nexport default class CutPasteListChain implements EditorPlugin {\n private chains: VListChain[];\n private expectedChangeSource: ChangeSource;\n private editor: IEditor;\n private disposer: () => void;\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'CutPasteListChain';\n }\n\n /**\n * Initialize this plugin\n * @param editor The editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.disposer = this.editor.addDomEventHandler('drop', this.onDrop);\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.disposer?.();\n this.disposer = null;\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n switch (event.eventType) {\n case PluginEventType.BeforeCutCopy:\n if (event.isCut) {\n this.cacheListChains(ChangeSource.Cut);\n }\n break;\n\n case PluginEventType.BeforePaste:\n this.cacheListChains(ChangeSource.Paste);\n break;\n\n case PluginEventType.ContentChanged:\n if (this.chains?.length > 0 && this.expectedChangeSource == event.source) {\n experimentCommitListChains(this.editor, this.chains);\n this.chains = null;\n this.expectedChangeSource = null;\n }\n break;\n }\n }\n\n private onDrop = () => {\n this.cacheListChains(ChangeSource.Drop);\n };\n\n private cacheListChains(source: ChangeSource) {\n this.chains = VListChain.createListChains(this.editor.getSelectedRegions());\n this.expectedChangeSource = source;\n }\n}\n","export * from './plugins/HyperLink/index';\n","export { default as HyperLink } from './HyperLink';\n","import { Browser, isCharacterValue, isCtrlOrMetaPressed, matchLink } from 'roosterjs-editor-dom';\nimport { EditorPlugin, IEditor, Keys, PluginEvent, PluginEventType } from 'roosterjs-editor-types';\n\n/**\n * An editor plugin that show a tooltip for existing link\n */\nexport default class HyperLink implements EditorPlugin {\n private originalHref: string;\n private trackedLink: HTMLAnchorElement = null;\n private editor: IEditor;\n private disposer: () => void;\n\n /**\n * Create a new instance of HyperLink class\n * @param getTooltipCallback A callback function to get tooltip text for an existing hyperlink.\n * Default value is to return the href itself. If null, there will be no tooltip text.\n * @param target (Optional) Target window name for hyperlink. If null, will use \"_blank\"\n * @param onLinkClick (Optional) Open link callback (return false to use default behavior)\n */\n constructor(\n private getTooltipCallback: (href: string, a: HTMLAnchorElement) => string = href => href,\n private target?: string,\n private onLinkClick?: (anchor: HTMLAnchorElement, mouseEvent: MouseEvent) => boolean | void\n ) {}\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'Hyperlink';\n }\n\n /**\n * Initialize this plugin\n * @param editor The editor instance\n */\n public initialize(editor: IEditor): void {\n this.editor = editor;\n this.disposer =\n this.getTooltipCallback &&\n editor.addDomEventHandler({\n mouseover: this.onMouse,\n mouseout: this.onMouse,\n blur: this.onBlur,\n });\n }\n\n protected onMouse = (e: MouseEvent) => {\n const a = this.editor.getElementAtCursor('a[href]', e.target) as HTMLAnchorElement;\n const href = this.tryGetHref(a);\n\n if (href) {\n this.editor.setEditorDomAttribute(\n 'title',\n e.type == 'mouseover' ? this.getTooltipCallback(href, a) : null\n );\n }\n };\n\n protected onBlur = (e: FocusEvent) => {\n if (this.trackedLink) {\n this.updateLinkHrefIfShouldUpdate();\n }\n\n this.resetLinkTracking();\n };\n\n /**\n * Dispose this plugin\n */\n public dispose(): void {\n if (this.disposer) {\n this.disposer();\n this.disposer = null;\n }\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n public onPluginEvent(event: PluginEvent): void {\n if (\n event.eventType == PluginEventType.MouseUp ||\n (event.eventType == PluginEventType.KeyUp &&\n (!this.isContentEditValue(event.rawEvent) || event.rawEvent.which == Keys.SPACE)) ||\n event.eventType == PluginEventType.ContentChanged\n ) {\n const anchor = this.editor.getElementAtCursor(\n 'A[href]',\n null /*startFrom*/,\n event\n ) as HTMLAnchorElement;\n\n const shouldCheckUpdateLink =\n anchor !== this.trackedLink ||\n event.eventType == PluginEventType.KeyUp ||\n event.eventType == PluginEventType.ContentChanged;\n\n if (\n this.trackedLink &&\n (shouldCheckUpdateLink || this.tryGetHref(this.trackedLink) !== this.originalHref)\n ) {\n // If cursor has moved out of previously tracked link\n // update link href if display text doesn't match href anymore.\n if (shouldCheckUpdateLink) {\n this.updateLinkHrefIfShouldUpdate();\n }\n\n // If the link's href value was edited, or the cursor has moved out of the\n // previously tracked link, stop tracking the link.\n this.resetLinkTracking();\n }\n\n // Cache link and href value if its href attribute currently matches its display text\n if (!this.trackedLink && this.doesLinkDisplayMatchHref(anchor)) {\n this.trackedLink = anchor;\n this.originalHref = this.tryGetHref(anchor);\n }\n }\n\n if (event.eventType == PluginEventType.MouseUp) {\n const anchor = this.editor.getElementAtCursor(\n 'A',\n event.rawEvent.srcElement\n ) as HTMLAnchorElement;\n\n if (anchor) {\n if (this.onLinkClick && this.onLinkClick(anchor, event.rawEvent) !== false) {\n return;\n }\n\n let href: string;\n if (\n !Browser.isFirefox &&\n (href = this.tryGetHref(anchor)) &&\n isCtrlOrMetaPressed(event.rawEvent) &&\n event.rawEvent.button === 0\n ) {\n try {\n const target = this.target || '_blank';\n const window = this.editor.getDocument().defaultView;\n window.open(href, target);\n } catch {}\n }\n }\n }\n }\n\n /**\n * Try get href from an anchor element\n * The reason this is put in a try-catch is that\n * it has been seen that accessing href may throw an exception, in particular on IE/Edge\n */\n private tryGetHref(anchor: HTMLAnchorElement): string {\n try {\n return anchor ? anchor.href : null;\n } catch {}\n }\n\n /**\n * Determines if KeyboardEvent is meant to edit content\n */\n private isContentEditValue(event: KeyboardEvent): boolean {\n return (\n isCharacterValue(event) || event.which == Keys.BACKSPACE || event.which == Keys.DELETE\n );\n }\n\n /**\n * Updates the href of the tracked link if the display text doesn't match href anymore\n */\n private updateLinkHrefIfShouldUpdate() {\n if (!this.doesLinkDisplayMatchHref(this.trackedLink)) {\n this.updateLinkHref();\n }\n }\n\n /**\n * Clears the tracked link and its original href value so that it's back to default state\n */\n private resetLinkTracking() {\n this.trackedLink = null;\n this.originalHref = '';\n }\n\n /**\n * Compares the normalized URL of inner text of element to its href to see if they match.\n */\n private doesLinkDisplayMatchHref(element: HTMLAnchorElement): boolean {\n if (element) {\n let display = element.innerText.trim();\n\n // We first escape the display text so that any text passed into the regex is not\n // treated as a special character.\n let escapedDisplay = display.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n let rule = new RegExp(`^(?:https?:\\\\/\\\\/)?${escapedDisplay}\\\\/?`, 'i');\n let href = this.tryGetHref(element);\n if (href !== null) {\n return rule.test(href);\n }\n }\n\n return false;\n }\n\n /**\n * Update href of an element in place to new display text if it's a valid URL\n */\n private updateLinkHref() {\n if (this.trackedLink) {\n let linkData = matchLink(this.trackedLink.innerText.trim());\n if (linkData !== null) {\n this.editor.addUndoSnapshot(() => {\n this.trackedLink.href = linkData.normalizedUrl;\n });\n }\n }\n }\n}\n","export { default as ImageEdit } from './ImageEdit';\nexport { default as canRegenerateImage } from './api/canRegenerateImage';\nexport { default as resizeByPercentage } from './api/resizeByPercentage';\nexport { default as isResizedTo } from './api/isResizedTo';\nexport { default as resetImage } from './api/resetImage';\n","import applyChange from './editInfoUtils/applyChange';\nimport canRegenerateImage from './api/canRegenerateImage';\nimport Cropper, { getCropHTML } from './imageEditors/Cropper';\nimport deleteEditInfo from './editInfoUtils/deleteEditInfo';\nimport DragAndDropContext, { X, Y } from './types/DragAndDropContext';\nimport DragAndDropHandler from '../../pluginUtils/DragAndDropHandler';\nimport DragAndDropHelper from '../../pluginUtils/DragAndDropHelper';\nimport getEditInfoFromImage from './editInfoUtils/getEditInfoFromImage';\nimport getGeneratedImageSize from './editInfoUtils/getGeneratedImageSize';\nimport ImageEditInfo from './types/ImageEditInfo';\nimport ImageHtmlOptions from './types/ImageHtmlOptions';\nimport Rotator, { getRotateHTML, ROTATE_GAP, ROTATE_SIZE } from './imageEditors/Rotator';\nimport { ImageEditElementClass } from './types/ImageEditElementClass';\nimport { insertEntity } from 'roosterjs-editor-api';\nimport {\n arrayPush,\n Browser,\n createElement,\n getComputedStyle,\n getEntityFromElement,\n getEntitySelector,\n matchesSelector,\n safeInstanceOf,\n toArray,\n wrap,\n} from 'roosterjs-editor-dom';\nimport Resizer, {\n doubleCheckResize,\n getSideResizeHTML,\n getCornerResizeHTML,\n} from './imageEditors/Resizer';\nimport {\n ExperimentalFeatures,\n ImageEditOperation,\n ImageEditOptions,\n ChangeSource,\n EditorPlugin,\n IEditor,\n PluginEvent,\n PluginEventType,\n EntityOperation,\n Entity,\n Keys,\n PositionType,\n CreateElementData,\n KnownCreateElementDataIndex,\n} from 'roosterjs-editor-types';\n\nconst SHIFT_KEYCODE = 16;\nconst CTRL_KEYCODE = 17;\nconst ALT_KEYCODE = 18;\n\nconst DIRECTIONS = 8;\nconst DirectionRad = (Math.PI * 2) / DIRECTIONS;\nconst DirectionOrder = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];\n\n/**\n * Map the experimental features to image edit operations to help determine which operation is allowed\n */\nconst FeatureToOperationMap = {\n [ExperimentalFeatures.SingleDirectionResize]: ImageEditOperation.SideResize,\n [ExperimentalFeatures.ImageRotate]: ImageEditOperation.Rotate,\n [ExperimentalFeatures.ImageCrop]: ImageEditOperation.Crop,\n};\n\n/**\n * Default image edit options\n */\nconst DefaultOptions: Required = {\n borderColor: '#DB626C',\n minWidth: 10,\n minHeight: 10,\n preserveRatio: false,\n minRotateDeg: 5,\n imageSelector: 'img',\n rotateIconHTML: null,\n};\n\n/**\n * Map the image edit operation to a function that returns editing elements HTML to help\n * build image editing UI\n */\nconst ImageEditHTMLMap = {\n [ImageEditOperation.CornerResize]: getCornerResizeHTML,\n [ImageEditOperation.SideResize]: getSideResizeHTML,\n [ImageEditOperation.Rotate]: getRotateHTML,\n [ImageEditOperation.Crop]: getCropHTML,\n};\n\n/**\n * Image edit entity name\n */\nconst IMAGE_EDIT_WRAPPER_ENTITY_TYPE = 'IMAGE_EDIT_WRAPPER';\n\n/**\n * Default background colors for rotate handle\n */\nconst LIGHT_MODE_BGCOLOR = 'white';\nconst DARK_MODE_BGCOLOR = '#333';\n\n/**\n * ImageEdit plugin provides the ability to edit an inline image in editor, including image resizing, rotation and cropping\n */\nexport default class ImageEdit implements EditorPlugin {\n protected editor: IEditor;\n protected options: ImageEditOptions;\n private disposer: () => void;\n\n // Allowed editing operations\n private allowedOperations: ImageEditOperation = ImageEditOperation.CornerResize;\n\n // Current editing image\n private image: HTMLImageElement;\n\n // Current edit info of the image. All changes user made will be stored in this object.\n // We use this object to update the editing UI, and finally we will use this object to generate\n // the new image if necessary\n private editInfo: ImageEditInfo;\n\n // Src of the image before current editing\n private lastSrc: string;\n\n // Drag and drop helper objects\n private dndHelpers: DragAndDropHelper[];\n\n /**\n * Create a new instance of ImageEdit\n * @param options Image editing options\n */\n constructor(options?: ImageEditOptions) {\n this.options = {\n ...DefaultOptions,\n ...(options || {}),\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'ImageEdit';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.disposer = editor.addDomEventHandler('blur', this.onBlur);\n\n // Read current enabled features from editor to determine which editing operations are allowed\n Object.keys(FeatureToOperationMap).forEach((key: keyof typeof FeatureToOperationMap) => {\n this.allowedOperations |= this.editor.isFeatureEnabled(key)\n ? FeatureToOperationMap[key]\n : 0;\n });\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.clearDndHelpers();\n this.disposer();\n this.disposer = null;\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(e: PluginEvent) {\n switch (e.eventType) {\n case PluginEventType.MouseDown:\n this.setEditingImage(null);\n break;\n\n case PluginEventType.MouseUp:\n const target = e.rawEvent.target;\n if (\n e.isClicking &&\n e.rawEvent.button == 0 &&\n safeInstanceOf(target, 'HTMLImageElement') &&\n target.isContentEditable &&\n matchesSelector(target, this.options.imageSelector)\n ) {\n this.setEditingImage(target, ImageEditOperation.ResizeAndRotate);\n }\n\n break;\n\n case PluginEventType.KeyDown:\n const key = e.rawEvent.which;\n if (key == Keys.DELETE || key == Keys.BACKSPACE) {\n // Set current editing image to null and select the image if any, and do not prevent default of the event\n // so that browser will delete the selected image for us\n this.setEditingImage(null, true /*selectImage*/);\n } else if (key == Keys.ESCAPE && this.image) {\n // Press ESC should cancel current editing operation, resume back to original edit info\n this.editInfo = getEditInfoFromImage(this.image);\n this.setEditingImage(null);\n e.rawEvent.preventDefault();\n } else if (key != SHIFT_KEYCODE && key != CTRL_KEYCODE && key != ALT_KEYCODE) {\n // For other key, just unselect current image and select it. If this is an input key, the image will be replaced\n this.setEditingImage(null, true /*selectImage*/);\n }\n break;\n\n case PluginEventType.ContentChanged:\n if (\n e.source != ChangeSource.InsertEntity ||\n (e.data).type != IMAGE_EDIT_WRAPPER_ENTITY_TYPE\n ) {\n // After contentChanged event, the current image wrapper may not be valid any more, remove all of them if any\n this.editor.queryElements(\n getEntitySelector(IMAGE_EDIT_WRAPPER_ENTITY_TYPE),\n this.removeWrapper\n );\n }\n\n break;\n\n case PluginEventType.EntityOperation:\n if (e.entity.type == IMAGE_EDIT_WRAPPER_ENTITY_TYPE) {\n if (e.operation == EntityOperation.ReplaceTemporaryContent) {\n this.removeWrapper(e.entity.wrapper);\n } else if (e.operation == EntityOperation.Click) {\n e.rawEvent.preventDefault();\n }\n }\n break;\n\n case PluginEventType.ExtractContentWithDom:\n // When extract content, remove all image info since they may not be valid when load the content again\n toArray(e.clonedRoot.querySelectorAll(this.options.imageSelector)).forEach(img => {\n deleteEditInfo(img as HTMLImageElement);\n });\n break;\n }\n }\n\n /**\n * Set current image for edit. If there is already image in editing, it will quit editing mode and any pending editing\n * operation will be submitted\n * @param image The image to edit\n * @param operation The editing operation\n */\n setEditingImage(image: HTMLImageElement, operation: ImageEditOperation): void;\n\n /**\n * Stop editing image. If there is already image in editing, it will quit editing mode and any pending editing\n * operation will be submitted\n * @param image The image to edit\n * @param selectImage True to select this image after quit editing mode\n */\n setEditingImage(image: null, selectImage?: boolean): void;\n\n setEditingImage(\n image: HTMLImageElement | null,\n operationOrSelect?: ImageEditOperation | boolean\n ) {\n let operation =\n typeof operationOrSelect === 'number' ? operationOrSelect : ImageEditOperation.None;\n const selectImage = typeof operationOrSelect === 'number' ? false : !!operationOrSelect;\n\n if (this.image) {\n // When there is image in editing, clean up any cached objects and elements\n this.clearDndHelpers();\n\n // Apply the changes, and add undo snapshot if necessary\n if (applyChange(this.editor, this.image, this.editInfo, this.lastSrc)) {\n this.editor.addUndoSnapshot(() => this.image, ChangeSource.ImageResize);\n }\n\n // Remove editing wrapper\n const wrapper = this.getImageWrapper(this.image);\n if (wrapper) {\n this.removeWrapper(wrapper);\n }\n\n if (selectImage) {\n this.editor.select(this.image);\n }\n\n this.image = null;\n this.editInfo = null;\n this.lastSrc = null;\n }\n\n if (!this.image && image?.isContentEditable) {\n // If there is new image to edit, enter editing mode for this image\n this.editor.addUndoSnapshot();\n this.image = image;\n\n // Get initial edit info\n this.editInfo = getEditInfoFromImage(image);\n operation =\n (canRegenerateImage(image) ? operation : ImageEditOperation.Resize) &\n this.allowedOperations;\n\n // Create and update editing wrapper and elements\n const wrapper = this.createWrapper(operation);\n this.updateWrapper();\n\n // Init drag and drop\n this.dndHelpers = [\n ...this.createDndHelpers(ImageEditElementClass.ResizeHandle, Resizer),\n ...this.createDndHelpers(ImageEditElementClass.RotateHandle, Rotator),\n ...this.createDndHelpers(ImageEditElementClass.CropHandle, Cropper),\n ...this.createDndHelpers(ImageEditElementClass.CropContainer, Cropper),\n ];\n\n // Put cursor next to the image\n this.editor.select(wrapper, PositionType.After);\n }\n }\n\n /**\n * quit editing mode when editor lose focus\n */\n private onBlur = () => {\n this.setEditingImage(null);\n };\n\n /**\n * Create editing wrapper for the image\n */\n private createWrapper(operation: ImageEditOperation) {\n // Wrap the image with an entity so that we can easily retrieve it later\n const { wrapper } = insertEntity(\n this.editor,\n IMAGE_EDIT_WRAPPER_ENTITY_TYPE,\n wrap(this.image, KnownCreateElementDataIndex.ImageEditWrapper),\n false /*isBlock*/,\n true /*isReadonly*/\n );\n\n wrapper.style.position = 'relative';\n wrapper.style.maxWidth = '100%';\n wrapper.style.verticalAlign = 'bottom';\n wrapper.style.display = Browser.isSafari ? 'inline-block' : 'inline-flex';\n\n // Cache current src so that we can compare it after edit see if src is changed\n this.lastSrc = this.image.src;\n\n // Set image src to original src to help show editing UI, also it will be used when regenerate image dataURL after editing\n this.image.src = this.editInfo.src;\n this.image.style.position = 'absolute';\n this.image.style.maxWidth = null;\n\n // Get HTML for all edit elements (resize handle, rotate handle, crop handle and overlay, ...) and create HTML element\n const options: ImageHtmlOptions = {\n borderColor: this.options.borderColor,\n rotateIconHTML: this.options.rotateIconHTML,\n rotateHandleBackColor: this.editor.isDarkMode()\n ? DARK_MODE_BGCOLOR\n : LIGHT_MODE_BGCOLOR,\n };\n const htmlData: CreateElementData[] = [];\n\n ((Object.keys(ImageEditHTMLMap) as any[]) as (keyof typeof ImageEditHTMLMap)[]).forEach(\n thisOperation => {\n if ((operation & thisOperation) == thisOperation) {\n arrayPush(htmlData, ImageEditHTMLMap[thisOperation](options));\n }\n }\n );\n\n htmlData.forEach(data => {\n const element = createElement(data, this.image.ownerDocument);\n if (element) {\n wrapper.appendChild(element);\n }\n });\n return wrapper;\n }\n\n /**\n * Get image wrapper from image\n * @param image The image to get wrapper from\n */\n private getImageWrapper(image: HTMLImageElement): HTMLElement {\n // Get the image wrapper from image using Entity API\n const entity = getEntityFromElement(image?.parentNode?.parentNode as HTMLElement);\n\n return entity?.type == IMAGE_EDIT_WRAPPER_ENTITY_TYPE ? entity.wrapper : null;\n }\n\n /**\n * Remove the temp wrapper of the image\n * @param wrapper The wrapper object to remove. If not specified, remove all existing wrappers.\n */\n private removeWrapper = (wrapper: HTMLElement) => {\n const parent = wrapper?.parentNode;\n const img = wrapper?.querySelector('img');\n\n if (img && parent) {\n img.style.position = '';\n img.style.maxWidth = '100%';\n img.style.margin = null;\n img.style.textAlign = null;\n\n parent.insertBefore(img, wrapper);\n parent.removeChild(wrapper);\n }\n };\n\n /**\n * Update image edit elements to reflect current editing result\n * @param context\n */\n private updateWrapper = (context?: DragAndDropContext) => {\n const wrapper = this.getImageWrapper(this.image);\n if (wrapper) {\n // Prepare: get related editing elements\n const cropContainers = getEditElements(wrapper, ImageEditElementClass.CropContainer);\n const cropOverlays = getEditElements(wrapper, ImageEditElementClass.CropOverlay);\n const rotateCenter = getEditElements(wrapper, ImageEditElementClass.RotateCenter)[0];\n const rotateHandle = getEditElements(wrapper, ImageEditElementClass.RotateHandle)[0];\n const resizeHandles = getEditElements(wrapper, ImageEditElementClass.ResizeHandle);\n const cropHandles = getEditElements(wrapper, ImageEditElementClass.CropHandle);\n\n // Cropping and resizing will show different UI, so check if it is cropping here first\n const isCropping = cropContainers.length == 1 && cropOverlays.length == 4;\n const {\n angleRad,\n heightPx,\n bottomPercent,\n leftPercent,\n rightPercent,\n topPercent,\n } = this.editInfo;\n\n // Width/height of the image\n const {\n targetWidth,\n targetHeight,\n originalWidth,\n originalHeight,\n visibleWidth,\n visibleHeight,\n } = getGeneratedImageSize(this.editInfo, isCropping);\n const marginHorizontal = (targetWidth - visibleWidth) / 2;\n const marginVertical = (targetHeight - visibleHeight) / 2;\n const cropLeftPx = originalWidth * leftPercent;\n const cropRightPx = originalWidth * rightPercent;\n const cropTopPx = originalHeight * topPercent;\n const cropBottomPx = originalHeight * bottomPercent;\n\n // Update size and margin of the wrapper\n wrapper.style.width = getPx(visibleWidth);\n wrapper.style.height = getPx(visibleHeight);\n wrapper.style.margin = `${marginVertical}px ${marginHorizontal}px`;\n wrapper.style.transform = `rotate(${angleRad}rad)`;\n\n // Update the text-alignment to avoid the image to overflow if the parent element have align center or right\n // or if the direction is Right To Left\n wrapper.style.textAlign = isRtl(wrapper.parentNode) ? 'right' : 'left';\n\n // Update size of the image\n this.image.style.width = getPx(originalWidth);\n this.image.style.height = getPx(originalHeight);\n\n if (isCropping) {\n // For crop, we also need to set position of the overlays\n setSize(\n cropContainers[0],\n cropLeftPx,\n cropTopPx,\n cropRightPx,\n cropBottomPx,\n undefined,\n undefined\n );\n setSize(cropOverlays[0], 0, 0, cropRightPx, undefined, undefined, cropTopPx);\n setSize(cropOverlays[1], undefined, 0, 0, cropBottomPx, cropRightPx, undefined);\n setSize(cropOverlays[2], cropLeftPx, undefined, 0, 0, undefined, cropBottomPx);\n setSize(cropOverlays[3], 0, cropTopPx, undefined, 0, cropLeftPx, undefined);\n updateHandleCursor(cropHandles, angleRad);\n } else {\n // For rotate/resize, set the margin of the image so that cropped part won't be visible\n this.image.style.margin = `${-cropTopPx}px 0 0 ${-cropLeftPx}px`;\n\n // Double check resize\n if (context?.elementClass == ImageEditElementClass.ResizeHandle) {\n const clientWidth = wrapper.clientWidth;\n const clientHeight = wrapper.clientHeight;\n doubleCheckResize(\n this.editInfo,\n this.options.preserveRatio,\n clientWidth,\n clientHeight\n );\n\n this.updateWrapper();\n }\n\n // Move rotate handle. When image is very close to the border of editor, rotate handle may not be visible.\n // Fix it by reduce the distance from image to rotate handle\n const distance = this.editor.getRelativeDistanceToEditor(\n wrapper,\n true /*addScroll*/\n );\n\n if (rotateCenter && rotateHandle && distance) {\n const cosAngle = Math.cos(angleRad);\n const adjustedDistance =\n cosAngle <= 0\n ? Number.MAX_SAFE_INTEGER\n : (distance[1] + heightPx / 2 + marginVertical) / cosAngle -\n heightPx / 2;\n\n const rotateGap = Math.max(Math.min(ROTATE_GAP, adjustedDistance), 0);\n const rotateTop = Math.max(\n Math.min(ROTATE_SIZE, adjustedDistance - rotateGap),\n 0\n );\n rotateCenter.style.top = getPx(-rotateGap);\n rotateCenter.style.height = getPx(rotateGap);\n rotateHandle.style.top = getPx(-rotateTop);\n updateHandleCursor(resizeHandles, angleRad);\n }\n }\n }\n };\n\n /**\n * Create drag and drop helpers\n * @param wrapper\n * @param elementClass\n * @param dragAndDrop\n */\n private createDndHelpers(\n elementClass: ImageEditElementClass,\n dragAndDrop: DragAndDropHandler\n ): DragAndDropHelper[] {\n const commonContext = {\n editInfo: this.editInfo,\n options: this.options,\n elementClass,\n };\n const wrapper = this.getImageWrapper(this.image);\n return wrapper\n ? getEditElements(wrapper, elementClass).map(\n element =>\n new DragAndDropHelper(\n element,\n {\n ...commonContext,\n x: element.dataset.x as X,\n y: element.dataset.y as Y,\n },\n this.updateWrapper,\n dragAndDrop\n )\n )\n : [];\n }\n\n /**\n * Clean up drag and drop helpers\n */\n private clearDndHelpers() {\n this.dndHelpers?.forEach(helper => helper.dispose());\n this.dndHelpers = null;\n }\n}\n\nfunction setSize(\n element: HTMLElement,\n left: number,\n top: number,\n right: number,\n bottom: number,\n width: number,\n height: number\n) {\n element.style.left = getPx(left);\n element.style.top = getPx(top);\n element.style.right = getPx(right);\n element.style.bottom = getPx(bottom);\n element.style.width = getPx(width);\n element.style.height = getPx(height);\n}\n\nfunction getPx(value: number): string {\n return value === undefined ? null : value + 'px';\n}\n\nfunction getEditElements(wrapper: HTMLElement, elementClass: ImageEditElementClass): HTMLElement[] {\n return toArray(wrapper.querySelectorAll('.' + elementClass)) as HTMLElement[];\n}\n\nfunction isRtl(element: Node): boolean {\n return safeInstanceOf(element, 'HTMLElement')\n ? getComputedStyle(element, 'direction') == 'rtl'\n : false;\n}\n\nfunction handleRadIndexCalculator(angleRad: number): number {\n let idx = Math.round(angleRad / DirectionRad) % DIRECTIONS;\n return idx < 0 ? idx + DIRECTIONS : idx;\n}\n\nfunction rotateHandles(element: HTMLElement, angleRad: number): string {\n const radIndex = handleRadIndexCalculator(angleRad);\n const originalDirection = element.dataset.y + element.dataset.x;\n const originalIndex = DirectionOrder.indexOf(originalDirection);\n const rotatedIndex = originalIndex >= 0 && originalIndex + radIndex;\n return DirectionOrder[rotatedIndex % DIRECTIONS];\n}\n\n/**\n * Rotate the resizer and cropper handles according to the image position.\n * @param handles The resizer handles.\n * @param angleRad The angle that the image was rotated.\n */\nfunction updateHandleCursor(handles: HTMLElement[], angleRad: number) {\n handles.map(handle => {\n handle.style.cursor = `${rotateHandles(handle, angleRad)}-resize`;\n });\n}\n","import getGeneratedImageSize from './getGeneratedImageSize';\nimport ImageEditInfo from '../types/ImageEditInfo';\n\n/**\n * @internal\n * Generate new dataURL from an image and edit info\n * @param image The image to generate data URL from. It is supposed to have original src loaded\n * @param editInfo Edit info of the image\n * @returns A BASE64 encoded string with image prefix that represents the content of the generated image.\n * If there are rotate/crop/resize info in the edit info, the generated image will also reflect the result.\n * It is possible to throw exception since the original image may not be able to read its content from\n * the code, so better check canRegenerateImage() of the image first.\n * @throws Exception when fail to generate dataURL from canvas\n */\nexport default function generateDataURL(image: HTMLImageElement, editInfo: ImageEditInfo): string {\n const {\n angleRad: angle,\n widthPx: width,\n heightPx: height,\n bottomPercent: bottom,\n leftPercent: left,\n rightPercent: right,\n topPercent: top,\n naturalWidth,\n naturalHeight,\n } = editInfo;\n const imageWidth = naturalWidth * (1 - left - right);\n const imageHeight = naturalHeight * (1 - top - bottom);\n const canvas = document.createElement('canvas');\n const { targetWidth, targetHeight } = getGeneratedImageSize(editInfo);\n canvas.width = targetWidth;\n canvas.height = targetHeight;\n\n const context = canvas.getContext('2d');\n context.translate(targetWidth / 2, targetHeight / 2);\n context.rotate(angle);\n context.drawImage(\n image,\n naturalWidth * left,\n naturalHeight * top,\n imageWidth,\n imageHeight,\n -width / 2,\n -height / 2,\n width,\n height\n );\n\n return canvas.toDataURL('image/png', 1.0);\n}\n","import ImageEditInfo, { IMAGE_EDIT_INFO_NAME } from '../types/ImageEditInfo';\n\n/**\n * @internal\n * Save edit info to image\n * @param image The image to save edit info to\n * @param editInfo The edit info to save\n */\nexport default function saveEditInfo(image: HTMLImageElement, editInfo: ImageEditInfo) {\n if (image) {\n image.dataset[IMAGE_EDIT_INFO_NAME] = JSON.stringify(editInfo);\n }\n}\n","import DragAndDropContext, { X, Y } from '../types/DragAndDropContext';\nimport DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler';\nimport { CreateElementData } from 'roosterjs-editor-types';\nimport { CropInfo } from '../types/ImageEditInfo';\nimport { ImageEditElementClass } from '../types/ImageEditElementClass';\nimport { rotateCoordinate } from './Resizer';\n\nconst CROP_HANDLE_SIZE = 22;\nconst CROP_HANDLE_WIDTH = 7;\nconst Xs: X[] = ['w', 'e'];\nconst Ys: Y[] = ['s', 'n'];\nconst ROTATION: Record = {\n sw: 0,\n nw: 90,\n ne: 180,\n se: 270,\n};\n\n/**\n * Crop handle for DragAndDropHelper\n */\nconst Cropper: DragAndDropHandler = {\n onDragStart: ({ editInfo }) => ({ ...editInfo }),\n onDragging: ({ editInfo, x, y, options }, e, base, dx, dy) => {\n [dx, dy] = rotateCoordinate(dx, dy, editInfo.angleRad);\n\n const {\n widthPx,\n heightPx,\n leftPercent,\n rightPercent,\n topPercent,\n bottomPercent,\n } = editInfo;\n const { minWidth, minHeight } = options;\n const widthPercent = 1 - leftPercent - rightPercent;\n const heightPercent = 1 - topPercent - bottomPercent;\n\n if (widthPercent > 0 && heightPercent > 0) {\n const fullWidth = widthPx / widthPercent;\n const fullHeight = heightPx / heightPercent;\n const newLeft =\n x != 'e'\n ? crop(base.leftPercent, dx, fullWidth, rightPercent, minWidth)\n : leftPercent;\n const newRight =\n x != 'w'\n ? crop(base.rightPercent, -dx, fullWidth, leftPercent, minWidth)\n : rightPercent;\n const newTop =\n y != 's'\n ? crop(base.topPercent, dy, fullHeight, bottomPercent, minHeight)\n : topPercent;\n const newBottom =\n y != 'n'\n ? crop(base.bottomPercent, -dy, fullHeight, topPercent, minHeight)\n : bottomPercent;\n\n editInfo.leftPercent = newLeft;\n editInfo.rightPercent = newRight;\n editInfo.topPercent = newTop;\n editInfo.bottomPercent = newBottom;\n editInfo.widthPx = fullWidth * (1 - newLeft - newRight);\n editInfo.heightPx = fullHeight * (1 - newTop - newBottom);\n\n return true;\n } else {\n return false;\n }\n },\n};\n\nfunction crop(\n basePercentage: number,\n deltaValue: number,\n fullValue: number,\n currentPercentage: number,\n minValue: number\n): number {\n const maxValue = fullValue * (1 - currentPercentage) - minValue;\n const newValue = fullValue * basePercentage + deltaValue;\n const validValue = Math.max(Math.min(newValue, maxValue), 0);\n return validValue / fullValue;\n}\n\n/**\n * @internal\n */\nexport default Cropper;\n\n/**\n * @internal\n * Get HTML for crop elements, including 4 overlays (to show dark shadow), 1 container and 4 crop handles\n */\nexport function getCropHTML(): CreateElementData[] {\n const overlayHTML: CreateElementData = {\n tag: 'div',\n style: 'position:absolute;background-color:rgb(0,0,0,0.5);pointer-events:none',\n className: ImageEditElementClass.CropOverlay,\n };\n const containerHTML: CreateElementData = {\n tag: 'div',\n style: 'position:absolute;overflow:hidden',\n className: ImageEditElementClass.CropContainer,\n children: [],\n };\n\n Xs.forEach(x => Ys.forEach(y => containerHTML.children.push(getCropHTMLInternal(x, y))));\n\n return [containerHTML, overlayHTML, overlayHTML, overlayHTML, overlayHTML];\n}\n\nfunction getCropHTMLInternal(x: X, y: Y): CreateElementData {\n const leftOrRight = x == 'w' ? 'left' : 'right';\n const topOrBottom = y == 'n' ? 'top' : 'bottom';\n const rotation = ROTATION[y + x];\n\n return {\n tag: 'div',\n className: ImageEditElementClass.CropHandle,\n style: `position:absolute;pointer-events:auto;cursor:${y}${x}-resize;${leftOrRight}:0;${topOrBottom}:0;width:${CROP_HANDLE_SIZE}px;height:${CROP_HANDLE_SIZE}px;transform:rotate(${rotation}deg)`,\n dataset: { x, y },\n children: getCropHandleHTML(),\n };\n}\n\nfunction getCropHandleHTML(): CreateElementData[] {\n const result: CreateElementData[] = [];\n [0, 1].forEach(layer =>\n [0, 1].forEach(dir => {\n result.push(getCropHandleHTMLInternal(layer, dir));\n })\n );\n return result;\n}\n\nfunction getCropHandleHTMLInternal(layer: number, dir: number): CreateElementData {\n const position =\n dir == 0\n ? `right:${layer}px;height:${CROP_HANDLE_WIDTH - layer * 2}px;`\n : `top:${layer}px;width:${CROP_HANDLE_WIDTH - layer * 2}px;`;\n const bgColor = layer == 0 ? 'white' : 'black';\n\n return {\n tag: 'div',\n style: `position:absolute;left:${layer}px;bottom:${layer}px;${position};background-color:${bgColor}`,\n };\n}\n","import DragAndDropHandler from './DragAndDropHandler';\n\n/**\n * @internal\n * A helper class to help manage drag and drop to an HTML element\n */\nexport default class DragAndDropHelper {\n private initX: number;\n private initY: number;\n private initValue: TInitValue;\n\n /**\n * Create a new instance of DragAndDropHelper class\n * @param trigger The trigger element. When user start drag on this element,\n * events will be fired to the handler object\n * @param context Context object that will be passed to handler function when event is fired,\n * so that the handler object knows which element it is triggered from.\n * @param onSubmit A callback that will be invoked when event handler in handler object returns true\n * @param handler The event handler object, see DragAndDropHandler interface for more information\n */\n constructor(\n private trigger: HTMLElement,\n private context: TContext,\n private onSubmit: (context: TContext) => void,\n private handler: DragAndDropHandler\n ) {\n trigger.addEventListener('mousedown', this.onMouseDown);\n }\n\n /**\n * Dispose this object, remove all event listeners that has been attached\n */\n dispose() {\n this.trigger.removeEventListener('mousedown', this.onMouseDown);\n this.removeDocumentEvents();\n }\n\n private addDocumentEvents() {\n const doc = this.trigger.ownerDocument;\n doc.addEventListener('mousemove', this.onMouseMove, true /*useCapture*/);\n doc.addEventListener('mouseup', this.onMouseUp, true /*useCapture*/);\n }\n\n private removeDocumentEvents() {\n const doc = this.trigger.ownerDocument;\n doc.removeEventListener('mousemove', this.onMouseMove, true /*useCapture*/);\n doc.removeEventListener('mouseup', this.onMouseUp, true /*useCapture*/);\n }\n\n private onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n this.addDocumentEvents();\n\n this.initX = e.pageX;\n this.initY = e.pageY;\n this.initValue = this.handler.onDragStart?.(this.context, e);\n };\n\n private onMouseMove = (e: MouseEvent) => {\n e.preventDefault();\n const deltaX = e.pageX - this.initX;\n const deltaY = e.pageY - this.initY;\n if (this.handler.onDragging?.(this.context, e, this.initValue, deltaX, deltaY)) {\n this.onSubmit?.(this.context);\n }\n };\n\n private onMouseUp = (e: MouseEvent) => {\n e.preventDefault();\n this.removeDocumentEvents();\n\n if (this.handler.onDragEnd?.(this.context, e, this.initValue)) {\n this.onSubmit?.(this.context);\n }\n };\n}\n","import DragAndDropContext from '../types/DragAndDropContext';\nimport DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler';\nimport ImageHtmlOptions from '../types/ImageHtmlOptions';\nimport { CreateElementData } from 'roosterjs-editor-types';\nimport { ImageEditElementClass } from '../types/ImageEditElementClass';\nimport { RotateInfo } from '../types/ImageEditInfo';\n\n/**\n * @internal Size of rotate icon\n */\nexport const ROTATE_SIZE = 32;\n\n/**\n * @internal Gap between image and the rotate handle\n */\nexport const ROTATE_GAP = 15;\n\nconst DEG_PER_RAD = 180 / Math.PI;\nconst DEFAULT_ROTATE_HANDLE_HEIGHT = ROTATE_SIZE / 2 + ROTATE_GAP;\nconst ROTATE_ICON_MARGIN = 8;\n\n/**\n * The rotate drag and drop handler\n */\nconst Rotator: DragAndDropHandler = {\n onDragStart: ({ editInfo }) => ({ ...editInfo }),\n onDragging: ({ editInfo, options }, e, base, deltaX, deltaY) => {\n const distance = editInfo.heightPx / 2 + DEFAULT_ROTATE_HANDLE_HEIGHT;\n const newX = distance * Math.sin(base.angleRad) + deltaX;\n const newY = distance * Math.cos(base.angleRad) - deltaY;\n let angleInRad = Math.atan2(newX, newY);\n\n if (!e.altKey) {\n const angleInDeg = angleInRad * DEG_PER_RAD;\n const adjustedAngleInDeg =\n Math.round(angleInDeg / options.minRotateDeg) * options.minRotateDeg;\n angleInRad = adjustedAngleInDeg / DEG_PER_RAD;\n }\n\n if (editInfo.angleRad != angleInRad) {\n editInfo.angleRad = angleInRad;\n return true;\n } else {\n return false;\n }\n },\n};\n\n/**\n * @internal\n */\nexport default Rotator;\n\n/**\n * @internal\n * Get HTML for rotate elements, including the rotate handle with icon, and a line between the handle and the image\n */\nexport function getRotateHTML({\n borderColor,\n rotateHandleBackColor,\n}: ImageHtmlOptions): CreateElementData[] {\n const handleLeft = ROTATE_SIZE / 2;\n return [\n {\n tag: 'div',\n className: ImageEditElementClass.RotateCenter,\n style: `position:absolute;left:50%;width:1px;background-color:${borderColor}`,\n children: [\n {\n tag: 'div',\n className: ImageEditElementClass.RotateHandle,\n style: `position:absolute;background-color:${rotateHandleBackColor};border:solid 1px ${borderColor};border-radius:50%;width:${ROTATE_SIZE}px;height:${ROTATE_SIZE}px;left:-${handleLeft}px;cursor:move`,\n children: [getRotateIconHTML(borderColor)],\n },\n ],\n },\n ];\n}\n\nfunction getRotateIconHTML(borderColor: string): CreateElementData {\n return {\n tag: 'svg',\n namespace: 'http://www.w3.org/2000/svg',\n style: `width:16px;height:16px;margin: ${ROTATE_ICON_MARGIN}px ${ROTATE_ICON_MARGIN}px`,\n children: [\n {\n tag: 'path',\n namespace: 'http://www.w3.org/2000/svg',\n attributes: {\n d: 'M 10.5,10.0 A 3.8,3.8 0 1 1 6.7,6.3',\n transform: 'matrix(1.1 1.1 -1.1 1.1 11.6 -10.8)',\n ['fill-opacity']: '0',\n stroke: borderColor,\n },\n },\n {\n tag: 'path',\n namespace: 'http://www.w3.org/2000/svg',\n attributes: {\n d: 'M12.0 3.648l.884-.884.53 2.298-2.298-.53z',\n stroke: borderColor,\n },\n },\n ],\n };\n}\n","import applyChange from '../editInfoUtils/applyChange';\nimport getEditInfoFromImage from '../editInfoUtils/getEditInfoFromImage';\nimport getTargetSizeByPercentage from '../editInfoUtils/getTargetSizeByPercentage';\nimport isResizedTo from './isResizedTo';\nimport { ChangeSource, IEditor } from 'roosterjs-editor-types';\n\n/**\n * Resize the image by percentage of its natural size. If the image is cropped or rotated,\n * the final size will also calculated with crop and rotate info.\n * @param editor The editor that contains the image\n * @param image The image to resize\n * @param percentage Percentage to resize to\n * @param minWidth Minimum width\n * @param minHeight Minimum height\n */\nexport default function resizeByPercentage(\n editor: IEditor,\n image: HTMLImageElement,\n percentage: number,\n minWidth: number,\n minHeight: number\n) {\n const editInfo = getEditInfoFromImage(image);\n\n if (!isResizedTo(image, percentage)) {\n loadImage(image, editInfo.src, () => {\n if (!editor.isDisposed() && editor.contains(image)) {\n const lastSrc = image.src;\n const { width, height } = getTargetSizeByPercentage(editInfo, percentage);\n editInfo.widthPx = Math.max(width, minWidth);\n editInfo.heightPx = Math.max(height, minHeight);\n\n editor.addUndoSnapshot(() => {\n applyChange(editor, image, editInfo, lastSrc);\n }, ChangeSource.ImageResize);\n }\n });\n }\n}\n\nfunction loadImage(img: HTMLImageElement, src: string, callback: () => void) {\n img.onload = () => {\n img.onload = null;\n img.onerror = null;\n callback();\n };\n img.onerror = () => {\n img.onload = null;\n img.onerror = null;\n callback();\n };\n img.src = src;\n}\n","import deleteEditInfo from '../editInfoUtils/deleteEditInfo';\nimport { ChangeSource, IEditor } from 'roosterjs-editor-types';\n\n/**\n * Remove explicit width & height attributes on the image element.\n * @param editor The editor that contains the image\n * @param image The image to remove w/h from\n */\nexport default function resetImage(editor: IEditor, image: HTMLImageElement) {\n editor.addUndoSnapshot(() => {\n image.style.width = '';\n image.style.height = '';\n image.style.maxWidth = '100%';\n image.removeAttribute('width');\n image.removeAttribute('height');\n deleteEditInfo(image);\n }, ChangeSource.ImageResize);\n}\n","import { ImageEdit } from './ImageEdit';\nimport { ImageEditOperation } from 'roosterjs-editor-types';\n\n/**\n * @deprecated Use ImageEdit plugin instead\n */\nexport class ImageResize extends ImageEdit {\n /**\n * Create a new instance of ImageResize\n * @param minWidth Minimum width of image when resize in pixel, default value is 10\n * @param minHeight Minimum height of image when resize in pixel, default value is 10\n * @param selectionBorderColor Color of resize border and handles, default value is #DB626C\n * @param forcePreserveRatio Whether always preserve width/height ratio when resize, default value is false\n * @param resizableImageSelector Selector for picking which image is resizable (e.g. for all images not placeholders), note\n * that the tag must be IMG regardless what the selector is\n */\n constructor(\n minWidth: number = 10,\n minHeight: number = 10,\n selectionBorderColor: string = '#DB626C',\n forcePreserveRatio: boolean = false,\n resizableImageSelector: string = 'img'\n ) {\n super({\n minHeight,\n minWidth,\n borderColor: selectionBorderColor,\n preserveRatio: forcePreserveRatio,\n imageSelector: resizableImageSelector,\n });\n }\n\n /**\n * @deprecated\n */\n showResizeHandle(img: HTMLImageElement) {\n this.setEditingImage(img, ImageEditOperation.Resize);\n }\n\n /**\n * @deprecated\n */\n hideResizeHandle(selectImageAfterUnSelect?: boolean) {\n this.setEditingImage(null /*image*/, selectImageAfterUnSelect);\n }\n}\n","export * from './plugins/Paste/index';\n","export { default as Paste } from './Paste';\n","import convertPasteContentForSingleImage from './imageConverter/convertPasteContentForSingleImage';\nimport convertPastedContentForLI from './commonConverter/convertPastedContentForLI';\nimport convertPastedContentFromExcel from './excelConverter/convertPastedContentFromExcel';\nimport convertPastedContentFromPowerPoint from './pptConverter/convertPastedContentFromPowerPoint';\nimport convertPastedContentFromWord from './wordConverter/convertPastedContentFromWord';\nimport handleLineMerge from './lineMerge/handleLineMerge';\nimport { toArray } from 'roosterjs-editor-dom';\nimport { WAC_IDENTIFY_SELECTOR } from './officeOnlineConverter/constants';\nimport {\n EditorPlugin,\n ExperimentalFeatures,\n IEditor,\n PluginEvent,\n PluginEventType,\n} from 'roosterjs-editor-types';\nimport convertPastedContentFromWordOnline, {\n isWordOnlineWithList,\n} from './officeOnlineConverter/convertPastedContentFromWordOnline';\n\nconst WORD_ATTRIBUTE_NAME = 'xmlns:w';\nconst WORD_ATTRIBUTE_VALUE = 'urn:schemas-microsoft-com:office:word';\nconst EXCEL_ATTRIBUTE_NAME = 'xmlns:x';\nconst EXCEL_ATTRIBUTE_VALUE = 'urn:schemas-microsoft-com:office:excel';\nconst PROG_ID_NAME = 'ProgId';\nconst EXCEL_ONLINE_ATTRIBUTE_VALUE = 'Excel.Sheet';\nconst POWERPOINT_ATTRIBUTE_VALUE = 'PowerPoint.Slide';\nconst GOOGLE_SHEET_NODE_NAME = 'google-sheets-html-origin';\n\n/**\n * Paste plugin, handles BeforePaste event and reformat some special content, including:\n * 1. Content copied from Word\n * 2. Content copied from Excel\n * 3. Content copied from Word Online or OneNote Online\n */\nexport default class Paste implements EditorPlugin {\n private editor: IEditor;\n\n /**\n * Construct a new instance of Paste class\n * @param unknownTagReplacement Replace solution of unknown tags, default behavior is to replace with SPAN\n */\n constructor(private unknownTagReplacement: string = 'SPAN') {}\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'Paste';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n if (event.eventType == PluginEventType.BeforePaste) {\n const { htmlAttributes, fragment, sanitizingOption, clipboardData } = event;\n const trustedHTMLHandler = this.editor.getTrustedHTMLHandler();\n let wacListElements: Node[];\n\n if (htmlAttributes[WORD_ATTRIBUTE_NAME] == WORD_ATTRIBUTE_VALUE) {\n // Handle HTML copied from Word\n convertPastedContentFromWord(event);\n } else if (\n htmlAttributes[EXCEL_ATTRIBUTE_NAME] == EXCEL_ATTRIBUTE_VALUE ||\n htmlAttributes[PROG_ID_NAME] == EXCEL_ONLINE_ATTRIBUTE_VALUE\n ) {\n // Handle HTML copied from Excel\n convertPastedContentFromExcel(event, trustedHTMLHandler);\n } else if (htmlAttributes[PROG_ID_NAME] == POWERPOINT_ATTRIBUTE_VALUE) {\n convertPastedContentFromPowerPoint(event, trustedHTMLHandler);\n } else if (\n (wacListElements = toArray(fragment.querySelectorAll(WAC_IDENTIFY_SELECTOR))) &&\n wacListElements.length > 0\n ) {\n // Once it is known that the document is from WAC\n // We need to remove the display property and margin from all the list item\n wacListElements.forEach((el: HTMLElement) => {\n el.style.display = null;\n el.style.margin = null;\n });\n // call conversion function if the pasted content is from word online and\n // has list element in the pasted content.\n if (isWordOnlineWithList(fragment)) {\n convertPastedContentFromWordOnline(fragment);\n }\n } else if (fragment.querySelector(GOOGLE_SHEET_NODE_NAME)) {\n sanitizingOption.additionalTagReplacements[GOOGLE_SHEET_NODE_NAME] = '*';\n } else if (\n this.editor.isFeatureEnabled(ExperimentalFeatures.ConvertSingleImageBody) &&\n clipboardData.htmlFirstLevelChildTags?.length == 1 &&\n clipboardData.htmlFirstLevelChildTags[0] == 'IMG'\n ) {\n convertPasteContentForSingleImage(event, trustedHTMLHandler);\n } else {\n convertPastedContentForLI(fragment);\n handleLineMerge(fragment);\n }\n\n // Replace unknown tags with SPAN\n sanitizingOption.unknownTagReplacement = this.unknownTagReplacement;\n }\n }\n}\n","import { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types';\nimport { moveChildNodes } from 'roosterjs-editor-dom';\n\n/**\n * @internal\n * Convert pasted content if there are HTML and Image data in the Clipboard\n * @param event The BeforePaste event\n */\nexport default function convertPasteContentForSingleImage(\n event: BeforePasteEvent,\n trustedHTMLHandler: TrustedHTMLHandler\n) {\n const { fragment, clipboardData } = event;\n const { html, image } = clipboardData;\n\n if (html && image) {\n //If there are Html in the clipboard, and the html body only have one img children, use the HTML\n const doc = new DOMParser().parseFromString(trustedHTMLHandler(html), 'text/html');\n moveChildNodes(fragment, doc?.body);\n }\n}\n","import {\n changeElementTag,\n getTagOfNode,\n toArray,\n wrap,\n safeInstanceOf,\n} from 'roosterjs-editor-dom';\n\n/**\n * @internal\n * Convert content copied from Teams to be well-formed\n */\nexport default function convertPastedContentForLI(fragment: DocumentFragment) {\n // Sometimes it is possible that we get LI nodes directly under DIV.\n // In that case we need to convert DIV to UL. It is also possible to be OL, but we don't know it.\n // So always assume it is UL here, and later user can change it.\n if (isPureLiNode(fragment)) {\n wrap(toArray(fragment.childNodes), 'UL');\n } else if (\n safeInstanceOf(fragment.firstChild, 'HTMLElement') &&\n isPureLiNode(fragment.firstChild)\n ) {\n changeElementTag(fragment.firstChild as HTMLElement, 'UL');\n }\n}\n\nfunction isPureLiNode(node: ParentNode & Node) {\n if (node && !node.nextSibling && ['OL', 'UL', 'MENU'].indexOf(getTagOfNode(node)) < 0) {\n let hasLi = false;\n if (\n toArray(node.childNodes).every(childNode => {\n if (safeInstanceOf(childNode, 'Text') && !childNode.nodeValue?.trim()) {\n return true;\n } else if (getTagOfNode(childNode) == 'LI') {\n hasLi = true;\n return true;\n } else {\n return false;\n }\n }) &&\n hasLi\n ) {\n return true;\n }\n }\n return false;\n}\n","import { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types';\nimport { chainSanitizerCallback, moveChildNodes } from 'roosterjs-editor-dom';\n\nconst LAST_TD_END_REGEX = /<\\/\\s*td\\s*>((?!<\\/\\s*tr\\s*>)[\\s\\S])*$/i;\nconst LAST_TR_END_REGEX = /<\\/\\s*tr\\s*>((?!<\\/\\s*table\\s*>)[\\s\\S])*$/i;\nconst LAST_TR_REGEX = /]*>[^<]*/i;\nconst LAST_TABLE_REGEX = /]*>[^<]*/i;\nconst DEFAULT_BORDER_STYLE = 'solid 1px #d4d4d4';\n\n/**\n * @internal\n * Convert pasted content from Excel, add borders when source doc doesn't have a border\n * @param event The BeforePaste event\n */\nexport default function convertPastedContentFromExcel(\n event: BeforePasteEvent,\n trustedHTMLHandler: TrustedHTMLHandler\n) {\n const { fragment, sanitizingOption, htmlBefore, clipboardData } = event;\n const html = excelHandler(clipboardData.html, htmlBefore);\n\n if (clipboardData.html != html) {\n const doc = new DOMParser().parseFromString(trustedHTMLHandler(html), 'text/html');\n moveChildNodes(fragment, doc?.body);\n }\n\n chainSanitizerCallback(sanitizingOption.elementCallbacks, 'TD', element => {\n if (element.style.borderStyle == 'none') {\n element.style.border = DEFAULT_BORDER_STYLE;\n }\n return true;\n });\n}\n\n/**\n * @internal Export for test only\n * @param html Source html\n */\nexport function excelHandler(html: string, htmlBefore: string): string {\n if (html.match(LAST_TD_END_REGEX)) {\n const trMatch = htmlBefore.match(LAST_TR_REGEX);\n const tr = trMatch ? trMatch[0] : '';\n html = tr + html + '';\n }\n if (html.match(LAST_TR_END_REGEX)) {\n let tableMatch = htmlBefore.match(LAST_TABLE_REGEX);\n let table = tableMatch ? tableMatch[0] : '';\n html = table + html + '
';\n }\n\n return html;\n}\n","import { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types';\nimport { moveChildNodes } from 'roosterjs-editor-dom';\n\n/**\n * @internal\n * Convert pasted content from PowerPoint\n * @param event The BeforePaste event\n */\nexport default function convertPastedContentFromPowerPoint(\n event: BeforePasteEvent,\n trustedHTMLHandler: TrustedHTMLHandler\n) {\n const { fragment, clipboardData } = event;\n\n if (clipboardData.html && !clipboardData.text && clipboardData.image) {\n // It is possible that PowerPoint copied both image and HTML but not plain text.\n // We always prefer HTML if any.\n const doc = new DOMParser().parseFromString(\n trustedHTMLHandler(clipboardData.html),\n 'text/html'\n );\n\n moveChildNodes(fragment, doc?.body);\n }\n}\n","import { BeforePasteEvent } from 'roosterjs-editor-types';\nimport { chainSanitizerCallback, moveChildNodes } from 'roosterjs-editor-dom';\nimport { createWordConverter } from './wordConverter';\nimport { createWordConverterArguments } from './WordConverterArguments';\nimport { processNodeConvert, processNodesDiscovery } from './converterUtils';\n\n/**\n * @internal\n * Converts all the Word generated list items in the specified node into standard HTML UL and OL tags\n */\nexport default function convertPastedContentFromWord(event: BeforePasteEvent) {\n const { sanitizingOption, fragment } = event;\n\n // Preserve when its innerHTML is \" \" to avoid dropping an empty line\n chainSanitizerCallback(sanitizingOption.elementCallbacks, 'O:P', element => {\n moveChildNodes(element);\n element.appendChild(element.ownerDocument.createTextNode('\\u00A0')); //  \n return true;\n });\n\n let wordConverter = createWordConverter();\n\n // First find all the nodes that we need to check for list item information\n // This call will return all the p and header elements under the root node.. These are the elements that\n // Word uses a list items, so we'll only process them and avoid walking the whole tree.\n let elements = fragment.querySelectorAll('p');\n if (elements.length > 0) {\n wordConverter.wordConverterArgs = createWordConverterArguments(elements);\n if (processNodesDiscovery(wordConverter)) {\n processNodeConvert(wordConverter);\n }\n }\n}\n","import WordConverterArguments from './WordConverterArguments';\nimport WordCustomData, { createCustomData } from './WordCustomData';\n\n/**\n * @internal\n * Processes HTML generated by Word, converting Word Lists into standard HTML UL and OL tags\n */\nexport default interface WordConverter {\n /** Next unique id to be assigned to a list */\n nextUniqueId: number;\n\n /** Number of bullets converted */\n numBulletsConverted: number;\n\n /** Number of numbering converted */\n numNumberedConverted: number;\n\n /** The structure that records the status of the conversion */\n wordConverterArgs: WordConverterArguments;\n\n /** Custom data storage for list items */\n wordCustomData: WordCustomData;\n}\n\n/**\n * @internal\n * create an empty WordConverter\n */\nexport function createWordConverter(): WordConverter {\n return {\n nextUniqueId: 1,\n numBulletsConverted: 0,\n numNumberedConverted: 0,\n wordConverterArgs: null,\n wordCustomData: createCustomData(),\n };\n}\n","import LevelLists, { createLevelLists } from './LevelLists';\nimport ListItemMetadata from './ListItemMetadata';\nimport ListMetadata from './ListMetadata';\n\n/**\n * @internal\n * Contains the state of the WordConverter when called back after yielding\n */\nexport default interface WordConverterArguments {\n /** The list of element nodes being processed */\n nodes: NodeListOf;\n\n /** The index of the element currently being processed */\n currentIndex: number;\n\n /**\n * Holds the metadata for all the lists we have found\n * key: unique list id, value: list metadata\n */\n lists: { [key: string]: ListMetadata };\n\n /**\n * Stores the list item meta data of the items we\n * have found that need to be converted\n */\n listItems: ListItemMetadata[];\n\n /**\n * This array holds the list id of the lists we are processing\n * that are next to each other.. This list will be used to determine\n * if list items are next to each other or if they are separated...\n * Separated items are ignored from the conversion\n */\n\n currentListIdsByLevels: LevelLists[];\n\n /** Remembers the item that was last processed */\n lastProcessedItem: HTMLElement;\n}\n\n/**\n * @internal\n * create an empty WordConverterArguments\n */\nexport function createWordConverterArguments(\n nodes: NodeListOf\n): WordConverterArguments {\n return {\n nodes: nodes,\n currentIndex: 0,\n lists: {},\n listItems: [],\n currentListIdsByLevels: [createLevelLists()],\n lastProcessedItem: null,\n };\n}\n","import ListItemMetadata from './ListItemMetadata';\nimport ListMetadata from './ListMetadata';\nimport WordConverter from './wordConverter';\nimport WordConverterArguments from './WordConverterArguments';\nimport { createLevelLists } from './LevelLists';\nimport { getObject, setObject } from './WordCustomData';\nimport { getStyles, getTagOfNode, moveChildNodes } from 'roosterjs-editor-dom';\nimport { NodeType } from 'roosterjs-editor-types';\n\n/** Word list metadata style name */\nconst LOOKUP_DEPTH = 5;\n\n/** Name for the word list id property in the custom data */\nconst UNIQUE_LIST_ID_CUSTOM_DATA = 'UniqueListId';\n\n/** Word list metadata style name */\nconst MSO_LIST_STYLE_NAME = 'mso-list';\n\n/** Regular expression to match line breaks */\nconst LINE_BREAKS = /[\\n|\\r]/gi;\n\n/**\n * @internal\n * Handles the pass 1: Discovery\n * During discovery, we'll parse the metadata out of the elements and store it in the list items dictionary.\n * We'll detect cases where the list items for a particular ordered list are not next to each other. Word does these\n * for numbered headers, and we don't want to convert those, because the numbering would be completely wrong.\n */\nexport function processNodesDiscovery(wordConverter: WordConverter): boolean {\n let args = wordConverter.wordConverterArgs;\n while (args.currentIndex < args.nodes.length) {\n let node = args.nodes.item(args.currentIndex);\n\n // Try to get the list metadata for the specified node\n let itemMetadata = getListItemMetadata(node);\n if (itemMetadata) {\n let levelInfo =\n args.currentListIdsByLevels[itemMetadata.level - 1] || createLevelLists();\n args.currentListIdsByLevels[itemMetadata.level - 1] = levelInfo;\n\n // We need to drop some list information if this is not an item next to another\n if (args.lastProcessedItem && getRealPreviousSibling(node) != args.lastProcessedItem) {\n // This list item is not next to the previous one. This means that there is some content in between them\n // so we need to reset our list of list ids per level\n resetCurrentLists(args);\n }\n\n // Get the list metadata for the list that will hold this item\n let listMetadata = levelInfo.listsMetadata[itemMetadata.wordListId];\n if (!listMetadata) {\n // Get the first item fake bullet.. This will be used later to check what is the right type of list\n let firstFakeBullet = getFakeBulletText(node, LOOKUP_DEPTH);\n\n // This is a the first item of a list.. We'll create the list metadata using the information\n // we already have from this first item\n listMetadata = {\n numberOfItems: 0,\n uniqueListId: wordConverter.nextUniqueId++,\n firstFakeBullet: firstFakeBullet,\n\n // If the bullet we got is empty or not found, we ignore the list out.. this means\n // that this is not an item we need to convert of that the format doesn't match what\n // we are expecting\n ignore: !firstFakeBullet || firstFakeBullet.length == 0,\n\n // We'll use the first fake bullet to try to figure out which type of list we create. If this list has a second\n // item, we'll perform a better comparison, but for one item lists, this will be check that will determine the list type\n tagName: getFakeBulletTagName(firstFakeBullet),\n };\n levelInfo.listsMetadata[itemMetadata.wordListId] = listMetadata;\n args.lists[listMetadata.uniqueListId.toString()] = listMetadata;\n } else if (!listMetadata.ignore && listMetadata.numberOfItems == 1) {\n // This is the second item we've seen for this list.. we'll compare the 2 fake bullet\n // items we have an decide if we create ordered or unordered lists based on this.\n // This is the best way we can do this since we cannot read the metadata that Word\n // puts in the head of the HTML...\n let secondFakeBullet = getFakeBulletText(node, LOOKUP_DEPTH);\n listMetadata.tagName =\n listMetadata.firstFakeBullet == secondFakeBullet ? 'UL' : 'OL';\n }\n\n // Set the unique id to the list\n itemMetadata.uniqueListId = listMetadata.uniqueListId;\n\n // Check if we need to ignore this list... we'll either know already that we need to ignore\n // it, or we'll know it because the previous list items are not next to this one\n if (\n listMetadata.ignore ||\n (listMetadata.tagName == 'OL' &&\n listMetadata.numberOfItems > 0 &&\n levelInfo.currentUniqueListId != itemMetadata.uniqueListId)\n ) {\n // We need to ignore this item... and we also need to forget about the lists that\n // are not at the root level\n listMetadata.ignore = true;\n args.currentListIdsByLevels[0].currentUniqueListId = -1;\n args.currentListIdsByLevels = args.currentListIdsByLevels.slice(0, 1);\n } else {\n // This is an item we don't need to ignore... If added lists deep under this one before\n // we'll drop their ids from the list of ids per level.. this is because this list item\n // breaks the deeper lists.\n if (args.currentListIdsByLevels.length > itemMetadata.level) {\n args.currentListIdsByLevels = args.currentListIdsByLevels.slice(\n 0,\n itemMetadata.level\n );\n }\n\n levelInfo.currentUniqueListId = itemMetadata.uniqueListId;\n\n // Add the list item into the list of items to be processed\n args.listItems.push(itemMetadata);\n listMetadata.numberOfItems++;\n }\n\n args.lastProcessedItem = node;\n } else {\n // Here, we know that this is not a list item, but we'll want to check if it is one \"no bullet\" list items...\n // these can be created by creating a bullet and hitting delete on it it... The content will continue to be indented, but there will\n // be no bullet and the list will continue correctly after that. Visually, it looks like the previous item has multiple lines, but\n // the HTML generated has multiple paragraphs with the same class. We'll merge these when we find them, so the logic doesn't skips\n // the list conversion thinking that the list items are not together...\n let last = args.lastProcessedItem;\n if (\n last &&\n getRealPreviousSibling(node) == last &&\n node.tagName == last.tagName &&\n node.className == last.className\n ) {\n // Add 2 line breaks and move all the nodes to the last item\n last.appendChild(last.ownerDocument.createElement('br'));\n last.appendChild(last.ownerDocument.createElement('br'));\n moveChildNodes(last, node, true /*keepExistingChildren*/);\n\n // Remove the item that we don't need anymore\n node.parentNode.removeChild(node);\n }\n }\n\n // Move to the next element are return true if more elements need to be processed\n args.currentIndex++;\n }\n\n return args.listItems.length > 0;\n}\n\n/**\n * @internal\n * Handles the pass 2: Conversion\n * During conversion, we'll go over the elements that belong to a list that we've marked as a list to convert, and we'll perform the\n * conversion needed\n */\nexport function processNodeConvert(wordConverter: WordConverter): boolean {\n let args = wordConverter.wordConverterArgs;\n args.currentIndex = 0;\n\n while (args.currentIndex < args.listItems.length) {\n let metadata = args.listItems[args.currentIndex];\n let node = metadata.originalNode;\n let listMetadata = args.lists[metadata.uniqueListId.toString()];\n if (!listMetadata.ignore) {\n // We have a list item that we need to convert, get or create the list\n // that hold this item out\n let list = getOrCreateListForNode(wordConverter, node, metadata, listMetadata);\n if (list) {\n // Clean the element out.. this call gets rid of the fake bullet and unneeded nodes\n cleanupListIgnore(node, LOOKUP_DEPTH);\n\n // Create a new list item and transfer the children\n let li = node.ownerDocument.createElement('LI');\n moveChildNodes(li, node);\n\n // Append the list item into the list\n list.appendChild(li);\n\n // Remove the node we just converted\n node.parentNode.removeChild(node);\n\n if (listMetadata.tagName == 'UL') {\n wordConverter.numBulletsConverted++;\n } else {\n wordConverter.numNumberedConverted++;\n }\n }\n }\n\n args.currentIndex++;\n }\n\n return wordConverter.numBulletsConverted > 0 || wordConverter.numNumberedConverted > 0;\n}\n\n/**\n * Gets or creates the list (UL or OL) that holds this item out based on the\n * items content and the specified metadata\n */\nfunction getOrCreateListForNode(\n wordConverter: WordConverter,\n node: HTMLElement,\n metadata: ListItemMetadata,\n listMetadata: ListMetadata\n): Node {\n // First get the last list next to this node under the specified level. This code\n // path will return the list or will create lists if needed\n let list = recurringGetOrCreateListAtNode(node, metadata.level, listMetadata);\n\n // Here use the unique list ID to detect if we have the right list...\n // it is possible to have 2 different lists next to each other with different formats, so\n // we want to detect this an create separate lists for those cases\n let listId = getObject(wordConverter.wordCustomData, list, UNIQUE_LIST_ID_CUSTOM_DATA);\n\n // If we have a list with and ID, but the ID is different than the ID for this list item, this\n // is a completely new list, so we'll append a new list for that\n if ((listId && listId != metadata.uniqueListId) || (!listId && list.firstChild)) {\n let newList = node.ownerDocument.createElement(listMetadata.tagName);\n list.parentNode.insertBefore(newList, list.nextSibling);\n list = newList;\n }\n\n // Set the list id into the custom data\n setObject(\n wordConverter.wordCustomData,\n list,\n UNIQUE_LIST_ID_CUSTOM_DATA,\n metadata.uniqueListId\n );\n\n // This call will convert the list if needed to the right type of list required. This can happen\n // on the cases where the first list item for this list is located after a deeper list. for that\n // case, we will have created a UL for it, and we may need to convert it\n return convertListIfNeeded(wordConverter, list, listMetadata);\n}\n\n/**\n * Converts the list between UL and OL if needed, by using the fake bullet and\n * information already stored in the list itself\n */\nfunction convertListIfNeeded(\n wordConverter: WordConverter,\n list: Node,\n listMetadata: ListMetadata\n): Node {\n // Check if we need to convert the list out\n if (listMetadata.tagName != getTagOfNode(list)) {\n // We have the wrong list type.. convert it, set the id again and transfer all the children\n let newList = list.ownerDocument.createElement(listMetadata.tagName);\n setObject(\n wordConverter.wordCustomData,\n newList,\n UNIQUE_LIST_ID_CUSTOM_DATA,\n getObject(wordConverter.wordCustomData, list, UNIQUE_LIST_ID_CUSTOM_DATA)\n );\n moveChildNodes(newList, list);\n list.parentNode.insertBefore(newList, list);\n list.parentNode.removeChild(list);\n list = newList;\n }\n\n return list;\n}\n\n/**\n * Gets or creates the specified list\n */\nfunction recurringGetOrCreateListAtNode(\n node: HTMLElement,\n level: number,\n listMetadata: ListMetadata\n): Node {\n let parent: Node = null;\n let possibleList: Node;\n if (level == 1) {\n // Root case, we'll check if the list is the previous sibling of the node\n possibleList = getRealPreviousSibling(node);\n } else {\n // If we get here, we are looking for level 2 or deeper... get the upper list\n // and check if the last element is a list\n parent = recurringGetOrCreateListAtNode(node, level - 1, null);\n possibleList = parent.lastChild;\n }\n\n // Check the element that we got and verify that it is a list\n if (possibleList && possibleList.nodeType == NodeType.Element) {\n let tag = getTagOfNode(possibleList);\n if (tag == 'UL' || tag == 'OL') {\n // We have a list.. use it\n return possibleList;\n }\n }\n\n // If we get here, it means we don't have a list and we need to create one\n // this code path will always create new lists as UL lists\n let newList = node.ownerDocument.createElement(listMetadata ? listMetadata.tagName : 'UL');\n if (level == 1) {\n // For level 1, we'll insert the list before the node\n node.parentNode.insertBefore(newList, node);\n } else {\n // Any level 2 or above, we insert the list as the last\n // child of the upper level list\n parent.appendChild(newList);\n }\n\n return newList;\n}\n\n/**\n * Cleans up the node children by removing the children marked as mso-list: Ignore.\n * This nodes hold the fake bullet information that Word puts in and when\n * conversion is happening, we want to get rid of these elements\n */\nfunction cleanupListIgnore(node: Node, levels: number) {\n let nodesToRemove: Node[] = [];\n\n for (let child: Node = node.firstChild; child; child = child.nextSibling) {\n // Clean up the item internally first if we need to based on the number of levels\n if (child.nodeType == NodeType.Element && levels > 1) {\n cleanupListIgnore(child, levels - 1);\n }\n\n // Try to convert word comments into ignore elements if we haven't done so for this element\n child = fixWordListComments(child, true /*removeComments*/);\n\n // Check if we can remove this item out\n if (isEmptySpan(child) || isIgnoreNode(child)) {\n nodesToRemove.push(child);\n }\n }\n\n nodesToRemove.forEach(child => node.removeChild(child));\n}\n\n/**\n * Reads the word list meta dada out of the specified node. If the node\n * is not a Word list item, it returns null.\n */\nfunction getListItemMetadata(node: HTMLElement): ListItemMetadata {\n if (node.nodeType == NodeType.Element) {\n let listAttribute = getStyleValue(node, MSO_LIST_STYLE_NAME);\n if (listAttribute && listAttribute.length > 0) {\n try {\n // Word mso-list property holds 3 space separated values in the following format: lst1 level1 lfo0\n // Where:\n // (0) List identified for the metadata in the <head> of the document. We cannot read the <head> meta data\n // (1) Level of the list. This also maps to the <head> metadata that we cannot read, but\n // for almost all cases, it maps to the list indentation (or level). We'll use it as the\n // list indentation value\n // (2) Contains a specific list identifier.\n // Example value: \"l0 level1 lfo1\"\n let listProps = listAttribute.split(' ');\n if (listProps.length == 3) {\n return {\n level: parseInt(listProps[1].substr('level'.length)),\n wordListId: listAttribute,\n originalNode: node,\n uniqueListId: 0,\n };\n }\n } catch (e) {}\n }\n }\n return null;\n}\n\nfunction isFakeBullet(fakeBullet: string): boolean {\n return ['o', '·', '§', '-'].indexOf(fakeBullet) >= 0;\n}\n\n/** Given a fake bullet text, returns the type of list that should be used for it */\nfunction getFakeBulletTagName(fakeBullet: string): string {\n return isFakeBullet(fakeBullet) ? 'UL' : 'OL';\n}\n\n/**\n * Finds the fake bullet text out of the specified node and returns it. For images, it will return\n * a bullet string. If not found, it returns null...\n */\nfunction getFakeBulletText(node: Node, levels: number): string {\n // Word uses the following format for their bullets:\n // <p style=\"mso-list:l1 level1 lfo2\">\n // <span style=\"...\">\n // <span style=\"mso-list:Ignore\">1.<span style=\"...\">      </span></span>\n // </span>\n // Content here...\n // </p>\n //\n // Basically, we need to locate the mso-list:Ignore SPAN, which holds either one text or image node. That\n // text or image node will be the fake bullet we are looking for\n let result: string = null;\n let child: Node = node.firstChild;\n while (!result && child) {\n // First, check if we need to convert the Word list comments into real elements\n child = fixWordListComments(child, true /*removeComments*/);\n\n // Check if this is the node that holds the fake bullets (mso-list: Ignore)\n if (isIgnoreNode(child)) {\n // Yes... this is the node that holds either the text or image data\n result = child.textContent.trim();\n\n // This is the case for image case\n if (result.length == 0) {\n result = 'o';\n }\n } else if (child.nodeType == NodeType.Element && levels > 1) {\n // If this is an element and we are not in the last level, try to get the fake bullet\n // out of the child\n result = getFakeBulletText(child, levels - 1);\n }\n\n child = child.nextSibling;\n }\n\n return result;\n}\n\n/**\n * If the specified element is a Word List comments, this code verifies and fixes\n * the markup when needed to ensure that Chrome bullet conversions work as expected\n * -----\n * We'll convert <!--[if !supportLists]--> and <!--[endif]--> comments into\n * <span style=\"mso-list:Ignore\"></span>... Chrome has a bug where it drops the\n * styles of the span, but we'll use these comments to recreate them out\n */\nfunction fixWordListComments(child: Node, removeComments: boolean): Node {\n if (child.nodeType == NodeType.Comment) {\n let value = (child as Comment).data;\n if (value && value.trim().toLowerCase() == '[if !supportlists]') {\n // We have a list ignore start, find the end.. We know is not more than\n // 3 nodes away, so we'll optimize our checks\n let nextElement = child;\n let endComment: Node = null;\n for (let j = 0; j < 4; j++) {\n nextElement = getRealNextSibling(nextElement);\n if (!nextElement) {\n break;\n }\n if (nextElement.nodeType == NodeType.Comment) {\n value = (nextElement as Comment).data;\n if (value && value.trim().toLowerCase() == '[endif]') {\n endComment = nextElement;\n break;\n }\n }\n }\n\n // if we found the end node, wrap everything out\n if (endComment) {\n let newSpan = child.ownerDocument.createElement('span');\n newSpan.setAttribute('style', 'mso-list: ignore');\n nextElement = getRealNextSibling(child);\n while (nextElement != endComment) {\n nextElement = nextElement.nextSibling as HTMLElement;\n newSpan.appendChild(nextElement.previousSibling);\n }\n\n // Insert the element out and use that one as the current child\n endComment.parentNode.insertBefore(newSpan, endComment);\n\n // Remove the comments out if the call specified it out\n if (removeComments) {\n child.parentNode.removeChild(child);\n endComment.parentNode.removeChild(endComment);\n }\n\n // Last, make sure we return the new element out instead of the comment\n child = newSpan;\n }\n }\n }\n\n return child;\n}\n\n/** Finds the real previous sibling, ignoring empty text nodes */\nfunction getRealPreviousSibling(node: Node): Node {\n let prevSibling = node;\n do {\n prevSibling = prevSibling.previousSibling;\n } while (prevSibling && isEmptyTextNode(prevSibling));\n return prevSibling;\n}\n\n/** Finds the real next sibling, ignoring empty text nodes */\nfunction getRealNextSibling(node: Node): Node {\n let nextSibling = node;\n do {\n nextSibling = nextSibling.nextSibling;\n } while (nextSibling && isEmptyTextNode(nextSibling));\n\n return nextSibling;\n}\n\n/**\n * Checks if the specified node is marked as a mso-list: Ignore. These\n * nodes need to be ignored when a list item is converted into standard\n * HTML lists\n */\nfunction isIgnoreNode(node: Node): boolean {\n if (node.nodeType == NodeType.Element) {\n let listAttribute = getStyleValue(node as HTMLElement, MSO_LIST_STYLE_NAME);\n if (\n listAttribute &&\n listAttribute.length > 0 &&\n listAttribute.trim().toLowerCase() == 'ignore'\n ) {\n return true;\n }\n }\n\n return false;\n}\n\n/** Checks if the specified node is an empty span. */\nfunction isEmptySpan(node: Node): boolean {\n return getTagOfNode(node) == 'SPAN' && !node.firstChild;\n}\n\n/** Reads the specified style value from the node */\nfunction getStyleValue(node: HTMLElement, styleName: string): string {\n // Word uses non-standard names for the metadata that puts in the style of the element...\n // Most browsers will not provide the information for those nonstandard values through the node.style\n // property, so the only reliable way to read them is to get the attribute directly and do\n // the required parsing..\n return getStyles(node)[styleName] || null;\n}\n\n/** Checks if the node is an empty text node that can be ignored */\nfunction isEmptyTextNode(node: Node): boolean {\n // No node is empty\n if (!node) {\n return true;\n }\n\n // Empty text node is empty\n if (node.nodeType == NodeType.Text) {\n let value = node.nodeValue;\n value = value.replace(LINE_BREAKS, '');\n return value.trim().length == 0;\n }\n\n // Span or Font with an empty child node is empty\n let tagName = getTagOfNode(node);\n if (node.firstChild == node.lastChild && (tagName == 'SPAN' || tagName == 'FONT')) {\n return isEmptyTextNode(node.firstChild);\n }\n\n // If not found, then this is not empty\n return false;\n}\n\n/** Resets the list */\nfunction resetCurrentLists(args: WordConverterArguments) {\n for (let i = 0; i < args.currentListIdsByLevels.length; i++) {\n let ll = args.currentListIdsByLevels[i];\n if (ll) {\n ll.currentUniqueListId = -1;\n }\n }\n}\n","import {\n changeElementTag,\n ContentTraverser,\n getBlockElementAtNode,\n getNextLeafSibling,\n getPreviousLeafSibling,\n getTagOfNode,\n} from 'roosterjs-editor-dom';\n\n/**\n * Process pasted content, if there are multiple blocks that are not wrapped by a shared ancestor node,\n * change the tag of first and last node to be SPAN so that it will be merged into current block\n * @param root Root node of content to process\n */\nexport default function handleLineMerge(root: Node) {\n const traverser = ContentTraverser.createBodyTraverser(root);\n const blocks: { start: Node; end: Node }[] = [];\n\n for (\n let block = traverser?.currentBlockElement;\n block;\n block = traverser.getNextBlockElement()\n ) {\n blocks.push({\n start: block.getStartNode(),\n end: block.getEndNode(),\n });\n }\n\n if (blocks.length > 0) {\n processBlock(blocks[0]);\n processBlock(blocks[blocks.length - 1]);\n checkAndAddBr(root, blocks[0], true /*isFirst*/);\n checkAndAddBr(root, blocks[blocks.length - 1], false /*isFirst*/);\n }\n}\n\nfunction processBlock(block: { start: Node; end: Node }) {\n const { start, end } = block;\n\n if (start == end && getTagOfNode(start) == 'DIV') {\n const node = changeElementTag(start as HTMLElement, 'SPAN');\n block.start = node;\n block.end = node;\n\n if (getTagOfNode(node.lastChild) == 'BR') {\n node.removeChild(node.lastChild);\n }\n } else if (getTagOfNode(end) == 'BR') {\n const node = end.ownerDocument.createTextNode('');\n end.parentNode?.insertBefore(node, end);\n block.end = node;\n end.parentNode?.removeChild(end);\n }\n}\n\nfunction checkAndAddBr(root: Node, block: { start: Node; end: Node }, isFirst: boolean) {\n const blockElement = getBlockElementAtNode(root, block.start);\n const sibling = isFirst\n ? getNextLeafSibling(root, block.end)\n : getPreviousLeafSibling(root, block.start);\n\n if (blockElement?.contains(sibling)) {\n (isFirst ? block.end : block.start).parentNode?.insertBefore(\n block.start.ownerDocument.createElement('br'),\n isFirst ? block.end.nextSibling : block.start\n );\n }\n}\n","import ListItemBlock, { createListItemBlock } from './ListItemBlock';\nimport {\n WORD_ORDERED_LIST_SELECTOR,\n WORD_UNORDERED_LIST_SELECTOR,\n WORD_ONLINE_IDENTIFYING_SELECTOR,\n LIST_CONTAINER_ELEMENT_CLASS_NAME,\n ORDERED_LIST_TAG_NAME,\n UNORDERED_LIST_TAG_NAME,\n} from './constants';\n\nimport {\n splitParentNode,\n getNextLeafSibling,\n getFirstLeafNode,\n getTagOfNode,\n collapseNodes,\n unwrap,\n toArray,\n} from 'roosterjs-editor-dom';\n\n/**\n * @internal\n */\nexport function isWordOnlineWithList(fragment: DocumentFragment): boolean {\n return !!(fragment && fragment.querySelector(WORD_ONLINE_IDENTIFYING_SELECTOR));\n}\n\n// Word Online pasted content DOM structure as of July 12th 2019\n//\n// \n//
----------> this layer may exist depend on the content user paste\n//
----------> text content\n//

\n//
\n//
----------> list items: for unordered list, all the items on the same level is under the same wrapper\n//
    list items in the same list can be divided into different ListItemWrapper\n//
  • list items in the same list can also be divided into different Outline Group;\n//
  • \n//
\n//
\n//
\n//
\n//
----------> list items: for ordered list, each items has it's own wrapper\n//
    \n//
  1. \n//
\n//
\n//
\n//
    \n//
  1. \n//
\n//
\n//
\n// \n//\n//\n\n/**\n * @internal\n * Convert text copied from word online into text that's workable with rooster editor\n * @param fragment Document fragment that is being pasted into editor.\n */\nexport default function convertPastedContentFromWordOnline(fragment: DocumentFragment) {\n sanitizeListItemContainer(fragment);\n const listItemBlocks: ListItemBlock[] = getListItemBlocks(fragment);\n\n listItemBlocks.forEach(itemBlock => {\n // There are cases where consecutive List Elements are separated into different nodes:\n //
\n //
\n //
    \n //
    \n //
    \n //
      \n //
      \n //
      \n //
      \n //
      \n //
        \n //
        \n //
        \n // in the above case we want to collapse the two root level div into one and unwrap the list item nodes.\n // after the following flattening the list will become following:\n //\n //
        \n //
          \n //
          \n //
          \n //
            \n //
            \n //
            \n //
              \n //
              \n // Then we are start processing.\n flattenListBlock(fragment, itemBlock);\n\n // Find the node to insertBefore, which is next sibling node of the end of a listItemBlock.\n itemBlock.insertPositionNode = itemBlock.endElement.nextSibling;\n\n let convertedListElement: Element;\n const doc = fragment.ownerDocument;\n\n itemBlock.listItemContainers.forEach(listItemContainer => {\n let listType: 'OL' | 'UL' = getContainerListType(listItemContainer); // list type that is contained by iterator.\n // Initialize processed element with proper listType if this is the first element\n if (!convertedListElement) {\n convertedListElement = doc.createElement(listType);\n }\n\n // Get all list items(
            1. ) in the current iterator element.\n const currentListItems = toArray(listItemContainer.querySelectorAll('li'));\n currentListItems.forEach(item => {\n // If item is in root level and the type of list changes then\n // insert the current list into body and then reinitialize the convertedListElement\n // Word Online is using data-aria-level to determine the the depth of the list item.\n const itemLevel = parseInt(item.getAttribute('data-aria-level'));\n // In first level list, there are cases where a consecutive list item DIV may have different list type\n // When that happens we need to insert the processed elements into the document, then change the list type\n // and keep the processing going.\n if (getTagOfNode(convertedListElement) != listType && itemLevel == 1) {\n insertConvertedListToDoc(convertedListElement, fragment, itemBlock);\n convertedListElement = doc.createElement(listType);\n }\n insertListItem(convertedListElement, item, listType, doc);\n });\n });\n\n insertConvertedListToDoc(convertedListElement, fragment, itemBlock);\n\n // Once we finish the process the list items and put them into a list.\n // After inserting the processed element,\n // we need to remove all the non processed node from the parent node.\n const parentContainer = itemBlock.startElement.parentNode;\n if (parentContainer) {\n itemBlock.listItemContainers.forEach(listItemContainer => {\n parentContainer.removeChild(listItemContainer);\n });\n }\n });\n}\n\n/**\n * The node processing is based on the premise of only ol/ul is in ListContainerWrapper class\n * However the html might be malformed, this function is to split all the other elements out of ListContainerWrapper\n * @param fragment pasted document that contains all the list element.\n */\nfunction sanitizeListItemContainer(fragment: DocumentFragment) {\n const listItemContainerListEl = toArray(\n fragment.querySelectorAll(`${WORD_ORDERED_LIST_SELECTOR}, ${WORD_UNORDERED_LIST_SELECTOR}`)\n );\n listItemContainerListEl.forEach(el => {\n const replaceRegex = new RegExp(`\\\\b${LIST_CONTAINER_ELEMENT_CLASS_NAME}\\\\b`, 'g');\n if (el.previousSibling) {\n const prevParent = splitParentNode(el, true) as HTMLElement;\n prevParent.className = prevParent.className.replace(replaceRegex, '');\n }\n if (el.nextSibling) {\n const nextParent = splitParentNode(el, false) as HTMLElement;\n nextParent.className = nextParent.className.replace(replaceRegex, '');\n }\n });\n}\n\n/**\n * Take all the list items in the document, and group the consecutive list times in a list block;\n * @param fragment pasted document that contains all the list element.\n */\nfunction getListItemBlocks(fragment: DocumentFragment): ListItemBlock[] {\n const listElements = fragment.querySelectorAll('.' + LIST_CONTAINER_ELEMENT_CLASS_NAME);\n const result: ListItemBlock[] = [];\n let curListItemBlock: ListItemBlock;\n for (let i = 0; i < listElements.length; i++) {\n let curItem = listElements[i];\n if (!curListItemBlock) {\n curListItemBlock = createListItemBlock(curItem);\n } else {\n const { listItemContainers } = curListItemBlock;\n const lastItemInCurBlock = listItemContainers[listItemContainers.length - 1];\n if (\n curItem == lastItemInCurBlock.nextSibling ||\n getFirstLeafNode(curItem) ==\n getNextLeafSibling(lastItemInCurBlock.parentNode, lastItemInCurBlock)\n ) {\n listItemContainers.push(curItem);\n curListItemBlock.endElement = curItem;\n } else {\n curListItemBlock.endElement = lastItemInCurBlock;\n result.push(curListItemBlock);\n curListItemBlock = createListItemBlock(curItem);\n }\n }\n }\n\n if (curListItemBlock?.listItemContainers.length > 0) {\n result.push(curListItemBlock);\n }\n\n return result;\n}\n\n/**\n * Flatten the list items, so that all the consecutive list items are under the same parent.\n * @param fragment Root element of that contains the element.\n * @param listItemBlock The list item block needed to be flattened.\n */\nfunction flattenListBlock(fragment: DocumentFragment, listItemBlock: ListItemBlock) {\n const collapsedListItemSections = collapseNodes(\n fragment,\n listItemBlock.startElement,\n listItemBlock.endElement,\n true\n );\n collapsedListItemSections.forEach(section => {\n if (getTagOfNode(section.firstChild) == 'DIV') {\n unwrap(section);\n }\n });\n}\n\n/**\n * Get the list type that the container contains. If there is no list in the container\n * return null;\n * @param listItemContainer Container that contains a list\n */\nfunction getContainerListType(listItemContainer: Element): 'OL' | 'UL' | null {\n const tag = getTagOfNode(listItemContainer.firstChild);\n return tag == UNORDERED_LIST_TAG_NAME || tag == ORDERED_LIST_TAG_NAME ? tag : null;\n}\n\n/**\n * Insert list item into the correct position of a list\n * @param listRootElement Root element of the list that is accepting a coming element.\n * @param itemToInsert List item that needed to be inserted.\n * @param listType Type of list(ul/ol)\n */\nfunction insertListItem(\n listRootElement: Element,\n itemToInsert: HTMLElement,\n listType: string,\n doc: HTMLDocument\n): void {\n if (!listType) {\n return;\n }\n // Get item level from 'data-aria-level' attribute\n let itemLevel = parseInt(itemToInsert.getAttribute('data-aria-level'));\n let curListLevel = listRootElement; // Level iterator to find the correct place for the current element.\n // if the itemLevel is 1 it means the level iterator is at the correct place.\n while (itemLevel > 1) {\n if (!curListLevel.firstChild) {\n // If the current level is empty, create empty list within the current level\n // then move the level iterator into the next level.\n curListLevel.appendChild(doc.createElement(listType));\n curListLevel = curListLevel.firstElementChild;\n } else {\n // If the current level is not empty, the last item in the needs to be a UL or OL\n // and the level iterator should move to the UL/OL at the last position.\n let lastChild = curListLevel.lastElementChild;\n let lastChildTag = getTagOfNode(lastChild);\n if (lastChildTag == UNORDERED_LIST_TAG_NAME || lastChildTag == ORDERED_LIST_TAG_NAME) {\n // If the last child is a list(UL/OL), then move the level iterator to last child.\n curListLevel = lastChild;\n } else {\n // If the last child is not a list, then append a new list to the level\n // and move the level iterator to the new level.\n curListLevel.appendChild(doc.createElement(listType));\n curListLevel = curListLevel.lastElementChild;\n }\n }\n itemLevel--;\n }\n\n // Once the level iterator is at the right place, then append the list item in the level.\n curListLevel.appendChild(itemToInsert);\n}\n\n/**\n * Insert the converted list item into the correct place.\n * @param convertedListElement List element that is converted from list item block\n * @param fragment Root element of that contains the converted listItemBlock\n * @param listItemBlock List item block that was converted.\n */\nfunction insertConvertedListToDoc(\n convertedListElement: Element,\n fragment: DocumentFragment,\n listItemBlock: ListItemBlock\n) {\n if (!convertedListElement) {\n return;\n }\n\n const { insertPositionNode } = listItemBlock;\n if (insertPositionNode) {\n const parentNode = insertPositionNode.parentNode;\n if (parentNode) {\n parentNode.insertBefore(convertedListElement, insertPositionNode);\n }\n } else {\n const parentNode = listItemBlock.startElement.parentNode;\n if (parentNode) {\n parentNode.appendChild(convertedListElement);\n } else {\n fragment.appendChild(convertedListElement);\n }\n }\n}\n","/**\n * @internal\n * Type that holds all the info of a consecutive list item block.\n */\nexport default interface ListItemBlock {\n /**\n * The first element in block of list item from pasted word online document.\n */\n startElement: Element;\n\n /**\n * The last element in block of list item from pasted word online document.\n */\n endElement: Element;\n\n /**\n * The position where the processed bulleted list should be inserted.\n */\n insertPositionNode: Node;\n\n /**\n * The list of containers that wraps each list item.\n */\n listItemContainers: Element[];\n}\n\n/**\n * @internal\n * Initialize an empty ListItemBlock\n */\nexport function createListItemBlock(listItem: Element = null): ListItemBlock {\n return {\n startElement: listItem,\n endElement: listItem,\n insertPositionNode: null,\n listItemContainers: listItem ? [listItem] : [],\n };\n}\n","export * from './plugins/Picker/index';\n","export { default as PickerPlugin } from './PickerPlugin';\n","import { replaceWithNode } from 'roosterjs-editor-api';\nimport {\n Browser,\n createRange,\n isCharacterValue,\n isModifierKey,\n PartialInlineElement,\n} from 'roosterjs-editor-dom';\nimport {\n ChangeSource,\n EditorPlugin,\n IEditor,\n NodePosition,\n PickerDataProvider,\n PickerPluginOptions,\n PluginDomEvent,\n PluginEvent,\n PluginEventType,\n PluginInputEvent,\n PluginKeyboardEvent,\n PositionType,\n} from 'roosterjs-editor-types';\n\n// Character codes.\n// IE11 uses different character codes. which are noted below.\n// If adding a new key, test in IE to figure out what the code is.\nconst BACKSPACE_CHAR_CODE = 'Backspace';\nconst TAB_CHAR_CODE = 'Tab';\nconst ENTER_CHAR_CODE = 'Enter';\nconst ESC_CHAR_CODE = !Browser.isIE ? 'Escape' : 'Esc';\nconst LEFT_ARROW_CHAR_CODE = !Browser.isIE ? 'ArrowLeft' : 'Left';\nconst UP_ARROW_CHAR_CODE = !Browser.isIE ? 'ArrowUp' : 'Up';\nconst RIGHT_ARROW_CHAR_CODE = !Browser.isIE ? 'ArrowRight' : 'Right';\nconst DOWN_ARROW_CHAR_CODE = !Browser.isIE ? 'ArrowDown' : 'Down';\nconst DELETE_CHAR_CODE = !Browser.isIE ? 'Delete' : 'Del';\n\n// Input event input types.\nconst DELETE_CONTENT_BACKWARDS_INPUT_TYPE = 'deleteContentBackwards';\n\n// Unidentified key, the code for Android keyboard events.\nconst UNIDENTIFIED_KEY = 'Unidentified';\n// the char code for Android keyboard events on Webview below 51.\nconst UNIDENTIFIED_CODE = [0, 229];\n\n/**\n * PickerPlugin represents a plugin of editor which can handle picker related behaviors, including\n * - Show picker when special trigger key is pressed\n * - Hide picker\n * - Change selection in picker by Up/Down/Left/Right\n * - Apply selected item in picker\n *\n * PickerPlugin doesn't provide any UI, it just wraps related DOM events and invoke callback functions.\n * To show a picker UI, you need to build your own UI component. Please reference to\n * https://github.com/microsoft/roosterjs/tree/master/demo/scripts/controls/samplepicker\n */\nexport default class PickerPlugin\n implements EditorPlugin {\n private editor: IEditor;\n private eventHandledOnKeyDown: boolean;\n private blockSuggestions: boolean;\n private isSuggesting: boolean;\n private lastKnownRange: Range;\n\n // For detecting backspace in Android\n private isPendingInputEventHandling: boolean = false;\n private currentInputLength: number;\n private newInputLength: number;\n\n constructor(public readonly dataProvider: T, private pickerOptions: PickerPluginOptions) {}\n\n /**\n * Get a friendly name\n */\n getName() {\n return 'Picker';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n public initialize(editor: IEditor) {\n this.editor = editor;\n this.dataProvider.onInitalize(\n (htmlNode: Node) => {\n this.editor.focus();\n\n let wordToReplace = this.getWord(null);\n\n // Safari drops our focus out so we get an empty word to replace when we call getWord.\n // We fall back to using the lastKnownRange to try to get around this.\n if ((!wordToReplace || wordToReplace.length == 0) && this.lastKnownRange) {\n this.editor.select(this.lastKnownRange);\n wordToReplace = this.getWord(null);\n }\n\n let insertNode = () => {\n if (wordToReplace) {\n replaceWithNode(\n this.editor,\n wordToReplace,\n htmlNode,\n true /* exactMatch */\n );\n } else {\n this.editor.insertNode(htmlNode);\n }\n this.setIsSuggesting(false);\n };\n\n this.editor.addUndoSnapshot(\n insertNode,\n this.pickerOptions.changeSource,\n this.pickerOptions.handleAutoComplete\n );\n },\n (isSuggesting: boolean) => {\n this.setIsSuggesting(isSuggesting);\n },\n editor\n );\n }\n\n /**\n * Dispose this plugin\n */\n public dispose() {\n this.editor = null;\n this.dataProvider.onDispose();\n }\n\n /**\n * Check if the plugin should handle the given event exclusively.\n * Handle an event exclusively means other plugin will not receive this event in\n * onPluginEvent method.\n * If two plugins will return true in willHandleEventExclusively() for the same event,\n * the final result depends on the order of the plugins are added into editor\n * @param event The event to check\n */\n public willHandleEventExclusively(event: PluginEvent) {\n return (\n this.isSuggesting &&\n (event.eventType == PluginEventType.KeyDown ||\n event.eventType == PluginEventType.KeyUp ||\n event.eventType == PluginEventType.Input)\n );\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n public onPluginEvent(event: PluginEvent) {\n switch (event.eventType) {\n case PluginEventType.ContentChanged:\n if (event.source == ChangeSource.SetContent && this.dataProvider.onContentChanged) {\n // Stop suggesting since content is fully changed\n if (this.isSuggesting) {\n this.setIsSuggesting(false);\n }\n\n // Undo and other major changes to document content fire this type of event.\n // Inform the data provider of the current picker placed elements in the body.\n let elementIds: string[] = [];\n this.editor.queryElements(\n \"[id^='\" + this.pickerOptions.elementIdPrefix + \"']\",\n element => {\n if (element.id) {\n elementIds.push(element.id);\n }\n }\n );\n this.dataProvider.onContentChanged(elementIds);\n }\n break;\n\n case PluginEventType.KeyDown:\n this.eventHandledOnKeyDown = false;\n if (this.isAndroidKeyboardEvent(event)) {\n // On Android, the key for KeyboardEvent is \"Unidentified\" or undefined,\n // so handling should be done using the input rather than key down event\n // Since the key down event happens right before the input event, calculate the input\n // length here in preparation for onAndroidInputEvent\n this.currentInputLength = this.calcInputLength(event);\n this.isPendingInputEventHandling = true;\n } else {\n this.onKeyDownEvent(event);\n this.isPendingInputEventHandling = false;\n }\n break;\n\n case PluginEventType.Input:\n if (this.isPendingInputEventHandling) {\n this.onAndroidInputEvent(event);\n }\n break;\n\n case PluginEventType.KeyUp:\n if (!this.eventHandledOnKeyDown && this.shouldHandleKeyUpEvent(event)) {\n this.onKeyUpDomEvent(event);\n this.isPendingInputEventHandling = false;\n }\n break;\n\n case PluginEventType.MouseUp:\n if (this.isSuggesting) {\n this.setIsSuggesting(false);\n }\n break;\n\n case PluginEventType.Scroll:\n if (this.dataProvider.onScroll) {\n // Dispatch scroll event to data provider\n this.dataProvider.onScroll(event.scrollContainer);\n }\n break;\n }\n }\n\n private setLastKnownRange(range: Range) {\n this.lastKnownRange = range;\n }\n\n private setIsSuggesting(isSuggesting: boolean) {\n this.isSuggesting = isSuggesting;\n\n if (!isSuggesting) {\n this.setLastKnownRange(null);\n }\n this.dataProvider.onIsSuggestingChanged(isSuggesting);\n\n this.setAriaOwns(isSuggesting);\n this.setAriaActiveDescendant(isSuggesting ? 0 : null);\n }\n\n private cancelDefaultKeyDownEvent(event: PluginKeyboardEvent) {\n this.eventHandledOnKeyDown = true;\n event.rawEvent.preventDefault();\n event.rawEvent.stopImmediatePropagation();\n }\n\n private getIdValue(node: Node): string {\n let element = node as Element;\n return element.attributes && element.attributes.getNamedItem('id')\n ? (element.attributes.getNamedItem('id').value as string)\n : null;\n }\n\n private getWordBeforeCursor(event: PluginKeyboardEvent): string {\n let searcher = this.editor.getContentSearcherOfCursor(event);\n return searcher ? searcher.getWordBefore() : null;\n }\n\n private replaceNode(currentNode: Node, replacementNode: Node) {\n if (currentNode) {\n this.editor.deleteNode(currentNode);\n }\n if (replacementNode) {\n this.editor.insertNode(replacementNode);\n }\n }\n\n private getRangeUntilAt(event: PluginKeyboardEvent): Range {\n let positionContentSearcher = this.editor.getContentSearcherOfCursor(event);\n let startPos: NodePosition;\n let endPos: NodePosition;\n positionContentSearcher.forEachTextInlineElement(textInline => {\n let hasMatched = false;\n let nodeContent = textInline.getTextContent();\n let nodeIndex = nodeContent ? nodeContent.length : -1;\n while (nodeIndex >= 0) {\n if (nodeContent[nodeIndex] == this.pickerOptions.triggerCharacter) {\n startPos = textInline.getStartPosition().move(nodeIndex);\n hasMatched = true;\n break;\n }\n nodeIndex--;\n }\n\n if (hasMatched) {\n endPos = textInline.getEndPosition();\n }\n\n return hasMatched;\n });\n return createRange(startPos, endPos) || this.editor.getDocument().createRange();\n }\n\n private shouldHandleKeyUpEvent(event: PluginKeyboardEvent) {\n // onKeyUpDomEvent should only be called when a key that produces a character value is pressed\n // This check will always fail on Android since the KeyboardEvent's key is \"Unidentified\" or undefined\n // However, we don't need to check for modifier events on mobile, so can ignore this check\n return (\n this.isAndroidKeyboardEvent(event) ||\n isCharacterValue(event.rawEvent) ||\n (this.isSuggesting && !isModifierKey(event.rawEvent))\n );\n }\n\n private onKeyUpDomEvent(event: PluginKeyboardEvent) {\n if (this.isSuggesting) {\n // Word before cursor represents the text prior to the cursor, up to and including the trigger symbol.\n const wordBeforeCursor = this.getWord(event);\n const wordBeforeCursorWithoutTriggerChar = wordBeforeCursor.substring(1);\n const trimmedWordBeforeCursor = wordBeforeCursorWithoutTriggerChar.trim();\n\n // If we hit a case where wordBeforeCursor is just the trigger character,\n // that means we've gotten a onKeyUp event right after it's been typed.\n // Otherwise, update the query string when:\n // 1. There's an actual value\n // 2. That actual value isn't just pure whitespace\n // 3. That actual value isn't more than 4 words long (at which point we assume the person kept typing)\n // Otherwise, we want to dismiss the picker plugin's UX.\n if (\n wordBeforeCursor == this.pickerOptions.triggerCharacter ||\n (trimmedWordBeforeCursor &&\n trimmedWordBeforeCursor.length > 0 &&\n trimmedWordBeforeCursor.split(' ').length <= 4)\n ) {\n this.dataProvider.queryStringUpdated(\n trimmedWordBeforeCursor,\n wordBeforeCursorWithoutTriggerChar == trimmedWordBeforeCursor\n );\n this.setLastKnownRange(this.editor.getSelectionRange());\n } else {\n this.setIsSuggesting(false);\n }\n } else {\n let wordBeforeCursor = this.getWordBeforeCursor(event);\n if (!this.blockSuggestions) {\n if (\n wordBeforeCursor != null &&\n wordBeforeCursor.split(' ').length <= 4 &&\n wordBeforeCursor[0] == this.pickerOptions.triggerCharacter\n ) {\n this.setIsSuggesting(true);\n const wordBeforeCursorWithoutTriggerChar = wordBeforeCursor.substring(1);\n let trimmedWordBeforeCursor = wordBeforeCursorWithoutTriggerChar.trim();\n this.dataProvider.queryStringUpdated(\n trimmedWordBeforeCursor,\n wordBeforeCursorWithoutTriggerChar == trimmedWordBeforeCursor\n );\n this.setLastKnownRange(this.editor.getSelectionRange());\n if (this.dataProvider.setCursorPoint) {\n // Determine the bounding rectangle for the @mention\n let searcher = this.editor.getContentSearcherOfCursor(event);\n let rangeNode = this.editor.getDocument().createRange();\n let nodeBeforeCursor = searcher.getInlineElementBefore().getContainerNode();\n let rangeStartSuccessfullySet = this.setRangeStart(\n rangeNode,\n nodeBeforeCursor,\n wordBeforeCursor\n );\n if (!rangeStartSuccessfullySet) {\n // VSO 24891: Out of range error is occurring because nodeBeforeCursor\n // is not including the trigger character. In this case, the node before\n // the node before cursor is the trigger character, and this is where the range should start.\n let nodeBeforeNodeBeforeCursor = nodeBeforeCursor.previousSibling;\n this.setRangeStart(\n rangeNode,\n nodeBeforeNodeBeforeCursor,\n this.pickerOptions.triggerCharacter\n );\n }\n let rect = rangeNode.getBoundingClientRect();\n\n // Safari's support for range.getBoundingClientRect is incomplete.\n // We perform this check to fall back to getClientRects in case it's at the page origin.\n if (rect.left == 0 && rect.bottom == 0 && rect.top == 0) {\n rect = rangeNode.getClientRects()[0];\n }\n\n if (rect) {\n rangeNode.detach();\n\n // Display the @mention popup in the correct place\n let targetPoint = { x: rect.left, y: (rect.bottom + rect.top) / 2 };\n let bufferZone = (rect.bottom - rect.top) / 2;\n this.dataProvider.setCursorPoint(targetPoint, bufferZone);\n }\n }\n }\n } else {\n if (\n wordBeforeCursor != null &&\n wordBeforeCursor[0] != this.pickerOptions.triggerCharacter\n ) {\n this.blockSuggestions = false;\n }\n }\n }\n }\n\n private onKeyDownEvent(event: PluginKeyboardEvent) {\n let keyboardEvent = event.rawEvent;\n if (this.isSuggesting) {\n if (keyboardEvent.key == ESC_CHAR_CODE) {\n this.setIsSuggesting(false);\n this.blockSuggestions = true;\n this.cancelDefaultKeyDownEvent(event);\n } else if (keyboardEvent.key == BACKSPACE_CHAR_CODE) {\n // #483: If we are backspacing over the trigger character that triggered this Picker\n // then we need to hide the Picker\n const wordBeforeCursor = this.getWord(event);\n if (wordBeforeCursor == this.pickerOptions.triggerCharacter) {\n this.setIsSuggesting(false);\n }\n } else if (\n this.dataProvider.shiftHighlight &&\n (this.pickerOptions.isHorizontal\n ? keyboardEvent.key == LEFT_ARROW_CHAR_CODE ||\n keyboardEvent.key == RIGHT_ARROW_CHAR_CODE\n : keyboardEvent.key == UP_ARROW_CHAR_CODE ||\n keyboardEvent.key == DOWN_ARROW_CHAR_CODE)\n ) {\n this.dataProvider.shiftHighlight(\n this.pickerOptions.isHorizontal\n ? keyboardEvent.key == RIGHT_ARROW_CHAR_CODE\n : keyboardEvent.key == DOWN_ARROW_CHAR_CODE\n );\n\n if (this.dataProvider.getSelectedIndex) {\n this.setAriaActiveDescendant(this.dataProvider.getSelectedIndex());\n }\n\n this.cancelDefaultKeyDownEvent(event);\n } else if (\n this.dataProvider.selectOption &&\n (keyboardEvent.key == ENTER_CHAR_CODE || keyboardEvent.key == TAB_CHAR_CODE)\n ) {\n this.dataProvider.selectOption();\n this.cancelDefaultKeyDownEvent(event);\n } else {\n // Currently no op.\n }\n } else {\n if (keyboardEvent.key == BACKSPACE_CHAR_CODE) {\n const nodeRemoved = this.tryRemoveNode(event);\n if (nodeRemoved) {\n this.cancelDefaultKeyDownEvent(event);\n }\n } else if (keyboardEvent.key == DELETE_CHAR_CODE) {\n let searcher = this.editor.getContentSearcherOfCursor(event);\n let nodeAfterCursor = searcher.getInlineElementAfter()\n ? searcher.getInlineElementAfter().getContainerNode()\n : null;\n let nodeId = nodeAfterCursor ? this.getIdValue(nodeAfterCursor) : null;\n if (nodeId && nodeId.indexOf(this.pickerOptions.elementIdPrefix) == 0) {\n let replacementNode = this.dataProvider.onRemove(nodeAfterCursor, false);\n this.replaceNode(nodeAfterCursor, replacementNode);\n this.cancelDefaultKeyDownEvent(event);\n }\n }\n }\n }\n\n private onAndroidInputEvent(event: PluginInputEvent) {\n this.newInputLength = this.calcInputLength(event);\n\n if (\n this.newInputLength < this.currentInputLength ||\n (event.rawEvent as any).inputType === DELETE_CONTENT_BACKWARDS_INPUT_TYPE\n ) {\n const nodeRemoved = this.tryRemoveNode(event);\n if (nodeRemoved) {\n this.eventHandledOnKeyDown = true;\n }\n }\n }\n\n private calcInputLength(event: PluginEvent) {\n const wordBeforCursor = this.getInlineElementBeforeCursor(event);\n return wordBeforCursor ? wordBeforCursor.length : 0;\n }\n\n private tryRemoveNode(event: PluginDomEvent): boolean {\n const searcher = this.editor.getContentSearcherOfCursor(event);\n const inlineElementBefore = searcher.getInlineElementBefore();\n const nodeBeforeCursor = inlineElementBefore\n ? inlineElementBefore.getContainerNode()\n : null;\n const nodeId = nodeBeforeCursor ? this.getIdValue(nodeBeforeCursor) : null;\n const inlineElementAfter = searcher.getInlineElementAfter();\n\n if (\n nodeId &&\n nodeId.indexOf(this.pickerOptions.elementIdPrefix) == 0 &&\n (inlineElementAfter == null || !(inlineElementAfter instanceof PartialInlineElement))\n ) {\n const replacementNode = this.dataProvider.onRemove(nodeBeforeCursor, true);\n if (replacementNode) {\n this.replaceNode(nodeBeforeCursor, replacementNode);\n if (this.isPendingInputEventHandling) {\n this.editor.runAsync(editor => {\n editor.select(replacementNode, PositionType.After);\n });\n } else {\n this.editor.select(replacementNode, PositionType.After);\n }\n } else {\n this.editor.deleteNode(nodeBeforeCursor);\n }\n return true;\n }\n return false;\n }\n\n private getWord(event: PluginKeyboardEvent) {\n let wordFromRange = this.getRangeUntilAt(event).toString();\n let wordFromCache = this.getWordBeforeCursor(event);\n // VSO 24891: In picker, trigger and mention are separated into two nodes.\n // In this case, wordFromRange is the trigger character while wordFromCache is the whole string,\n // so wordFromCache is what we want to return.\n if (\n wordFromRange == this.pickerOptions.triggerCharacter &&\n wordFromRange != wordFromCache\n ) {\n return wordFromCache;\n }\n return wordFromRange;\n }\n\n private setRangeStart(rangeNode: Range, node: Node, target: string) {\n let nodeOffset = node ? node.textContent.lastIndexOf(target) : -1;\n if (nodeOffset > -1) {\n rangeNode.setStart(node, nodeOffset);\n return true;\n }\n return false;\n }\n\n private setAriaOwns(isSuggesting: boolean) {\n this.editor.setEditorDomAttribute(\n 'aria-owns',\n isSuggesting && this.pickerOptions.suggestionsLabel\n ? this.pickerOptions.suggestionsLabel\n : null\n );\n }\n\n private setAriaActiveDescendant(selectedIndex: number) {\n this.editor.setEditorDomAttribute(\n 'aria-activedescendant',\n selectedIndex != null && this.pickerOptions.suggestionLabelPrefix\n ? this.pickerOptions.suggestionLabelPrefix + selectedIndex.toString()\n : null\n );\n }\n\n private getInlineElementBeforeCursor(event: PluginEvent): string {\n const searcher = this.editor.getContentSearcherOfCursor(event);\n const element = searcher ? searcher.getInlineElementBefore() : null;\n return element ? element.getTextContent() : null;\n }\n\n private isAndroidKeyboardEvent(event: PluginKeyboardEvent): boolean {\n // Check keyboard events on Android for further handling.\n // On Android Webview later 51, the KeyboardEvent's key is \"Unidentified\".\n // On Android Webview below 51, the KeyboardEvent's key is not supported and always returns undefined,\n // so using the charCode property, which is 0 or 229.\n return (\n event.rawEvent.key == UNIDENTIFIED_KEY ||\n (event.rawEvent.key == undefined &&\n UNIDENTIFIED_CODE.indexOf(event.rawEvent.charCode) > -1)\n );\n }\n}\n","export * from './plugins/TableResize/index';\n","export { default as TableResize } from './TableResize';\n","import { createElement, getComputedStyle, normalizeRect, VTable } from 'roosterjs-editor-dom';\nimport {\n EditorPlugin,\n IEditor,\n PluginEvent,\n PluginEventType,\n Rect,\n ChangeSource,\n TableOperation,\n ContentPosition,\n CreateElementData,\n KnownCreateElementDataIndex,\n} from 'roosterjs-editor-types';\n\nconst INSERTER_COLOR = '#4A4A4A';\nconst INSERTER_COLOR_DARK_MODE = 'white';\nconst INSERTER_SIDE_LENGTH = 12;\nconst INSERTER_BORDER_SIZE = 1;\nconst INSERTER_HOVER_OFFSET = 5;\nconst MIN_CELL_WIDTH = 30;\nconst MIN_CELL_HEIGHT = 20;\nconst CELL_RESIZER_WIDTH = 4;\nconst TABLE_RESIZER_LENGTH = 12;\n\nconst enum ResizeState {\n None,\n Horizontal,\n Vertical,\n Both, // when resizing the whole table\n}\n\nfunction getHorizontalDistance(rect: Rect, pos: number, toLeft: boolean): number {\n return toLeft ? pos - rect.left : rect.right - pos;\n}\n\n/**\n * TableResize plugin, provides the ability to resize a table by drag-and-drop\n */\nexport default class TableResize implements EditorPlugin {\n private editor: IEditor;\n private onMouseMoveDisposer: () => void;\n private tableRectMap: { table: HTMLTableElement; rect: Rect }[] = null;\n private resizerContainer: HTMLDivElement;\n private tableResizerContainer: HTMLDivElement;\n private currentTable: HTMLTableElement;\n private currentTd: HTMLTableCellElement;\n private currentCellsToResize: HTMLTableCellElement[] = [];\n private nextCellsToResize: HTMLTableCellElement[] = [];\n private horizontalResizer: HTMLDivElement;\n private verticalResizer: HTMLDivElement;\n private tableResizer: HTMLDivElement;\n private resizingState: ResizeState = ResizeState.None;\n\n private currentInsertTd: HTMLTableCellElement;\n private insertingState: ResizeState = ResizeState.None;\n private inserter: HTMLDivElement;\n private isRTL: boolean;\n\n private currentTableVerticalBorder: number;\n private currentTableHorizontalBorder: number;\n private resizingVtable: VTable;\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'TableResize';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.setupResizerContainer();\n this.onMouseMoveDisposer = this.editor.addDomEventHandler('mousemove', this.onMouseMove);\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.onMouseMoveDisposer();\n this.tableRectMap = null;\n this.removeResizerContainer();\n this.setCurrentTable(null);\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(e: PluginEvent) {\n switch (e.eventType) {\n case PluginEventType.Input:\n case PluginEventType.ContentChanged:\n case PluginEventType.Scroll:\n this.setTableResizer(null);\n this.tableRectMap = null;\n break;\n }\n }\n\n private setupResizerContainer() {\n const document = this.editor.getDocument();\n this.resizerContainer = document.createElement('div');\n this.editor.insertNode(this.resizerContainer, {\n updateCursor: false,\n insertOnNewLine: false,\n replaceSelection: false,\n position: ContentPosition.Outside,\n });\n\n this.tableResizerContainer = document.createElement('div');\n this.editor.insertNode(this.tableResizerContainer, {\n updateCursor: false,\n insertOnNewLine: false,\n replaceSelection: false,\n position: ContentPosition.Outside,\n });\n }\n\n private removeResizerContainer() {\n this.resizerContainer?.parentNode?.removeChild(this.resizerContainer);\n this.resizerContainer = null;\n this.tableResizerContainer?.parentNode?.removeChild(this.tableResizerContainer);\n this.tableResizerContainer = null;\n }\n\n private onMouseOutInserter = () => {\n this.setCurrentInsertTd(ResizeState.None);\n };\n\n private onMouseMove = (e: MouseEvent) => {\n if (this.resizingState != ResizeState.None) {\n return;\n }\n\n if (!this.tableRectMap) {\n this.cacheRects();\n }\n\n if (this.tableRectMap) {\n this.setCurrentTable(null);\n\n for (let i = this.tableRectMap.length - 1; i >= 0; i--) {\n const { table, rect } = this.tableRectMap[i];\n\n if (\n e.pageX <=\n rect.right + (this.isRTL ? INSERTER_SIDE_LENGTH : TABLE_RESIZER_LENGTH) &&\n e.pageX >=\n rect.left - (this.isRTL ? TABLE_RESIZER_LENGTH : INSERTER_SIDE_LENGTH) &&\n e.pageY >= rect.top - INSERTER_SIDE_LENGTH &&\n e.pageY <= rect.bottom + TABLE_RESIZER_LENGTH\n ) {\n this.setCurrentTable(table);\n break;\n }\n }\n\n if (this.currentTable) {\n const map = this.tableRectMap.filter(map => map.table == this.currentTable)[0];\n this.setTableResizer(map.rect);\n for (let i = 0; i < this.currentTable.rows.length; i++) {\n const tr = this.currentTable.rows[i];\n let j = 0;\n for (; j < tr.cells.length; j++) {\n const td = tr.cells[j];\n const tdRect = normalizeRect(td.getBoundingClientRect());\n\n if (\n tdRect &&\n (this.isRTL ? e.pageX >= tdRect.left : e.pageX <= tdRect.right) &&\n e.pageY <= tdRect.bottom\n ) {\n // check vertical inserter\n if (\n i == 0 &&\n e.pageY >= tdRect.top - INSERTER_HOVER_OFFSET &&\n e.pageY <= tdRect.top + INSERTER_HOVER_OFFSET\n ) {\n let verticalInserterTd: HTMLTableCellElement = null;\n // set inserter at current td\n if (\n this.isRTL\n ? e.pageX <=\n tdRect.left + (tdRect.right - tdRect.left) / 2.0\n : e.pageX >=\n tdRect.left + (tdRect.right - tdRect.left) / 2.0\n ) {\n verticalInserterTd = td;\n } else if (\n this.isRTL ? e.pageX <= tdRect.right : e.pageX >= tdRect.left\n ) {\n // set inserter at previous td if it exists\n const preTd = td.previousElementSibling as HTMLTableCellElement;\n if (preTd) {\n verticalInserterTd = preTd;\n }\n }\n if (verticalInserterTd) {\n this.setCurrentTd(null);\n // we hide the inserter if left mouse button is pressed\n if (e.buttons == 0) {\n this.setCurrentInsertTd(\n ResizeState.Vertical,\n verticalInserterTd,\n map.rect\n );\n }\n break;\n }\n // check horizontal inserter\n } else if (\n j == 0 &&\n (this.isRTL\n ? e.pageX >= tdRect.right - INSERTER_HOVER_OFFSET\n : e.pageX <= tdRect.left + INSERTER_HOVER_OFFSET)\n ) {\n let horizontalInserterTd: HTMLTableCellElement = null;\n // set inserter at current td\n if (e.pageY >= tdRect.top + (tdRect.bottom - tdRect.top) / 2.0) {\n horizontalInserterTd = td;\n } else if (e.pageY >= tdRect.top) {\n // set insert at previous td if it exists\n const preTd = this.currentTable.rows[i - 1]?.cells[0];\n if (preTd) {\n horizontalInserterTd = preTd;\n }\n }\n\n if (horizontalInserterTd) {\n this.setCurrentTd(null);\n // we hide the inserter if left mouse button is pressed\n if (e.buttons == 0) {\n this.setCurrentInsertTd(\n ResizeState.Horizontal,\n horizontalInserterTd,\n map.rect\n );\n }\n break;\n }\n } else {\n this.setCurrentTd(\n td,\n map.rect,\n this.isRTL ? tdRect.left : tdRect.right,\n tdRect.bottom\n );\n this.setCurrentInsertTd(ResizeState.None);\n break;\n }\n }\n }\n if (j < tr.cells.length) {\n break;\n }\n }\n } else {\n this.setTableResizer(null);\n }\n }\n };\n\n private setCurrentInsertTd(insertingState: ResizeState.None): void;\n private setCurrentInsertTd(\n insertingState: ResizeState,\n td: HTMLTableCellElement,\n tableRect: Rect\n ): void;\n private setCurrentInsertTd(\n insertingState: ResizeState,\n td?: HTMLTableCellElement,\n tableRect?: Rect\n ) {\n if (td != this.currentInsertTd || insertingState != this.insertingState) {\n if (this.currentInsertTd) {\n this.inserter?.parentNode?.removeChild(this.inserter);\n this.inserter = null;\n }\n this.insertingState = insertingState;\n this.currentInsertTd = td;\n if (this.currentInsertTd) {\n this.inserter = this.createInserter(tableRect);\n this.resizerContainer.appendChild(this.inserter);\n }\n }\n }\n\n private createInserter(tableRect: Rect) {\n if (this.insertingState == ResizeState.None) {\n return;\n }\n\n const rect = normalizeRect(this.currentInsertTd.getBoundingClientRect());\n const editorBackgroundColor = this.editor.getDefaultFormat().backgroundColor;\n const inserterBackgroundColor = editorBackgroundColor || 'white';\n const inserterColor = this.editor.isDarkMode() ? INSERTER_COLOR_DARK_MODE : INSERTER_COLOR;\n const leftOrRight = this.isRTL ? 'right' : 'left';\n const outerDivStyle = `position: fixed; width: ${INSERTER_SIDE_LENGTH}px; height: ${INSERTER_SIDE_LENGTH}px; font-size: 16px; color: ${inserterColor}; line-height: 10px; vertical-align: middle; text-align: center; cursor: pointer; border: solid ${INSERTER_BORDER_SIZE}px ${inserterColor}; border-radius: 50%; background-color: ${inserterBackgroundColor}`;\n const HORIZONTAL_INSERTER: CreateElementData = {\n tag: 'div',\n style: outerDivStyle,\n children: [\n {\n tag: 'div',\n style: `position: absolute; ${leftOrRight}: 12px; top: 5px; height: 3px; border-top: 1px solid ${inserterColor}; border-bottom: 1px solid ${inserterColor}; border-right: 1px solid ${inserterColor}; border-left: 0px; box-sizing: border-box; background-color: ${inserterBackgroundColor};`,\n },\n '+',\n ],\n };\n const VERTICAL_INSERTER: CreateElementData = {\n tag: 'div',\n style: outerDivStyle,\n children: [\n {\n tag: 'div',\n style: `position: absolute; left: 5px; top: 12px; width: 3px; border-left: 1px solid ${inserterColor}; border-right: 1px solid ${inserterColor}; border-bottom: 1px solid ${inserterColor}; border-top: 0px; box-sizing: border-box; background-color: ${inserterBackgroundColor}`,\n },\n '+',\n ],\n };\n\n const inserter = createElement(\n this.insertingState == ResizeState.Horizontal ? HORIZONTAL_INSERTER : VERTICAL_INSERTER,\n this.editor.getDocument()\n ) as HTMLDivElement;\n\n if (rect) {\n if (this.insertingState == ResizeState.Horizontal) {\n if (this.isRTL) {\n inserter.style.left = `${rect.right}px`;\n } else {\n inserter.style.left = `${\n rect.left - (INSERTER_SIDE_LENGTH - 1 + 2 * INSERTER_BORDER_SIZE)\n }px`;\n }\n inserter.style.top = `${rect.bottom - 8}px`;\n (inserter.firstChild as HTMLElement).style.width = `${\n tableRect.right - tableRect.left\n }px`;\n } else {\n if (this.isRTL) {\n inserter.style.left = `${rect.left - 8}px`;\n } else {\n inserter.style.left = `${rect.right - 8}px`;\n }\n inserter.style.top = `${\n rect.top - (INSERTER_SIDE_LENGTH - 1 + 2 * INSERTER_BORDER_SIZE)\n }px`;\n (inserter.firstChild as HTMLElement).style.height = `${\n tableRect.bottom - tableRect.top\n }px`;\n }\n }\n\n inserter.addEventListener('click', this.insertTd);\n inserter.addEventListener('mouseout', this.onMouseOutInserter);\n\n return inserter;\n }\n\n private insertTd = () => {\n let vtable = new VTable(this.currentInsertTd);\n if (this.insertingState === ResizeState.Vertical) {\n vtable.normalizeTableCellSize();\n // Since adding new column will cause table width to change, we need to remove width properties\n vtable.table.removeAttribute('width');\n vtable.table.style.width = null;\n }\n this.editor.addUndoSnapshot((start, end) => {\n vtable.edit(\n this.insertingState == ResizeState.Horizontal\n ? TableOperation.InsertBelow\n : TableOperation.InsertRight\n );\n vtable.writeBack();\n this.editor.select(start, end);\n this.setCurrentInsertTd(ResizeState.None);\n // need to update the position of table resizer as new row/column has been added\n if (this.currentTable) {\n const rect = normalizeRect(this.currentTable.getBoundingClientRect());\n this.setTableResizer(rect);\n }\n }, ChangeSource.Format);\n };\n\n private setCurrentTable(table: HTMLTableElement) {\n if (this.currentTable != table) {\n this.setCurrentTd(null);\n this.setCurrentInsertTd(ResizeState.None);\n this.currentTable = table;\n }\n }\n\n private setCurrentTd(td: null): void;\n private setCurrentTd(\n td: HTMLTableCellElement,\n tableRect: Rect,\n resizerPosX: number,\n bottom: number\n ): void;\n private setCurrentTd(\n td: HTMLTableCellElement,\n tableRect?: Rect,\n resizerPosX?: number,\n bottom?: number\n ) {\n if (this.currentTd != td) {\n if (this.currentTd) {\n this.horizontalResizer?.parentNode?.removeChild(this.horizontalResizer);\n this.verticalResizer?.parentNode?.removeChild(this.verticalResizer);\n this.horizontalResizer = null;\n this.verticalResizer = null;\n }\n\n this.currentTd = td;\n\n if (this.currentTd) {\n this.horizontalResizer = this.createCellsResizer(\n true /*horizontal*/,\n tableRect.left,\n bottom - CELL_RESIZER_WIDTH + 1,\n tableRect.right - tableRect.left,\n CELL_RESIZER_WIDTH\n );\n this.verticalResizer = this.createCellsResizer(\n false /*horizontal*/,\n resizerPosX - CELL_RESIZER_WIDTH + 1,\n tableRect.top,\n CELL_RESIZER_WIDTH,\n tableRect.bottom - tableRect.top\n );\n\n this.resizerContainer.appendChild(this.horizontalResizer);\n this.resizerContainer.appendChild(this.verticalResizer);\n }\n }\n }\n\n private setTableResizer(rect: Rect | null): void {\n // remove old one if exists\n while (this.tableResizerContainer?.hasChildNodes()) {\n this.tableResizerContainer.removeChild(this.tableResizerContainer.lastChild);\n }\n this.tableResizer = null;\n // add new one if exists\n if (rect) {\n this.tableResizer = this.createTableResizer(rect);\n this.tableResizerContainer.appendChild(this.tableResizer);\n }\n }\n\n private createTableResizer(rect: Rect): HTMLDivElement {\n const div = createElement(\n this.isRTL\n ? KnownCreateElementDataIndex.TableResizerRTL\n : KnownCreateElementDataIndex.TableResizerLTR,\n this.editor.getDocument()\n ) as HTMLDivElement;\n\n div.style.top = `${rect.bottom}px`;\n div.style.left = this.isRTL\n ? `${rect.left - TABLE_RESIZER_LENGTH - 2}px`\n : `${rect.right}px`;\n div.style.width = `${TABLE_RESIZER_LENGTH}px`;\n div.style.height = `${TABLE_RESIZER_LENGTH}px`;\n\n div.addEventListener('mousedown', this.startResizingTable);\n\n return div;\n }\n\n private createCellsResizer(\n horizontal: boolean,\n left: number,\n top: number,\n width: number,\n height: number\n ): HTMLDivElement {\n const div = createElement(\n horizontal\n ? KnownCreateElementDataIndex.TableHorizontalResizer\n : KnownCreateElementDataIndex.TableVerticalResizer,\n this.editor.getDocument()\n ) as HTMLDivElement;\n div.style.top = `${top}px`;\n div.style.left = `${left}px`;\n div.style.width = `${width}px`;\n div.style.height = `${height}px`;\n\n div.addEventListener(\n 'mousedown',\n horizontal ? this.startHorizontalResizeCells : this.startVerticalResizeCells\n );\n\n return div;\n }\n\n private startResizingTable = (e: MouseEvent) => {\n if (!this.currentTable) {\n return;\n }\n this.resizingVtable = new VTable(\n this.currentTable,\n true /* normalize the table for resizing */\n );\n this.resizingState = ResizeState.Both;\n\n const rect = this.resizingVtable.table.getBoundingClientRect();\n this.currentTableVerticalBorder = this.isRTL ? rect.left : rect.right;\n this.currentTableHorizontalBorder = rect.bottom;\n\n this.startResizeCells(e);\n };\n\n private startHorizontalResizeCells = (e: MouseEvent) => {\n if (!this.currentTd) {\n return;\n }\n this.resizingVtable = new VTable(\n this.currentTd,\n true /* normalize the table for resizing */\n );\n this.resizingState = ResizeState.Horizontal;\n this.startResizeCells(e);\n };\n\n private startVerticalResizeCells = (e: MouseEvent) => {\n if (!this.currentTd) {\n return;\n }\n this.resizingVtable = new VTable(\n this.currentTd,\n true /* normalize the table for resizing */\n );\n this.resizingState = ResizeState.Vertical;\n if (this.resizingVtable) {\n const rect = normalizeRect(this.currentTd.getBoundingClientRect());\n\n // calculate and retrieve the cells of the two columns shared by the current vertical resizer\n this.currentCellsToResize = this.resizingVtable.getCellsWithBorder(\n this.isRTL ? rect.left : rect.right,\n !this.isRTL\n );\n\n this.nextCellsToResize = this.resizingVtable.getCellsWithBorder(\n this.isRTL ? rect.left : rect.right,\n this.isRTL\n );\n }\n this.startResizeCells(e);\n };\n\n private startResizeCells(e: MouseEvent) {\n const doc = this.editor.getDocument();\n doc.addEventListener('mousemove', this.frameAnimateResizeCells, true);\n doc.addEventListener('mouseup', this.endResizeCells, true);\n }\n\n private frameAnimateResizeCells = (e: MouseEvent) => {\n this.editor.runAsync(() => this.resizeCells(e));\n };\n\n private resizeTable = (mouseX: number, mouseY: number) => {\n const rect = normalizeRect(this.resizingVtable.table.getBoundingClientRect());\n const ratioX =\n 1.0 +\n (this.isRTL\n ? (this.currentTableVerticalBorder - mouseX) /\n (rect.right - this.currentTableVerticalBorder)\n : (mouseX - this.currentTableVerticalBorder) /\n (this.currentTableVerticalBorder - rect.left));\n const ratioY =\n 1.0 +\n (mouseY - this.currentTableHorizontalBorder) /\n (this.currentTableHorizontalBorder - rect.top);\n\n const shouldResizeX = Math.abs(ratioX - 1.0) > 1e-3;\n const shouldResizeY = Math.abs(ratioY - 1.0) > 1e-3;\n if (shouldResizeX || shouldResizeY) {\n for (let i = 0; i < this.resizingVtable.cells.length; i++) {\n for (let j = 0; j < this.resizingVtable.cells[i].length; j++) {\n const cell = this.resizingVtable.cells[i][j];\n if (cell.td) {\n if (shouldResizeX) {\n // the width of some external table is fixed, we need to make it resizable\n this.resizingVtable.table.style.width = null;\n const newWidth = cell.width * ratioX;\n cell.td.style.boxSizing = 'border-box';\n if (newWidth >= MIN_CELL_WIDTH) {\n cell.td.style.wordBreak = 'break-word';\n cell.td.style.whiteSpace = 'normal';\n cell.td.style.width = `${newWidth}px`;\n }\n }\n\n if (shouldResizeY) {\n // the height of some external table is fixed, we need to make it resizable\n this.resizingVtable.table.style.height = null;\n if (j == 0) {\n const newHeight = cell.height * ratioY;\n if (newHeight >= MIN_CELL_HEIGHT) {\n cell.td.style.height = `${newHeight}px`;\n }\n } else {\n cell.td.style.height = null;\n }\n }\n }\n }\n }\n }\n return;\n };\n\n private resizeRows = (newPos: number, rect: Rect) => {\n this.resizingVtable.table.removeAttribute('height');\n this.resizingVtable.table.style.height = null;\n this.resizingVtable.forEachCellOfCurrentRow(cell => {\n if (cell.td) {\n cell.td.style.height = cell.td == this.currentTd ? `${newPos - rect.top}px` : null;\n }\n });\n };\n\n private canResizeColumns = (newPos: number): boolean => {\n for (let i = 0; i < this.currentCellsToResize.length; i++) {\n const td = this.currentCellsToResize[i];\n const rect = normalizeRect(td.getBoundingClientRect());\n const width = getHorizontalDistance(rect, newPos, !this.isRTL);\n if (width < MIN_CELL_WIDTH) {\n return false;\n }\n }\n\n for (let i = 0; i < this.nextCellsToResize.length; i++) {\n const td = this.nextCellsToResize[i];\n let width: number = Number.MAX_SAFE_INTEGER;\n if (td) {\n const rect = normalizeRect(td.getBoundingClientRect());\n width = getHorizontalDistance(rect, newPos, this.isRTL);\n }\n\n if (width < MIN_CELL_WIDTH) {\n return false;\n }\n }\n return true;\n };\n\n private resizeColumns = (newPos: number, isShiftPressed: boolean) => {\n if (!this.canResizeColumns(newPos)) {\n return;\n }\n\n // Since we allow the user to resize the table width on adjusting the border of the last cell,\n // we need to make the table width resizable by setting it as null;\n // We also allow the user to resize the table width if Shift key is pressed\n const isLastCell: boolean = this.nextCellsToResize.length == 0;\n\n if (isLastCell || isShiftPressed) {\n this.resizingVtable.table.style.width = null;\n }\n\n this.currentCellsToResize.forEach(td => {\n const rect = normalizeRect(td.getBoundingClientRect());\n td.style.wordBreak = 'break-word';\n td.style.whiteSpace = 'normal';\n td.style.boxSizing = 'border-box';\n td.style.width = `${getHorizontalDistance(rect, newPos, !this.isRTL)}px`;\n });\n\n this.nextCellsToResize.forEach(td => {\n if (!isShiftPressed) {\n td.style.wordBreak = 'break-word';\n td.style.whiteSpace = 'normal';\n td.style.boxSizing = 'border-box';\n td.style.width = null;\n }\n });\n };\n\n private resizeCells = (e: MouseEvent) => {\n this.setTableResizer(null);\n if (this.resizingState === ResizeState.None) {\n return;\n } else if (this.resizingState === ResizeState.Both) {\n this.resizeTable(e.pageX, e.pageY);\n this.resizingVtable.writeBack();\n } else if (this.currentTd) {\n const rect = normalizeRect(this.currentTd.getBoundingClientRect());\n if (rect) {\n const newPos = this.resizingState == ResizeState.Horizontal ? e.pageY : e.pageX;\n if (this.resizingState === ResizeState.Horizontal) {\n this.resizeRows(newPos, rect);\n } else {\n this.resizeColumns(newPos, e.shiftKey);\n }\n this.resizingVtable.writeBack();\n }\n }\n };\n\n private endResizeCells = (e: MouseEvent) => {\n const doc = this.editor.getDocument();\n doc.removeEventListener('mousemove', this.frameAnimateResizeCells, true);\n doc.removeEventListener('mouseup', this.endResizeCells, true);\n this.currentCellsToResize = [];\n this.nextCellsToResize = [];\n\n this.editor.addUndoSnapshot((start, end) => {\n this.frameAnimateResizeCells(e);\n this.editor.select(start, end);\n }, ChangeSource.Format);\n\n this.setCurrentTd(null);\n this.setTableResizer(null);\n\n this.resizingVtable = null;\n this.resizingState = ResizeState.None;\n };\n\n private cacheRects() {\n this.tableRectMap = [];\n this.editor.queryElements('table', table => {\n if (table.isContentEditable) {\n const rect = normalizeRect(table.getBoundingClientRect());\n if (rect) {\n this.tableRectMap.push({\n table,\n rect,\n });\n }\n }\n });\n this.isRTL = getComputedStyle(this.editor.getDocument().body, 'direction') == 'rtl';\n }\n}\n","export * from './plugins/Watermark/index';\n","export { default as Watermark } from './Watermark';\n","import { applyFormat, getEntitySelector, getTagOfNode } from 'roosterjs-editor-dom';\nimport { insertEntity } from 'roosterjs-editor-api';\nimport {\n DefaultFormat,\n EditorPlugin,\n Entity,\n EntityOperation,\n IEditor,\n PluginEvent,\n PluginEventType,\n ContentPosition,\n} from 'roosterjs-editor-types';\n\nconst ENTITY_TYPE = 'WATERMARK_WRAPPER';\n\n/**\n * A watermark plugin to manage watermark string for roosterjs\n */\nexport default class Watermark implements EditorPlugin {\n private editor: IEditor;\n private disposer: () => void;\n\n /**\n * Create an instance of Watermark plugin\n * @param watermark The watermark string\n */\n constructor(private watermark: string, private format?: DefaultFormat) {\n this.format = this.format || {\n fontSize: '14px',\n textColors: {\n lightModeColor: '#AAAAAA',\n darkModeColor: '#6B6B6B',\n },\n };\n }\n\n /**\n * Get a friendly name of this plugin\n */\n getName() {\n return 'Watermark';\n }\n\n /**\n * Initialize this plugin. This should only be called from Editor\n * @param editor Editor instance\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.disposer = this.editor.addDomEventHandler({\n focus: this.showHideWatermark,\n blur: this.showHideWatermark,\n });\n }\n\n /**\n * Dispose this plugin\n */\n dispose() {\n this.disposer();\n this.disposer = null;\n this.editor = null;\n }\n\n /**\n * Handle events triggered from editor\n * @param event PluginEvent object\n */\n onPluginEvent(event: PluginEvent) {\n if (\n event.eventType == PluginEventType.EditorReady ||\n (event.eventType == PluginEventType.ContentChanged &&\n (event.data)?.type != ENTITY_TYPE)\n ) {\n this.showHideWatermark();\n } else if (\n event.eventType == PluginEventType.EntityOperation &&\n event.entity.type == ENTITY_TYPE\n ) {\n const {\n operation,\n entity: { wrapper },\n } = event;\n if (operation == EntityOperation.ReplaceTemporaryContent) {\n this.removeWatermark(wrapper);\n } else if (event.operation == EntityOperation.NewEntity) {\n applyFormat(wrapper, this.format, this.editor.isDarkMode());\n wrapper.spellcheck = false;\n }\n }\n }\n\n private showHideWatermark = () => {\n const hasFocus = this.editor.hasFocus();\n const watermarks = this.editor.queryElements(getEntitySelector(ENTITY_TYPE));\n const isShowing = watermarks.length > 0;\n\n if (hasFocus && isShowing) {\n watermarks.forEach(this.removeWatermark);\n this.editor.focus();\n } else if (!hasFocus && !isShowing && this.editor.isEmpty()) {\n insertEntity(\n this.editor,\n ENTITY_TYPE,\n this.editor.getDocument().createTextNode(this.watermark),\n false /*isBlock*/,\n false /*isReadonly*/,\n ContentPosition.Begin\n );\n }\n };\n\n private removeWatermark = (wrapper: HTMLElement) => {\n const parentNode = wrapper.parentNode;\n parentNode?.removeChild(wrapper);\n\n // After remove watermark node, if it leaves an empty DIV, append a BR node into it to make it a regular empty line\n if (\n this.editor.contains(parentNode) &&\n getTagOfNode(parentNode) == 'DIV' &&\n !parentNode.firstChild\n ) {\n parentNode.appendChild(this.editor.getDocument().createElement('BR'));\n }\n };\n}\n"],"sourceRoot":""}