roosterjs-content-model-plugins 0.21.3 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/edit/deleteSteps/deleteAllSegmentBefore.js +3 -0
- package/lib/edit/deleteSteps/deleteAllSegmentBefore.js.map +1 -1
- package/lib/edit/deleteSteps/deleteCollapsedSelection.js +3 -0
- package/lib/edit/deleteSteps/deleteCollapsedSelection.js.map +1 -1
- package/lib/edit/deleteSteps/deleteWordSelection.js +3 -0
- package/lib/edit/deleteSteps/deleteWordSelection.js.map +1 -1
- package/lib/edit/keyboardInput.js +5 -4
- package/lib/edit/keyboardInput.js.map +1 -1
- package/lib/paste/ContentModelPastePlugin.d.ts +3 -1
- package/lib/paste/ContentModelPastePlugin.js +5 -3
- package/lib/paste/ContentModelPastePlugin.js.map +1 -1
- package/lib/paste/Excel/processPastedContentFromExcel.d.ts +1 -1
- package/lib/paste/Excel/processPastedContentFromExcel.js +2 -2
- package/lib/paste/Excel/processPastedContentFromExcel.js.map +1 -1
- package/lib/paste/WacComponents/constants.d.ts +56 -0
- package/lib/paste/WacComponents/constants.js +86 -0
- package/lib/paste/WacComponents/constants.js.map +1 -0
- package/lib/paste/WacComponents/processPastedContentWacComponents.js +19 -34
- package/lib/paste/WacComponents/processPastedContentWacComponents.js.map +1 -1
- package/lib/paste/WordDesktop/WordMetadata.d.ts +9 -0
- package/lib/paste/WordDesktop/WordMetadata.js +3 -0
- package/lib/paste/WordDesktop/WordMetadata.js.map +1 -0
- package/lib/paste/WordDesktop/getStyleMetadata.d.ts +23 -0
- package/lib/paste/WordDesktop/getStyleMetadata.js +79 -0
- package/lib/paste/WordDesktop/getStyleMetadata.js.map +1 -0
- package/lib/paste/WordDesktop/processPastedContentFromWordDesktop.d.ts +2 -7
- package/lib/paste/WordDesktop/processPastedContentFromWordDesktop.js +14 -14
- package/lib/paste/WordDesktop/processPastedContentFromWordDesktop.js.map +1 -1
- package/lib/paste/WordDesktop/processWordLists.d.ts +2 -1
- package/lib/paste/WordDesktop/processWordLists.js +83 -85
- package/lib/paste/WordDesktop/processWordLists.js.map +1 -1
- package/lib/paste/pasteSourceValidations/documentContainWacElements.js +2 -11
- package/lib/paste/pasteSourceValidations/documentContainWacElements.js.map +1 -1
- package/lib/paste/pasteSourceValidations/getPasteSource.d.ts +2 -1
- package/lib/paste/pasteSourceValidations/getPasteSource.js.map +1 -1
- package/lib-amd/edit/deleteSteps/deleteAllSegmentBefore.js +3 -0
- package/lib-amd/edit/deleteSteps/deleteAllSegmentBefore.js.map +1 -1
- package/lib-amd/edit/deleteSteps/deleteCollapsedSelection.js +3 -0
- package/lib-amd/edit/deleteSteps/deleteCollapsedSelection.js.map +1 -1
- package/lib-amd/edit/deleteSteps/deleteWordSelection.js +3 -0
- package/lib-amd/edit/deleteSteps/deleteWordSelection.js.map +1 -1
- package/lib-amd/edit/keyboardInput.js +5 -4
- package/lib-amd/edit/keyboardInput.js.map +1 -1
- package/lib-amd/paste/ContentModelPastePlugin.d.ts +3 -1
- package/lib-amd/paste/ContentModelPastePlugin.js +5 -3
- package/lib-amd/paste/ContentModelPastePlugin.js.map +1 -1
- package/lib-amd/paste/Excel/processPastedContentFromExcel.d.ts +1 -1
- package/lib-amd/paste/Excel/processPastedContentFromExcel.js +2 -2
- package/lib-amd/paste/Excel/processPastedContentFromExcel.js.map +1 -1
- package/lib-amd/paste/WacComponents/constants.d.ts +56 -0
- package/lib-amd/paste/WacComponents/constants.js +87 -0
- package/lib-amd/paste/WacComponents/constants.js.map +1 -0
- package/lib-amd/paste/WacComponents/processPastedContentWacComponents.js +19 -35
- package/lib-amd/paste/WacComponents/processPastedContentWacComponents.js.map +1 -1
- package/lib-amd/paste/WordDesktop/WordMetadata.d.ts +9 -0
- package/lib-amd/paste/WordDesktop/WordMetadata.js +5 -0
- package/lib-amd/paste/WordDesktop/WordMetadata.js.map +1 -0
- package/lib-amd/paste/WordDesktop/getStyleMetadata.d.ts +23 -0
- package/lib-amd/paste/WordDesktop/getStyleMetadata.js +79 -0
- package/lib-amd/paste/WordDesktop/getStyleMetadata.js.map +1 -0
- package/lib-amd/paste/WordDesktop/processPastedContentFromWordDesktop.d.ts +2 -7
- package/lib-amd/paste/WordDesktop/processPastedContentFromWordDesktop.js +14 -15
- package/lib-amd/paste/WordDesktop/processPastedContentFromWordDesktop.js.map +1 -1
- package/lib-amd/paste/WordDesktop/processWordLists.d.ts +2 -1
- package/lib-amd/paste/WordDesktop/processWordLists.js +83 -85
- package/lib-amd/paste/WordDesktop/processWordLists.js.map +1 -1
- package/lib-amd/paste/pasteSourceValidations/documentContainWacElements.js +2 -12
- package/lib-amd/paste/pasteSourceValidations/documentContainWacElements.js.map +1 -1
- package/lib-amd/paste/pasteSourceValidations/getPasteSource.d.ts +2 -1
- package/lib-amd/paste/pasteSourceValidations/getPasteSource.js.map +1 -1
- package/lib-mjs/edit/deleteSteps/deleteAllSegmentBefore.js +3 -0
- package/lib-mjs/edit/deleteSteps/deleteAllSegmentBefore.js.map +1 -1
- package/lib-mjs/edit/deleteSteps/deleteCollapsedSelection.js +3 -0
- package/lib-mjs/edit/deleteSteps/deleteCollapsedSelection.js.map +1 -1
- package/lib-mjs/edit/deleteSteps/deleteWordSelection.js +3 -0
- package/lib-mjs/edit/deleteSteps/deleteWordSelection.js.map +1 -1
- package/lib-mjs/edit/keyboardInput.js +5 -4
- package/lib-mjs/edit/keyboardInput.js.map +1 -1
- package/lib-mjs/paste/ContentModelPastePlugin.d.ts +3 -1
- package/lib-mjs/paste/ContentModelPastePlugin.js +5 -3
- package/lib-mjs/paste/ContentModelPastePlugin.js.map +1 -1
- package/lib-mjs/paste/Excel/processPastedContentFromExcel.d.ts +1 -1
- package/lib-mjs/paste/Excel/processPastedContentFromExcel.js +2 -2
- package/lib-mjs/paste/Excel/processPastedContentFromExcel.js.map +1 -1
- package/lib-mjs/paste/WacComponents/constants.d.ts +56 -0
- package/lib-mjs/paste/WacComponents/constants.js +83 -0
- package/lib-mjs/paste/WacComponents/constants.js.map +1 -0
- package/lib-mjs/paste/WacComponents/processPastedContentWacComponents.js +12 -27
- package/lib-mjs/paste/WacComponents/processPastedContentWacComponents.js.map +1 -1
- package/lib-mjs/paste/WordDesktop/WordMetadata.d.ts +9 -0
- package/lib-mjs/paste/WordDesktop/WordMetadata.js +2 -0
- package/lib-mjs/paste/WordDesktop/WordMetadata.js.map +1 -0
- package/lib-mjs/paste/WordDesktop/getStyleMetadata.d.ts +23 -0
- package/lib-mjs/paste/WordDesktop/getStyleMetadata.js +76 -0
- package/lib-mjs/paste/WordDesktop/getStyleMetadata.js.map +1 -0
- package/lib-mjs/paste/WordDesktop/processPastedContentFromWordDesktop.d.ts +2 -7
- package/lib-mjs/paste/WordDesktop/processPastedContentFromWordDesktop.js +13 -12
- package/lib-mjs/paste/WordDesktop/processPastedContentFromWordDesktop.js.map +1 -1
- package/lib-mjs/paste/WordDesktop/processWordLists.d.ts +2 -1
- package/lib-mjs/paste/WordDesktop/processWordLists.js +84 -86
- package/lib-mjs/paste/WordDesktop/processWordLists.js.map +1 -1
- package/lib-mjs/paste/pasteSourceValidations/documentContainWacElements.js +1 -10
- package/lib-mjs/paste/pasteSourceValidations/documentContainWacElements.js.map +1 -1
- package/lib-mjs/paste/pasteSourceValidations/getPasteSource.d.ts +2 -1
- package/lib-mjs/paste/pasteSourceValidations/getPasteSource.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processPastedContentWacComponents.js","sourceRoot":"","sources":["../../../../../packages-content-model/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts"],"names":[],"mappings":"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","sourcesContent":["import addParser from '../utils/addParser';\nimport { setProcessor } from '../utils/setProcessor';\nimport type {\n ContentModelBeforePasteEvent,\n ContentModelBlockFormat,\n ContentModelBlockGroup,\n ContentModelListItemLevelFormat,\n ContentModelSegmentFormat,\n DomToModelContext,\n ElementProcessor,\n FormatParser,\n} from 'roosterjs-content-model-types';\n\nconst WAC_IDENTIFY_SELECTOR =\n 'ul[class^=\"BulletListStyle\"]>.OutlineElement,ol[class^=\"NumberListStyle\"]>.OutlineElement,span.WACImageContainer,span.WACImageBorder';\nconst LIST_CONTAINER_ELEMENT_CLASS_NAME = 'ListContainerWrapper';\n\nconst PARAGRAPH = 'Paragraph';\nconst TABLE_CONTAINER = 'TableContainer';\n\nconst TEMP_ELEMENTS_CLASSES = [\n 'TableInsertRowGapBlank',\n 'TableColumnResizeHandle',\n 'TableCellTopBorderHandle',\n 'TableCellLeftBorderHandle',\n 'TableHoverColumnHandle',\n 'TableHoverRowHandle',\n 'ListMarkerWrappingSpan',\n];\n\nconst CLASSES_TO_KEEP = [\n 'OutlineElement',\n 'NumberListStyle',\n 'WACImageContainer',\n 'ListContainerWrapper',\n 'BulletListStyle',\n ...TEMP_ELEMENTS_CLASSES,\n 'TableCellContent',\n PARAGRAPH,\n 'WACImageContainer',\n 'WACImageBorder',\n TABLE_CONTAINER,\n 'LineBreakBlob',\n];\n\nconst LIST_ELEMENT_TAGS = ['UL', 'OL', 'LI'];\nconst LIST_ELEMENT_SELECTOR = LIST_ELEMENT_TAGS.join(',');\n\n/**\n * Wac components do not use sub and super tags, instead only add vertical align to a span.\n * This parser normalize the content for content model\n */\nconst wacSubSuperParser: FormatParser<ContentModelSegmentFormat> = (\n format: ContentModelSegmentFormat,\n element: HTMLElement\n): void => {\n const verticalAlign = element.style.verticalAlign;\n if (verticalAlign === 'super') {\n format.superOrSubScriptSequence = 'super';\n }\n if (verticalAlign === 'sub') {\n format.superOrSubScriptSequence = 'sub';\n }\n};\n\n/**\n * This processor does:\n * 1) Remove the display and margin of the element.\n * 2) When an element should be ignored but should handle the child elements call the default child processor.\n * 3) Removes the End of Paragraph element to avoid empty lines, we should only remove this if the previous element of the EOP is an EmptyTextRun\n * 4) Finally call the default processor.\n * @returns\n */\nconst wacElementProcessor: ElementProcessor<HTMLElement> = (\n group: ContentModelBlockGroup,\n element: HTMLElement,\n context: DomToModelContext\n): void => {\n const elementTag = element.tagName;\n\n if (element.matches(WAC_IDENTIFY_SELECTOR)) {\n element.style.removeProperty('display');\n element.style.removeProperty('margin');\n }\n\n if (element.classList.contains(LIST_CONTAINER_ELEMENT_CLASS_NAME)) {\n context.elementProcessors.child(group, element, context);\n return;\n }\n\n if (TEMP_ELEMENTS_CLASSES.some(className => element.classList.contains(className))) {\n return;\n } else if (shouldClearListContext(elementTag, element, context)) {\n const { listFormat } = context;\n listFormat.levels = [];\n listFormat.listParent = undefined;\n }\n\n context.defaultElementProcessors.element(group, element, context);\n};\n\n/**\n * This processor calls the default list processor and then sets the correct list level and list bullet.\n */\nconst wacLiElementProcessor: ElementProcessor<HTMLLIElement> = (\n group: ContentModelBlockGroup,\n element: HTMLLIElement,\n context: DomToModelContext\n): void => {\n context.defaultElementProcessors.li?.(group, element, context);\n const { listFormat } = context;\n const listParent = listFormat.listParent;\n if (listParent) {\n const lastblock = listParent.blocks[listParent.blocks.length - 1];\n if (\n lastblock.blockType == 'BlockGroup' &&\n lastblock.blockGroupType == 'ListItem' &&\n context.listFormat.listParent !== lastblock\n ) {\n const currentLevel = lastblock.levels[lastblock.levels.length - 1];\n\n // Get item level from 'data-aria-level' attribute\n const level = parseInt(element.getAttribute('data-aria-level') ?? '');\n if (level > 0) {\n if (level > lastblock.levels.length) {\n while (level != lastblock.levels.length) {\n lastblock.levels.push(currentLevel);\n }\n } else {\n lastblock.levels.splice(level, lastblock.levels.length - 1);\n lastblock.levels[level - 1] = currentLevel;\n }\n }\n }\n }\n};\n\n/**\n * This parsers does:\n * 1) Sets the display for dummy item to undefined when the current style is block.\n * 2) Removes the Margin Left\n */\nconst wacListItemParser: FormatParser<ContentModelListItemLevelFormat> = (\n format: ContentModelListItemLevelFormat,\n element: HTMLElement\n): void => {\n if (element.style.display === 'block') {\n format.displayForDummyItem = undefined;\n }\n\n format.marginLeft = undefined;\n};\n\n/**\n * Wac usually adds padding to lists which is unwanted so remove it.\n */\nconst wacListLevelParser: FormatParser<ContentModelListItemLevelFormat> = (\n format: ContentModelListItemLevelFormat\n): void => {\n format.marginLeft = undefined;\n format.paddingLeft = undefined;\n};\n\n/**\n * This function returns whether we need to clear the list format.\n * Word Online wraps lists inside divs to have this structure:\n *\n * <div class='ListContainerWrapper'>\n * <ol>...</ol>\n * </div>\n * <div>\n * <p>...</p>\n * <div>\n * <div class='ListContainerWrapper'>\n * <ol>...</ol>\n * </div>\n *\n * So if a elements is not contained inside of a list we should clear the list context to prevent normal text to be\n * transformed into list\n * For the above scenario, if we do not clear the format, the content inside of the second div would be transformed to a list too.\n */\nfunction shouldClearListContext(\n elementTag: string,\n element: HTMLElement,\n context: DomToModelContext\n) {\n return (\n context.listFormat.levels.length > 0 &&\n LIST_ELEMENT_TAGS.every(tag => tag != elementTag) &&\n !element.closest(LIST_ELEMENT_SELECTOR)\n );\n}\n\n/**\n * @internal\n * Convert pasted content from Office Online\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 * @param ev ContentModelBeforePasteEvent\n */\nexport function processPastedContentWacComponents(ev: ContentModelBeforePasteEvent) {\n addParser(ev.domToModelOption, 'segment', wacSubSuperParser);\n addParser(ev.domToModelOption, 'listItemThread', wacListItemParser);\n addParser(ev.domToModelOption, 'listLevel', wacListLevelParser);\n addParser(ev.domToModelOption, 'container', wacBlockParser);\n\n setProcessor(ev.domToModelOption, 'element', wacElementProcessor);\n setProcessor(ev.domToModelOption, 'li', wacLiElementProcessor);\n setProcessor(ev.domToModelOption, 'ol', wacListProcessor);\n setProcessor(ev.domToModelOption, 'ul', wacListProcessor);\n ev.sanitizingOption.additionalAllowedCssClasses.push(...CLASSES_TO_KEEP);\n}\n\n/**\n * List items from word have this format when using List items:\n * @example\n <div>\n <ol></ol>\n </div>\n <div>\n <ol></ol>\n </div>\n <div>\n <ol></ol>\n </div>\n * Due to this the div between each of the lists we need to restore the list context to use the previous list,\n * otherwise it could create a new list instead under the same list element\n */\nconst wacListProcessor: ElementProcessor<HTMLOListElement | HTMLUListElement> = (\n group: ContentModelBlockGroup,\n element: HTMLOListElement | HTMLUListElement,\n context: DomToModelContext\n): void => {\n const lastBlock = group.blocks[group.blocks.length - 1];\n const isWrappedInContainer = element.closest(`.${LIST_CONTAINER_ELEMENT_CLASS_NAME}`);\n if (\n isWrappedInContainer?.previousElementSibling?.classList.contains(\n LIST_CONTAINER_ELEMENT_CLASS_NAME\n )\n ) {\n if (lastBlock?.blockType === 'BlockGroup' && lastBlock.blockGroupType == 'ListItem') {\n context.listFormat = {\n threadItemCounts: [],\n levels: lastBlock.levels,\n listParent: group,\n };\n }\n }\n if (element.tagName.toUpperCase() === 'OL') {\n context.defaultElementProcessors.ol?.(group, element as HTMLOListElement, context);\n } else {\n context.defaultElementProcessors.ul?.(group, element as HTMLUListElement, context);\n }\n};\n\nconst wacBlockParser: FormatParser<ContentModelBlockFormat> = (\n format: ContentModelBlockFormat,\n element: HTMLElement\n) => {\n if (element.classList.contains(TABLE_CONTAINER) && element.style.marginLeft.startsWith('-')) {\n delete format.marginLeft;\n }\n};\n"]}
|
|
1
|
+
{"version":3,"file":"processPastedContentWacComponents.js","sourceRoot":"","sources":["../../../../../packages-content-model/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts"],"names":[],"mappings":"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","sourcesContent":["import addParser from '../utils/addParser';\nimport { setProcessor } from '../utils/setProcessor';\nimport {\n CLASSES_TO_KEEP,\n COMMENT_HIGHLIGHT_CLASS,\n COMMENT_HIGHLIGHT_CLICKED_CLASS,\n LIST_CONTAINER_ELEMENT_CLASS_NAME,\n TABLE_CONTAINER,\n TEMP_ELEMENTS_CLASSES,\n WAC_IDENTIFY_SELECTOR,\n} from './constants';\nimport type {\n ContentModelBeforePasteEvent,\n ContentModelBlockFormat,\n ContentModelBlockGroup,\n ContentModelListItemLevelFormat,\n ContentModelSegmentFormat,\n DomToModelContext,\n ElementProcessor,\n FormatParser,\n} from 'roosterjs-content-model-types';\n\nconst LIST_ELEMENT_TAGS = ['UL', 'OL', 'LI'];\nconst LIST_ELEMENT_SELECTOR = LIST_ELEMENT_TAGS.join(',');\n\nconst COMMENT_BG_COLOR_REST = 'rgba(209, 209, 209, 0.5)';\nconst COMMENTS_TEXT_HIGHLIGHT_CLICKED = 'rgba(197, 139, 204, 0.5)';\n\n/**\n * Wac components do not use sub and super tags, instead only add vertical align to a span.\n * This parser normalize the content for content model\n */\nconst wacSubSuperParser: FormatParser<ContentModelSegmentFormat> = (\n format: ContentModelSegmentFormat,\n element: HTMLElement\n): void => {\n const verticalAlign = element.style.verticalAlign;\n if (verticalAlign === 'super') {\n format.superOrSubScriptSequence = 'super';\n }\n if (verticalAlign === 'sub') {\n format.superOrSubScriptSequence = 'sub';\n }\n};\n\n/**\n * This processor does:\n * 1) Remove the display and margin of the element.\n * 2) When an element should be ignored but should handle the child elements call the default child processor.\n * 3) Removes the End of Paragraph element to avoid empty lines, we should only remove this if the previous element of the EOP is an EmptyTextRun\n * 4) Finally call the default processor.\n * @returns\n */\nconst wacElementProcessor: ElementProcessor<HTMLElement> = (\n group: ContentModelBlockGroup,\n element: HTMLElement,\n context: DomToModelContext\n): void => {\n const elementTag = element.tagName;\n\n if (element.matches(WAC_IDENTIFY_SELECTOR)) {\n element.style.removeProperty('display');\n element.style.removeProperty('margin');\n }\n\n if (element.classList.contains(LIST_CONTAINER_ELEMENT_CLASS_NAME)) {\n context.elementProcessors.child(group, element, context);\n return;\n }\n\n if (TEMP_ELEMENTS_CLASSES.some(className => element.classList.contains(className))) {\n return;\n } else if (shouldClearListContext(elementTag, element, context)) {\n const { listFormat } = context;\n listFormat.levels = [];\n listFormat.listParent = undefined;\n }\n\n context.defaultElementProcessors.element(group, element, context);\n};\n\n/**\n * This processor calls the default list processor and then sets the correct list level and list bullet.\n */\nconst wacLiElementProcessor: ElementProcessor<HTMLLIElement> = (\n group: ContentModelBlockGroup,\n element: HTMLLIElement,\n context: DomToModelContext\n): void => {\n context.defaultElementProcessors.li?.(group, element, context);\n const { listFormat } = context;\n const listParent = listFormat.listParent;\n if (listParent) {\n const lastblock = listParent.blocks[listParent.blocks.length - 1];\n if (\n lastblock.blockType == 'BlockGroup' &&\n lastblock.blockGroupType == 'ListItem' &&\n context.listFormat.listParent !== lastblock\n ) {\n const currentLevel = lastblock.levels[lastblock.levels.length - 1];\n\n // Get item level from 'data-aria-level' attribute\n const level = parseInt(element.getAttribute('data-aria-level') ?? '');\n if (level > 0) {\n if (level > lastblock.levels.length) {\n while (level != lastblock.levels.length) {\n lastblock.levels.push(currentLevel);\n }\n } else {\n lastblock.levels.splice(level, lastblock.levels.length - 1);\n lastblock.levels[level - 1] = currentLevel;\n }\n }\n }\n }\n};\n\n/**\n * This parsers does:\n * 1) Sets the display for dummy item to undefined when the current style is block.\n * 2) Removes the Margin Left\n */\nconst wacListItemParser: FormatParser<ContentModelListItemLevelFormat> = (\n format: ContentModelListItemLevelFormat,\n element: HTMLElement\n): void => {\n if (element.style.display === 'block') {\n format.displayForDummyItem = undefined;\n }\n\n format.marginLeft = undefined;\n};\n\n/**\n * Wac usually adds padding to lists which is unwanted so remove it.\n */\nconst wacListLevelParser: FormatParser<ContentModelListItemLevelFormat> = (\n format: ContentModelListItemLevelFormat\n): void => {\n format.marginLeft = undefined;\n format.paddingLeft = undefined;\n};\n\n/**\n * This function returns whether we need to clear the list format.\n * Word Online wraps lists inside divs to have this structure:\n *\n * <div class='ListContainerWrapper'>\n * <ol>...</ol>\n * </div>\n * <div>\n * <p>...</p>\n * <div>\n * <div class='ListContainerWrapper'>\n * <ol>...</ol>\n * </div>\n *\n * So if a elements is not contained inside of a list we should clear the list context to prevent normal text to be\n * transformed into list\n * For the above scenario, if we do not clear the format, the content inside of the second div would be transformed to a list too.\n */\nfunction shouldClearListContext(\n elementTag: string,\n element: HTMLElement,\n context: DomToModelContext\n) {\n return (\n context.listFormat.levels.length > 0 &&\n LIST_ELEMENT_TAGS.every(tag => tag != elementTag) &&\n !element.closest(LIST_ELEMENT_SELECTOR)\n );\n}\n\nconst wacCommentParser: FormatParser<ContentModelSegmentFormat> = (\n format: ContentModelSegmentFormat,\n element: HTMLElement\n): void => {\n if (\n (element.className.includes(COMMENT_HIGHLIGHT_CLASS) &&\n element.style.backgroundColor == COMMENT_BG_COLOR_REST) ||\n (element.className.includes(COMMENT_HIGHLIGHT_CLICKED_CLASS) &&\n element.style.backgroundColor == COMMENTS_TEXT_HIGHLIGHT_CLICKED)\n ) {\n delete format.backgroundColor;\n }\n};\n/**\n * @internal\n * Convert pasted content from Office Online\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 * @param ev ContentModelBeforePasteEvent\n */\nexport function processPastedContentWacComponents(ev: ContentModelBeforePasteEvent) {\n addParser(ev.domToModelOption, 'segment', wacSubSuperParser);\n addParser(ev.domToModelOption, 'listItemThread', wacListItemParser);\n addParser(ev.domToModelOption, 'listLevel', wacListLevelParser);\n addParser(ev.domToModelOption, 'container', wacBlockParser);\n addParser(ev.domToModelOption, 'segment', wacCommentParser);\n\n setProcessor(ev.domToModelOption, 'element', wacElementProcessor);\n setProcessor(ev.domToModelOption, 'li', wacLiElementProcessor);\n setProcessor(ev.domToModelOption, 'ol', wacListProcessor);\n setProcessor(ev.domToModelOption, 'ul', wacListProcessor);\n ev.sanitizingOption.additionalAllowedCssClasses.push(...CLASSES_TO_KEEP);\n}\n\n/**\n * List items from word have this format when using List items:\n * @example\n <div>\n <ol></ol>\n </div>\n <div>\n <ol></ol>\n </div>\n <div>\n <ol></ol>\n </div>\n * Due to this the div between each of the lists we need to restore the list context to use the previous list,\n * otherwise it could create a new list instead under the same list element\n */\nconst wacListProcessor: ElementProcessor<HTMLOListElement | HTMLUListElement> = (\n group: ContentModelBlockGroup,\n element: HTMLOListElement | HTMLUListElement,\n context: DomToModelContext\n): void => {\n const lastBlock = group.blocks[group.blocks.length - 1];\n const isWrappedInContainer = element.closest(`.${LIST_CONTAINER_ELEMENT_CLASS_NAME}`);\n if (\n isWrappedInContainer?.previousElementSibling?.classList.contains(\n LIST_CONTAINER_ELEMENT_CLASS_NAME\n )\n ) {\n if (lastBlock?.blockType === 'BlockGroup' && lastBlock.blockGroupType == 'ListItem') {\n context.listFormat = {\n threadItemCounts: [],\n levels: lastBlock.levels,\n listParent: group,\n };\n }\n }\n if (element.tagName.toUpperCase() === 'OL') {\n context.defaultElementProcessors.ol?.(group, element as HTMLOListElement, context);\n } else {\n context.defaultElementProcessors.ul?.(group, element as HTMLUListElement, context);\n }\n};\n\nconst wacBlockParser: FormatParser<ContentModelBlockFormat> = (\n format: ContentModelBlockFormat,\n element: HTMLElement\n) => {\n if (element.classList.contains(TABLE_CONTAINER) && element.style.marginLeft.startsWith('-')) {\n delete format.marginLeft;\n }\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WordMetadata.js","sourceRoot":"","sources":["../../../../../packages-content-model/roosterjs-content-model-plugins/lib/paste/WordDesktop/WordMetadata.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * @internal\n * Metadata used when processing word desktop lists\n */\nexport interface WordMetadata {\n readonly 'mso-level-number-format'?: string;\n readonly 'mso-level-start-at'?: string;\n readonly 'mso-level-text'?: string;\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { WordMetadata } from './WordMetadata';
|
|
2
|
+
import type { ContentModelBeforePasteEvent } from 'roosterjs-content-model-types';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
* Word Desktop content has a style tag that contains data for the lists.
|
|
6
|
+
* So this function query that style tag and extract the data from the innerHTML, since it is not available from the HTMLStyleElement.sheet.
|
|
7
|
+
*
|
|
8
|
+
* The format is like:
|
|
9
|
+
* example of style element content
|
|
10
|
+
* @list l0:level1 {
|
|
11
|
+
* styleTag: styleValue;
|
|
12
|
+
* ...
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* To extract the data:
|
|
16
|
+
* 1. Substring the value of the style selector, using @ index and { index
|
|
17
|
+
* 2. Substring the value of the style rules by Substring the content between { and }
|
|
18
|
+
* 3. Split the value of the rules using ; as separator { styleTag: styleValue; styleTag1: StyleValue1 } = ['styleTag: styleValue', 'styleTag1: StyleValue1']
|
|
19
|
+
* 4. Split the value of the rule using : as separator: styleTag: styleValue = [styleTag, styleValue]
|
|
20
|
+
* 5. Save data in record and only use the required information.
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
export default function getStyleMetadata(ev: ContentModelBeforePasteEvent, trustedHTMLHandler: (val: string) => string): Map<string, WordMetadata>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var tslib_1 = require("tslib");
|
|
4
|
+
var roosterjs_content_model_dom_1 = require("roosterjs-content-model-dom");
|
|
5
|
+
var FORMATING_REGEX = /[\n\t'{}"]+/g;
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
* Word Desktop content has a style tag that contains data for the lists.
|
|
9
|
+
* So this function query that style tag and extract the data from the innerHTML, since it is not available from the HTMLStyleElement.sheet.
|
|
10
|
+
*
|
|
11
|
+
* The format is like:
|
|
12
|
+
* example of style element content
|
|
13
|
+
* @list l0:level1 {
|
|
14
|
+
* styleTag: styleValue;
|
|
15
|
+
* ...
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* To extract the data:
|
|
19
|
+
* 1. Substring the value of the style selector, using @ index and { index
|
|
20
|
+
* 2. Substring the value of the style rules by Substring the content between { and }
|
|
21
|
+
* 3. Split the value of the rules using ; as separator { styleTag: styleValue; styleTag1: StyleValue1 } = ['styleTag: styleValue', 'styleTag1: StyleValue1']
|
|
22
|
+
* 4. Split the value of the rule using : as separator: styleTag: styleValue = [styleTag, styleValue]
|
|
23
|
+
* 5. Save data in record and only use the required information.
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
function getStyleMetadata(ev, trustedHTMLHandler) {
|
|
27
|
+
var metadataMap = new Map();
|
|
28
|
+
var doc = new DOMParser().parseFromString(trustedHTMLHandler(ev.htmlBefore), 'text/html');
|
|
29
|
+
var styles = doc.querySelectorAll('style');
|
|
30
|
+
styles.forEach(function (style) {
|
|
31
|
+
var text = (style === null || style === void 0 ? void 0 : style.innerHTML.trim()) || '';
|
|
32
|
+
var index = 0;
|
|
33
|
+
var _loop_1 = function () {
|
|
34
|
+
var indexAt = text.indexOf('@', index + 1);
|
|
35
|
+
var indexCurlyEnd = text.indexOf('}', indexAt);
|
|
36
|
+
var indexCurlyStart = text.indexOf('{', indexAt);
|
|
37
|
+
index = indexAt;
|
|
38
|
+
// 1.
|
|
39
|
+
var metadataName = text
|
|
40
|
+
.substring(indexAt + 1, indexCurlyStart)
|
|
41
|
+
.replace(FORMATING_REGEX, '')
|
|
42
|
+
.replace('list', '')
|
|
43
|
+
.trimRight()
|
|
44
|
+
.trimLeft();
|
|
45
|
+
// 2.
|
|
46
|
+
var dataName = text
|
|
47
|
+
.substring(indexCurlyStart, indexCurlyEnd + 1)
|
|
48
|
+
.trimLeft()
|
|
49
|
+
.trimRight();
|
|
50
|
+
var record = {};
|
|
51
|
+
// 3.
|
|
52
|
+
var entries = dataName.split(';');
|
|
53
|
+
entries.forEach(function (entry) {
|
|
54
|
+
// 4.
|
|
55
|
+
var _a = (0, tslib_1.__read)(entry.split(':'), 2), key = _a[0], value = _a[1];
|
|
56
|
+
if (key && value) {
|
|
57
|
+
var formatedKey = key.replace(FORMATING_REGEX, '').trimRight().trimLeft();
|
|
58
|
+
var formatedValue = value.replace(FORMATING_REGEX, '').trimRight().trimLeft();
|
|
59
|
+
// 5.
|
|
60
|
+
record[formatedKey] = formatedValue;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
var data = {
|
|
64
|
+
'mso-level-number-format': record['mso-level-number-format'],
|
|
65
|
+
'mso-level-start-at': record['mso-level-start-at'],
|
|
66
|
+
'mso-level-text': record['mso-level-text'],
|
|
67
|
+
};
|
|
68
|
+
if ((0, roosterjs_content_model_dom_1.getObjectKeys)(data).some(function (key) { return !!data[key]; })) {
|
|
69
|
+
metadataMap.set(metadataName, data);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
while (index >= 0) {
|
|
73
|
+
_loop_1();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return metadataMap;
|
|
77
|
+
}
|
|
78
|
+
exports.default = getStyleMetadata;
|
|
79
|
+
//# sourceMappingURL=getStyleMetadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getStyleMetadata.js","sourceRoot":"","sources":["../../../../../packages-content-model/roosterjs-content-model-plugins/lib/paste/WordDesktop/getStyleMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import { getObjectKeys } from 'roosterjs-content-model-dom';\nimport type { WordMetadata } from './WordMetadata';\nimport type { ContentModelBeforePasteEvent } from 'roosterjs-content-model-types';\n\nconst FORMATING_REGEX = /[\\n\\t'{}\"]+/g;\n\n/**\n * @internal\n * Word Desktop content has a style tag that contains data for the lists.\n * So this function query that style tag and extract the data from the innerHTML, since it is not available from the HTMLStyleElement.sheet.\n *\n * The format is like:\n * example of style element content\n * @list l0:level1 {\n * styleTag: styleValue;\n * ...\n * }\n *\n * To extract the data:\n * 1. Substring the value of the style selector, using @ index and { index\n * 2. Substring the value of the style rules by Substring the content between { and }\n * 3. Split the value of the rules using ; as separator { styleTag: styleValue; styleTag1: StyleValue1 } = ['styleTag: styleValue', 'styleTag1: StyleValue1']\n * 4. Split the value of the rule using : as separator: styleTag: styleValue = [styleTag, styleValue]\n * 5. Save data in record and only use the required information.\n *\n */\nexport default function getStyleMetadata(\n ev: ContentModelBeforePasteEvent,\n trustedHTMLHandler: (val: string) => string\n) {\n const metadataMap: Map<string, WordMetadata> = new Map();\n const doc = new DOMParser().parseFromString(trustedHTMLHandler(ev.htmlBefore), 'text/html');\n const styles = doc.querySelectorAll('style');\n\n styles.forEach(style => {\n const text = style?.innerHTML.trim() || '';\n\n let index = 0;\n while (index >= 0) {\n const indexAt = text.indexOf('@', index + 1);\n const indexCurlyEnd = text.indexOf('}', indexAt);\n const indexCurlyStart = text.indexOf('{', indexAt);\n index = indexAt;\n\n // 1.\n const metadataName = text\n .substring(indexAt + 1, indexCurlyStart)\n .replace(FORMATING_REGEX, '')\n .replace('list', '')\n .trimRight()\n .trimLeft();\n\n // 2.\n const dataName = text\n .substring(indexCurlyStart, indexCurlyEnd + 1)\n .trimLeft()\n .trimRight();\n const record: Record<string, string> = {};\n\n // 3.\n const entries = dataName.split(';');\n entries.forEach(entry => {\n // 4.\n const [key, value] = entry.split(':');\n if (key && value) {\n const formatedKey = key.replace(FORMATING_REGEX, '').trimRight().trimLeft();\n const formatedValue = value.replace(FORMATING_REGEX, '').trimRight().trimLeft();\n // 5.\n record[formatedKey] = formatedValue;\n }\n });\n\n const data: WordMetadata = {\n 'mso-level-number-format': record['mso-level-number-format'],\n 'mso-level-start-at': record['mso-level-start-at'],\n 'mso-level-text': record['mso-level-text'],\n };\n if (getObjectKeys(data).some(key => !!data[key])) {\n metadataMap.set(metadataName, data);\n }\n }\n });\n return metadataMap;\n}\n"]}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import type { ContentModelBeforePasteEvent
|
|
1
|
+
import type { ContentModelBeforePasteEvent } from 'roosterjs-content-model-types';
|
|
2
2
|
/**
|
|
3
3
|
* @internal
|
|
4
4
|
* Handles Pasted content when source is Word Desktop
|
|
5
5
|
* @param ev ContentModelBeforePasteEvent
|
|
6
6
|
*/
|
|
7
|
-
export declare function processPastedContentFromWordDesktop(ev: ContentModelBeforePasteEvent): void;
|
|
8
|
-
/**
|
|
9
|
-
* @internal
|
|
10
|
-
* Exported only for unit test
|
|
11
|
-
*/
|
|
12
|
-
export declare const wordDesktopElementProcessor: ElementProcessor<HTMLElement>;
|
|
7
|
+
export declare function processPastedContentFromWordDesktop(ev: ContentModelBeforePasteEvent, trustedHTMLHandler: (text: string) => string): void;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.processPastedContentFromWordDesktop = void 0;
|
|
4
4
|
var addParser_1 = require("../utils/addParser");
|
|
5
|
+
var getStyleMetadata_1 = require("./getStyleMetadata");
|
|
5
6
|
var roosterjs_editor_dom_1 = require("roosterjs-editor-dom");
|
|
6
7
|
var getStyles_1 = require("../utils/getStyles");
|
|
7
8
|
var roosterjs_content_model_dom_1 = require("roosterjs-content-model-dom");
|
|
@@ -15,8 +16,9 @@ var DEFAULT_BROWSER_LINE_HEIGHT_PERCENTAGE = 120;
|
|
|
15
16
|
* Handles Pasted content when source is Word Desktop
|
|
16
17
|
* @param ev ContentModelBeforePasteEvent
|
|
17
18
|
*/
|
|
18
|
-
function processPastedContentFromWordDesktop(ev) {
|
|
19
|
-
(0,
|
|
19
|
+
function processPastedContentFromWordDesktop(ev, trustedHTMLHandler) {
|
|
20
|
+
var metadataMap = (0, getStyleMetadata_1.default)(ev, trustedHTMLHandler);
|
|
21
|
+
(0, setProcessor_1.setProcessor)(ev.domToModelOption, 'element', wordDesktopElementProcessor(metadataMap));
|
|
20
22
|
(0, addParser_1.default)(ev.domToModelOption, 'block', removeNonValidLineHeight);
|
|
21
23
|
(0, addParser_1.default)(ev.domToModelOption, 'listLevel', listLevelParser);
|
|
22
24
|
(0, addParser_1.default)(ev.domToModelOption, 'listItemElement', listItemElementParser);
|
|
@@ -32,18 +34,16 @@ function processPastedContentFromWordDesktop(ev) {
|
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
exports.processPastedContentFromWordDesktop = processPastedContentFromWordDesktop;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
37
|
+
var wordDesktopElementProcessor = function (metadataKey) {
|
|
38
|
+
return function (group, element, context) {
|
|
39
|
+
var styles = (0, getStyles_1.getStyles)(element);
|
|
40
|
+
// Process Word Lists or Word Commands, otherwise use the default processor on this element.
|
|
41
|
+
if (!((0, processWordLists_1.processWordList)(styles, group, element, context, metadataKey) ||
|
|
42
|
+
(0, processWordComments_1.processWordComments)(styles, element))) {
|
|
43
|
+
context.defaultElementProcessors.element(group, element, context);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
45
46
|
};
|
|
46
|
-
exports.wordDesktopElementProcessor = wordDesktopElementProcessor;
|
|
47
47
|
function removeNonValidLineHeight(format, element, context, defaultStyle) {
|
|
48
48
|
//If the line height is less than the browser default line height, line between the text is going to be too narrow
|
|
49
49
|
var parsedLineHeight;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processPastedContentFromWordDesktop.js","sourceRoot":"","sources":["../../../../../packages-content-model/roosterjs-content-model-plugins/lib/paste/WordDesktop/processPastedContentFromWordDesktop.ts"],"names":[],"mappings":";;;AAAA,gDAA2C;AAC3C,6DAA8D;AAC9D,gDAA+C;AAC/C,2EAA6D;AAC7D,6DAA4D;AAC5D,uDAAqD;AACrD,sDAAqD;
|
|
1
|
+
{"version":3,"file":"processPastedContentFromWordDesktop.js","sourceRoot":"","sources":["../../../../../packages-content-model/roosterjs-content-model-plugins/lib/paste/WordDesktop/processPastedContentFromWordDesktop.ts"],"names":[],"mappings":"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","sourcesContent":["import addParser from '../utils/addParser';\nimport getStyleMetadata from './getStyleMetadata';\nimport { chainSanitizerCallback } from 'roosterjs-editor-dom';\nimport { getStyles } from '../utils/getStyles';\nimport { moveChildNodes } from 'roosterjs-content-model-dom';\nimport { processWordComments } from './processWordComments';\nimport { processWordList } from './processWordLists';\nimport { setProcessor } from '../utils/setProcessor';\nimport type { WordMetadata } from './WordMetadata';\nimport type {\n ContentModelBeforePasteEvent,\n ContentModelBlockFormat,\n ContentModelListItemFormat,\n ContentModelListItemLevelFormat,\n DomToModelContext,\n ElementProcessor,\n FormatParser,\n} from 'roosterjs-content-model-types';\n\nconst PERCENTAGE_REGEX = /%/;\nconst DEFAULT_BROWSER_LINE_HEIGHT_PERCENTAGE = 120;\n\n/**\n * @internal\n * Handles Pasted content when source is Word Desktop\n * @param ev ContentModelBeforePasteEvent\n */\nexport function processPastedContentFromWordDesktop(\n ev: ContentModelBeforePasteEvent,\n trustedHTMLHandler: (text: string) => string\n) {\n const metadataMap: Map<string, WordMetadata> = getStyleMetadata(ev, trustedHTMLHandler);\n\n setProcessor(ev.domToModelOption, 'element', wordDesktopElementProcessor(metadataMap));\n addParser(ev.domToModelOption, 'block', removeNonValidLineHeight);\n addParser(ev.domToModelOption, 'listLevel', listLevelParser);\n addParser(ev.domToModelOption, 'listItemElement', listItemElementParser);\n\n // Remove \"border:none\" for image to fix image resize behavior\n // We found a problem that when paste an image with \"border:none\" then the resize border will be\n // displayed incorrectly when resize it. So we need to drop this style\n chainSanitizerCallback(\n ev.sanitizingOption.cssStyleCallbacks,\n 'border',\n (value, element) => element.tagName != 'IMG' || value != 'none'\n );\n\n // Preserve <o:p> when its innerHTML is \" \" to avoid dropping an empty line\n chainSanitizerCallback(ev.sanitizingOption.elementCallbacks, 'O:P', element => {\n moveChildNodes(element);\n element.appendChild(element.ownerDocument.createTextNode('\\u00A0')); // \n return true;\n });\n}\n\nconst wordDesktopElementProcessor = (\n metadataKey: Map<string, WordMetadata>\n): ElementProcessor<HTMLElement> => {\n return (group, element, context) => {\n const styles = getStyles(element);\n // Process Word Lists or Word Commands, otherwise use the default processor on this element.\n if (\n !(\n processWordList(styles, group, element, context, metadataKey) ||\n processWordComments(styles, element)\n )\n ) {\n context.defaultElementProcessors.element(group, element, context);\n }\n };\n};\n\nfunction removeNonValidLineHeight(\n format: ContentModelBlockFormat,\n element: HTMLElement,\n context: DomToModelContext,\n defaultStyle: Readonly<Partial<CSSStyleDeclaration>>\n): void {\n //If the line height is less than the browser default line height, line between the text is going to be too narrow\n let parsedLineHeight: number;\n if (\n PERCENTAGE_REGEX.test(element.style.lineHeight) &&\n !isNaN((parsedLineHeight = parseInt(element.style.lineHeight))) &&\n parsedLineHeight < DEFAULT_BROWSER_LINE_HEIGHT_PERCENTAGE\n ) {\n format.lineHeight = defaultStyle.lineHeight;\n }\n}\n\nfunction listLevelParser(\n format: ContentModelListItemLevelFormat,\n element: HTMLElement,\n context: DomToModelContext,\n defaultStyle: Readonly<Partial<CSSStyleDeclaration>>\n): void {\n if (element.style.marginLeft != '') {\n format.marginLeft = defaultStyle.marginLeft;\n }\n\n format.marginBottom = undefined;\n}\n\nconst listItemElementParser: FormatParser<ContentModelListItemFormat> = (\n format: ContentModelListItemFormat,\n element: HTMLElement\n): void => {\n if (element.style.marginLeft) {\n format.marginLeft = undefined;\n }\n if (element.style.marginRight) {\n format.marginRight = undefined;\n }\n};\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WordMetadata } from './WordMetadata';
|
|
1
2
|
import type { ContentModelBlockGroup, DomToModelContext } from 'roosterjs-content-model-types';
|
|
2
3
|
/**
|
|
3
4
|
* @internal
|
|
@@ -7,4 +8,4 @@ import type { ContentModelBlockGroup, DomToModelContext } from 'roosterjs-conten
|
|
|
7
8
|
* @param context
|
|
8
9
|
* @returns
|
|
9
10
|
*/
|
|
10
|
-
export declare function processWordList(styles: Record<string, string>, group: ContentModelBlockGroup, element: HTMLElement, context: DomToModelContext): boolean;
|
|
11
|
+
export declare function processWordList(styles: Record<string, string>, group: ContentModelBlockGroup, element: HTMLElement, context: DomToModelContext, metadata: Map<string, WordMetadata>): boolean;
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.processWordList = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
|
-
var
|
|
5
|
+
var roosterjs_content_model_core_1 = require("roosterjs-content-model-core");
|
|
6
6
|
var roosterjs_content_model_dom_1 = require("roosterjs-content-model-dom");
|
|
7
7
|
/** Word list metadata style name */
|
|
8
8
|
var MSO_LIST = 'mso-list';
|
|
9
9
|
var MSO_LIST_IGNORE = 'ignore';
|
|
10
|
-
var LOOKUP_DEPTH = 5;
|
|
11
10
|
var WORD_FIRST_LIST = 'l0';
|
|
11
|
+
var TEMPLATE_VALUE_REGEX = /%[0-9a-zA-Z]+/g;
|
|
12
|
+
var BULLET_METADATA = 'bullet';
|
|
12
13
|
/**
|
|
13
14
|
* @internal
|
|
14
15
|
* @param styles
|
|
@@ -17,7 +18,8 @@ var WORD_FIRST_LIST = 'l0';
|
|
|
17
18
|
* @param context
|
|
18
19
|
* @returns
|
|
19
20
|
*/
|
|
20
|
-
function processWordList(styles, group, element, context) {
|
|
21
|
+
function processWordList(styles, group, element, context, metadata) {
|
|
22
|
+
var _a;
|
|
21
23
|
var listFormat = context.listFormat;
|
|
22
24
|
if (!listFormat.wordKnownLevels) {
|
|
23
25
|
listFormat.wordKnownLevels = new Map();
|
|
@@ -28,19 +30,22 @@ function processWordList(styles, group, element, context) {
|
|
|
28
30
|
if (wordListStyle.toLowerCase() === MSO_LIST_IGNORE) {
|
|
29
31
|
return true;
|
|
30
32
|
}
|
|
31
|
-
var
|
|
33
|
+
var _b = (0, tslib_1.__read)(wordListStyle.split(' '), 2), lNumber = _b[0], level = _b[1];
|
|
32
34
|
// Try get the list metadata from word, which follows this format: l1 level1 lfo2
|
|
33
35
|
// If we are able to get the level property means we can process this element to be a list
|
|
34
|
-
listFormat.wordLevel =
|
|
35
|
-
listFormat.wordList =
|
|
36
|
+
listFormat.wordLevel = level && parseInt(level.substr('level'.length));
|
|
37
|
+
listFormat.wordList = lNumber || WORD_FIRST_LIST;
|
|
36
38
|
if (listFormat.levels.length == 0) {
|
|
37
|
-
listFormat.levels =
|
|
39
|
+
listFormat.levels =
|
|
40
|
+
(listFormat.wordList && listFormat.wordKnownLevels.get(listFormat.wordList)) || [];
|
|
38
41
|
}
|
|
39
42
|
if (wordListStyle && group && typeof listFormat.wordLevel === 'number') {
|
|
40
|
-
var wordLevel = listFormat.wordLevel;
|
|
43
|
+
var wordLevel = listFormat.wordLevel, wordList = listFormat.wordList;
|
|
41
44
|
// Retrieve the Fake bullet on the element and also the list type
|
|
42
|
-
var
|
|
43
|
-
var listType =
|
|
45
|
+
var listMetadata = metadata.get(lNumber + ":" + level);
|
|
46
|
+
var listType = ((_a = listMetadata === null || listMetadata === void 0 ? void 0 : listMetadata['mso-level-number-format']) === null || _a === void 0 ? void 0 : _a.toLowerCase()) != BULLET_METADATA
|
|
47
|
+
? 'OL'
|
|
48
|
+
: 'UL';
|
|
44
49
|
// Create the new level of the list item and parse the format
|
|
45
50
|
var newLevel = (0, roosterjs_content_model_dom_1.createListLevel)(listType);
|
|
46
51
|
(0, roosterjs_content_model_dom_1.parseFormat)(element, context.formatParsers.listLevel, newLevel.format, context);
|
|
@@ -56,100 +61,93 @@ function processWordList(styles, group, element, context) {
|
|
|
56
61
|
listFormat.levels[wordLevel - 1] = newLevel;
|
|
57
62
|
}
|
|
58
63
|
listFormat.listParent = group;
|
|
59
|
-
processAsListItem(listFormat, context, element, group,
|
|
64
|
+
processAsListItem(listFormat, context, element, group, listMetadata);
|
|
60
65
|
if (listFormat.levels.length > 0 &&
|
|
61
|
-
listFormat.wordKnownLevels.get(
|
|
62
|
-
listFormat.wordKnownLevels.set(
|
|
66
|
+
listFormat.wordKnownLevels.get(wordList) != listFormat.levels) {
|
|
67
|
+
listFormat.wordKnownLevels.set(wordList, (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(listFormat.levels), false));
|
|
63
68
|
}
|
|
64
69
|
return true;
|
|
65
70
|
}
|
|
66
71
|
return false;
|
|
67
72
|
}
|
|
68
73
|
exports.processWordList = processWordList;
|
|
69
|
-
function processAsListItem(listFormat, context, element, group,
|
|
74
|
+
function processAsListItem(listFormat, context, element, group, listMetadata) {
|
|
75
|
+
var listLevel = listFormat.levels[listFormat.levels.length - 1];
|
|
76
|
+
var listType = listLevel.listType;
|
|
77
|
+
var bullet = getBulletFromMetadata(listMetadata, listType);
|
|
78
|
+
if (bullet) {
|
|
79
|
+
(0, roosterjs_content_model_core_1.updateListMetadata)(listFormat.levels[listFormat.levels.length - 1], function (metadata) {
|
|
80
|
+
return Object.assign({}, metadata, {
|
|
81
|
+
unorderedStyleType: listType == 'UL' ? bullet : undefined,
|
|
82
|
+
orderedStyleType: listType == 'OL' ? bullet : undefined,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
70
86
|
var listItem = (0, roosterjs_content_model_dom_1.createListItem)(listFormat.levels, context.segmentFormat);
|
|
71
|
-
var lastLevel = listItem.levels[listItem.levels.length - 1];
|
|
72
87
|
(0, roosterjs_content_model_dom_1.parseFormat)(element, context.formatParsers.segmentOnBlock, context.segmentFormat, context);
|
|
73
88
|
(0, roosterjs_content_model_dom_1.parseFormat)(element, context.formatParsers.listItemElement, listItem.format, context);
|
|
74
|
-
if (
|
|
75
|
-
(0, roosterjs_content_model_dom_1.parseFormat)(element, [startNumberOverrideParser(
|
|
89
|
+
if (listType == 'OL') {
|
|
90
|
+
(0, roosterjs_content_model_dom_1.parseFormat)(element, [startNumberOverrideParser(listMetadata)], listItem.levels[listItem.levels.length - 1].format, context);
|
|
76
91
|
}
|
|
77
92
|
context.elementProcessors.child(listItem, element, context);
|
|
78
93
|
(0, roosterjs_content_model_dom_1.addBlock)(group, listItem);
|
|
79
94
|
}
|
|
80
|
-
function
|
|
95
|
+
function getBulletFromMetadata(listMetadata, listType) {
|
|
96
|
+
var templateType = (listMetadata === null || listMetadata === void 0 ? void 0 : listMetadata['mso-level-number-format']) || 'decimal';
|
|
97
|
+
var templateFinal;
|
|
98
|
+
if (listMetadata === null || listMetadata === void 0 ? void 0 : listMetadata['mso-level-text']) {
|
|
99
|
+
var templateValue = '';
|
|
100
|
+
switch (templateType) {
|
|
101
|
+
case 'alpha-upper':
|
|
102
|
+
templateValue = 'UpperAlpha';
|
|
103
|
+
break;
|
|
104
|
+
case 'alpha-lower':
|
|
105
|
+
templateValue = 'LowerAlpha';
|
|
106
|
+
break;
|
|
107
|
+
case 'roman-lower':
|
|
108
|
+
templateValue = 'LowerRoman';
|
|
109
|
+
break;
|
|
110
|
+
case 'roman-upper':
|
|
111
|
+
templateValue = 'UpperRoman';
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
templateValue = 'Number';
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
var template = (listMetadata['mso-level-text'] || '')
|
|
118
|
+
.replace('\\', '')
|
|
119
|
+
.replace('"', '')
|
|
120
|
+
.replace(TEMPLATE_VALUE_REGEX, '${' + templateValue + '}');
|
|
121
|
+
templateFinal = '"' + template + ' "';
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
switch (templateType) {
|
|
125
|
+
case 'alpha-lower':
|
|
126
|
+
templateFinal = 'lower-alpha';
|
|
127
|
+
break;
|
|
128
|
+
case 'roman-lower':
|
|
129
|
+
templateFinal = 'lower-roman';
|
|
130
|
+
break;
|
|
131
|
+
case 'roman-upper':
|
|
132
|
+
templateFinal = 'upper-roman';
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
templateFinal = 'decimal';
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return (0, roosterjs_content_model_core_1.getListStyleTypeFromString)(listType, templateFinal);
|
|
140
|
+
}
|
|
141
|
+
function startNumberOverrideParser(listMetadata) {
|
|
81
142
|
return function (format, _, context) {
|
|
82
|
-
var _a = context.listFormat, wordKnownLevels = _a.wordKnownLevels, wordLevel = _a.wordLevel, wordList = _a.wordList;
|
|
143
|
+
var _a = context.listFormat, wordKnownLevels = _a.wordKnownLevels, wordLevel = _a.wordLevel, wordList = _a.wordList, levels = _a.levels;
|
|
83
144
|
if (typeof wordLevel === 'number' && wordList) {
|
|
84
|
-
var start = parseInt(
|
|
85
|
-
|
|
145
|
+
var start = parseInt((listMetadata === null || listMetadata === void 0 ? void 0 : listMetadata['mso-level-start-at']) || '1');
|
|
146
|
+
var knownLevel = (wordKnownLevels === null || wordKnownLevels === void 0 ? void 0 : wordKnownLevels.get(wordList)) || [];
|
|
147
|
+
if (start != undefined && !isNaN(start) && knownLevel.length != levels.length) {
|
|
86
148
|
format.startNumberOverride = start;
|
|
87
149
|
}
|
|
88
150
|
}
|
|
89
151
|
};
|
|
90
152
|
}
|
|
91
|
-
/**
|
|
92
|
-
* Check whether the string is a fake bullet from word Desktop
|
|
93
|
-
*/
|
|
94
|
-
function isFakeBullet(fakeBullet) {
|
|
95
|
-
return ['o', '·', '§', '-'].indexOf(fakeBullet) >= 0;
|
|
96
|
-
}
|
|
97
|
-
/** Given a fake bullet text, returns the type of list that should be used for it */
|
|
98
|
-
function getFakeBulletTagName(fakeBullet) {
|
|
99
|
-
return isFakeBullet(fakeBullet) ? 'UL' : 'OL';
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Finds the fake bullet text out of the specified node and returns it. For images, it will return
|
|
103
|
-
* a bullet string. If not found, it returns null...
|
|
104
|
-
*/
|
|
105
|
-
function getFakeBulletText(node, levels) {
|
|
106
|
-
var _a, _b;
|
|
107
|
-
// Word uses the following format for their bullets:
|
|
108
|
-
// <p style="mso-list:l1 level1 lfo2">
|
|
109
|
-
// <span style="...">
|
|
110
|
-
// <span style="mso-list:Ignore">1.<span style="..."> </span></span>
|
|
111
|
-
// </span>
|
|
112
|
-
// Content here...
|
|
113
|
-
// </p>
|
|
114
|
-
//
|
|
115
|
-
// Basically, we need to locate the mso-list:Ignore SPAN, which holds either one text or image node. That
|
|
116
|
-
// text or image node will be the fake bullet we are looking for
|
|
117
|
-
var result = '';
|
|
118
|
-
levels = levels || LOOKUP_DEPTH;
|
|
119
|
-
var child = node.firstChild;
|
|
120
|
-
while (!result && child) {
|
|
121
|
-
// Check if this is the node that holds the fake bullets (mso-list: Ignore)
|
|
122
|
-
if (isIgnoreNode(child)) {
|
|
123
|
-
// Yes... this is the node that holds either the text or image data
|
|
124
|
-
result = (_b = (_a = child.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : '';
|
|
125
|
-
// This is the case for image case
|
|
126
|
-
if (result.length == 0) {
|
|
127
|
-
result = 'o';
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
else if ((0, roosterjs_content_model_dom_1.isNodeOfType)(child, 'ELEMENT_NODE') && levels > 1) {
|
|
131
|
-
// If this is an element and we are not in the last level, try to get the fake bullet
|
|
132
|
-
// out of the child
|
|
133
|
-
result = getFakeBulletText(child, levels - 1);
|
|
134
|
-
}
|
|
135
|
-
child = child.nextSibling;
|
|
136
|
-
}
|
|
137
|
-
return result;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Checks if the specified node is marked as a mso-list: Ignore. These
|
|
141
|
-
* nodes need to be ignored when a list item is converted into standard
|
|
142
|
-
* HTML lists
|
|
143
|
-
*/
|
|
144
|
-
function isIgnoreNode(node) {
|
|
145
|
-
if ((0, roosterjs_content_model_dom_1.isNodeOfType)(node, 'ELEMENT_NODE')) {
|
|
146
|
-
var listAttribute = (0, getStyles_1.getStyles)(node)[MSO_LIST];
|
|
147
|
-
if (listAttribute &&
|
|
148
|
-
listAttribute.length > 0 &&
|
|
149
|
-
listAttribute.trim().toLowerCase() == MSO_LIST_IGNORE) {
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
153
|
//# sourceMappingURL=processWordLists.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processWordLists.js","sourceRoot":"","sources":["../../../../../packages-content-model/roosterjs-content-model-plugins/lib/paste/WordDesktop/processWordLists.ts"],"names":[],"mappings":"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","sourcesContent":["import { getStyles } from '../utils/getStyles';\nimport {\n addBlock,\n createListItem,\n createListLevel,\n isNodeOfType,\n parseFormat,\n} from 'roosterjs-content-model-dom';\nimport type {\n ContentModelBlockGroup,\n ContentModelListItemLevelFormat,\n ContentModelListLevel,\n DomToModelContext,\n DomToModelListFormat,\n FormatParser,\n} from 'roosterjs-content-model-types';\n\n/** Word list metadata style name */\nconst MSO_LIST = 'mso-list';\nconst MSO_LIST_IGNORE = 'ignore';\nconst LOOKUP_DEPTH = 5;\nconst WORD_FIRST_LIST = 'l0';\n\ninterface WordDesktopListFormat extends DomToModelListFormat {\n wordLevel?: number | '';\n wordList?: string;\n wordKnownLevels?: Map<string, ContentModelListLevel[]>;\n}\n\n/**\n * @internal\n * @param styles\n * @param group\n * @param element\n * @param context\n * @returns\n */\nexport function processWordList(\n styles: Record<string, string>,\n group: ContentModelBlockGroup,\n element: HTMLElement,\n context: DomToModelContext\n) {\n const listFormat = context.listFormat as WordDesktopListFormat;\n if (!listFormat.wordKnownLevels) {\n listFormat.wordKnownLevels = new Map<string, ContentModelListLevel[]>();\n }\n const wordListStyle = styles[MSO_LIST] || '';\n\n // If the element contains Ignore style, do not process it,\n // Usually this element contains the fake bullet used in Word Desktop.\n if (wordListStyle.toLowerCase() === MSO_LIST_IGNORE) {\n return true;\n }\n\n const listProps = wordListStyle.split(' ');\n // Try get the list metadata from word, which follows this format: l1 level1 lfo2\n // If we are able to get the level property means we can process this element to be a list\n listFormat.wordLevel = listProps[1] && parseInt(listProps[1].substr('level'.length));\n\n listFormat.wordList = listProps[0] || WORD_FIRST_LIST;\n if (listFormat.levels.length == 0) {\n listFormat.levels = listFormat.wordKnownLevels.get(listFormat.wordList) || [];\n }\n\n if (wordListStyle && group && typeof listFormat.wordLevel === 'number') {\n const { wordLevel } = listFormat;\n // Retrieve the Fake bullet on the element and also the list type\n const fakeBullet = getFakeBulletText(element);\n const listType = getFakeBulletTagName(fakeBullet);\n\n // Create the new level of the list item and parse the format\n const newLevel: ContentModelListLevel = createListLevel(listType);\n parseFormat(element, context.formatParsers.listLevel, newLevel.format, context);\n\n // If the list format is in a different level, update the array so we get the new item\n // To be in the same level as the provided level metadata.\n if (wordLevel > listFormat.levels.length) {\n while (wordLevel != listFormat.levels.length) {\n listFormat.levels.push(newLevel);\n }\n } else {\n listFormat.levels.splice(wordLevel, listFormat.levels.length - 1);\n listFormat.levels[wordLevel - 1] = newLevel;\n }\n\n listFormat.listParent = group;\n\n processAsListItem(listFormat, context, element, group, fakeBullet);\n\n if (\n listFormat.levels.length > 0 &&\n listFormat.wordKnownLevels.get(listFormat.wordList) != listFormat.levels\n ) {\n listFormat.wordKnownLevels.set(listFormat.wordList, [...listFormat.levels]);\n }\n return true;\n }\n\n return false;\n}\n\nfunction processAsListItem(\n listFormat: WordDesktopListFormat,\n context: DomToModelContext,\n element: HTMLElement,\n group: ContentModelBlockGroup,\n fakeBullet: string\n) {\n const listItem = createListItem(listFormat.levels, context.segmentFormat);\n const lastLevel = listItem.levels[listItem.levels.length - 1];\n\n parseFormat(element, context.formatParsers.segmentOnBlock, context.segmentFormat, context);\n parseFormat(element, context.formatParsers.listItemElement, listItem.format, context);\n\n if (lastLevel?.listType == 'OL') {\n parseFormat(\n element,\n [startNumberOverrideParser(fakeBullet)],\n listItem.levels[listItem.levels.length - 1].format,\n context\n );\n }\n\n context.elementProcessors.child(listItem, element, context);\n addBlock(group, listItem);\n}\n\nfunction startNumberOverrideParser(\n fakeBullet: string\n): FormatParser<ContentModelListItemLevelFormat> | null {\n return (format, _, context) => {\n const {\n wordKnownLevels,\n wordLevel,\n wordList,\n } = context.listFormat as WordDesktopListFormat;\n if (typeof wordLevel === 'number' && wordList) {\n const start = parseInt(fakeBullet);\n if (start != undefined && !isNaN(start) && !wordKnownLevels?.has(wordList)) {\n format.startNumberOverride = start;\n }\n }\n };\n}\n\n/**\n * Check whether the string is a fake bullet from word Desktop\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): 'UL' | 'OL' {\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 = '';\n levels = levels || LOOKUP_DEPTH;\n let child: Node | null = node.firstChild;\n while (!result && child) {\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 (isNodeOfType(child, 'ELEMENT_NODE') && 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 * 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 (isNodeOfType(node, 'ELEMENT_NODE')) {\n const listAttribute = getStyles(node as HTMLElement)[MSO_LIST];\n if (\n listAttribute &&\n listAttribute.length > 0 &&\n listAttribute.trim().toLowerCase() == MSO_LIST_IGNORE\n ) {\n return true;\n }\n }\n\n return false;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"processWordLists.js","sourceRoot":"","sources":["../../../../../packages-content-model/roosterjs-content-model-plugins/lib/paste/WordDesktop/processWordLists.ts"],"names":[],"mappings":"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","sourcesContent":["import { getListStyleTypeFromString, updateListMetadata } from 'roosterjs-content-model-core';\nimport type { WordMetadata } from './WordMetadata';\nimport {\n addBlock,\n createListItem,\n createListLevel,\n parseFormat,\n} from 'roosterjs-content-model-dom';\nimport type {\n ContentModelBlockGroup,\n ContentModelListItemLevelFormat,\n ContentModelListLevel,\n DomToModelContext,\n DomToModelListFormat,\n FormatParser,\n} from 'roosterjs-content-model-types';\n\n/** Word list metadata style name */\nconst MSO_LIST = 'mso-list';\nconst MSO_LIST_IGNORE = 'ignore';\nconst WORD_FIRST_LIST = 'l0';\n\nconst TEMPLATE_VALUE_REGEX = /%[0-9a-zA-Z]+/g;\n\ninterface WordDesktopListFormat extends DomToModelListFormat {\n wordLevel?: number | '';\n wordList?: string;\n wordKnownLevels?: Map<string, ContentModelListLevel[]>;\n}\n\nconst BULLET_METADATA = 'bullet';\n/**\n * @internal\n * @param styles\n * @param group\n * @param element\n * @param context\n * @returns\n */\nexport function processWordList(\n styles: Record<string, string>,\n group: ContentModelBlockGroup,\n element: HTMLElement,\n context: DomToModelContext,\n metadata: Map<string, WordMetadata>\n) {\n const listFormat = context.listFormat as WordDesktopListFormat;\n if (!listFormat.wordKnownLevels) {\n listFormat.wordKnownLevels = new Map<string, ContentModelListLevel[]>();\n }\n const wordListStyle = styles[MSO_LIST] || '';\n\n // If the element contains Ignore style, do not process it,\n // Usually this element contains the fake bullet used in Word Desktop.\n if (wordListStyle.toLowerCase() === MSO_LIST_IGNORE) {\n return true;\n }\n\n const [lNumber, level] = wordListStyle.split(' ');\n // Try get the list metadata from word, which follows this format: l1 level1 lfo2\n // If we are able to get the level property means we can process this element to be a list\n listFormat.wordLevel = level && parseInt(level.substr('level'.length));\n\n listFormat.wordList = lNumber || WORD_FIRST_LIST;\n if (listFormat.levels.length == 0) {\n listFormat.levels =\n (listFormat.wordList && listFormat.wordKnownLevels.get(listFormat.wordList)) || [];\n }\n\n if (wordListStyle && group && typeof listFormat.wordLevel === 'number') {\n const { wordLevel, wordList } = listFormat;\n // Retrieve the Fake bullet on the element and also the list type\n const listMetadata = metadata.get(`${lNumber}:${level}`);\n const listType =\n listMetadata?.['mso-level-number-format']?.toLowerCase() != BULLET_METADATA\n ? 'OL'\n : 'UL';\n\n // Create the new level of the list item and parse the format\n const newLevel: ContentModelListLevel = createListLevel(listType);\n parseFormat(element, context.formatParsers.listLevel, newLevel.format, context);\n\n // If the list format is in a different level, update the array so we get the new item\n // To be in the same level as the provided level metadata.\n if (wordLevel > listFormat.levels.length) {\n while (wordLevel != listFormat.levels.length) {\n listFormat.levels.push(newLevel);\n }\n } else {\n listFormat.levels.splice(wordLevel, listFormat.levels.length - 1);\n listFormat.levels[wordLevel - 1] = newLevel;\n }\n\n listFormat.listParent = group;\n\n processAsListItem(listFormat, context, element, group, listMetadata);\n\n if (\n listFormat.levels.length > 0 &&\n listFormat.wordKnownLevels.get(wordList) != listFormat.levels\n ) {\n listFormat.wordKnownLevels.set(wordList, [...listFormat.levels]);\n }\n return true;\n }\n\n return false;\n}\n\nfunction processAsListItem(\n listFormat: WordDesktopListFormat,\n context: DomToModelContext,\n element: HTMLElement,\n group: ContentModelBlockGroup,\n listMetadata: WordMetadata | undefined\n) {\n const listLevel = listFormat.levels[listFormat.levels.length - 1];\n const { listType } = listLevel;\n const bullet = getBulletFromMetadata(listMetadata, listType);\n if (bullet) {\n updateListMetadata(listFormat.levels[listFormat.levels.length - 1], metadata =>\n Object.assign({}, metadata, {\n unorderedStyleType: listType == 'UL' ? bullet : undefined,\n orderedStyleType: listType == 'OL' ? bullet : undefined,\n })\n );\n }\n\n const listItem = createListItem(listFormat.levels, context.segmentFormat);\n\n parseFormat(element, context.formatParsers.segmentOnBlock, context.segmentFormat, context);\n parseFormat(element, context.formatParsers.listItemElement, listItem.format, context);\n\n if (listType == 'OL') {\n parseFormat(\n element,\n [startNumberOverrideParser(listMetadata)],\n listItem.levels[listItem.levels.length - 1].format,\n context\n );\n }\n\n context.elementProcessors.child(listItem, element, context);\n addBlock(group, listItem);\n}\n\nfunction getBulletFromMetadata(listMetadata: WordMetadata | undefined, listType: 'OL' | 'UL') {\n const templateType = listMetadata?.['mso-level-number-format'] || 'decimal';\n let templateFinal: string;\n\n if (listMetadata?.['mso-level-text']) {\n let templateValue: string = '';\n switch (templateType) {\n case 'alpha-upper':\n templateValue = 'UpperAlpha';\n break;\n case 'alpha-lower':\n templateValue = 'LowerAlpha';\n break;\n case 'roman-lower':\n templateValue = 'LowerRoman';\n break;\n case 'roman-upper':\n templateValue = 'UpperRoman';\n break;\n default:\n templateValue = 'Number';\n break;\n }\n const template = (listMetadata['mso-level-text'] || '')\n .replace('\\\\', '')\n .replace('\"', '')\n .replace(TEMPLATE_VALUE_REGEX, '${' + templateValue + '}');\n\n templateFinal = '\"' + template + ' \"';\n } else {\n switch (templateType) {\n case 'alpha-lower':\n templateFinal = 'lower-alpha';\n break;\n case 'roman-lower':\n templateFinal = 'lower-roman';\n break;\n case 'roman-upper':\n templateFinal = 'upper-roman';\n break;\n default:\n templateFinal = 'decimal';\n break;\n }\n }\n\n return getListStyleTypeFromString(listType, templateFinal);\n}\n\nfunction startNumberOverrideParser(\n listMetadata: WordMetadata | undefined\n): FormatParser<ContentModelListItemLevelFormat> | null {\n return (format, _, context) => {\n const {\n wordKnownLevels,\n wordLevel,\n wordList,\n levels,\n } = context.listFormat as WordDesktopListFormat;\n if (typeof wordLevel === 'number' && wordList) {\n const start = parseInt(listMetadata?.['mso-level-start-at'] || '1');\n const knownLevel = wordKnownLevels?.get(wordList) || [];\n\n if (start != undefined && !isNaN(start) && knownLevel.length != levels.length) {\n format.startNumberOverride = start;\n }\n }\n };\n}\n"]}
|