diff --git a/packages-content-model/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts b/packages-content-model/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts new file mode 100644 index 00000000000..eddf134cb31 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts @@ -0,0 +1,54 @@ +import { DefaultContentModelFormatMap } from 'roosterjs-content-model-types'; + +/** + * @internal + * A map from tag name to its default implicit formats + */ +export const defaultContentModelFormatMap: DefaultContentModelFormatMap = { + a: { + underline: true, + }, + blockquote: { + marginTop: '1em', + marginBottom: '1em', + marginLeft: '40px', + marginRight: '40px', + }, + code: { + fontFamily: 'monospace', + }, + h1: { + fontWeight: 'bold', + fontSize: '2em', + }, + h2: { + fontWeight: 'bold', + fontSize: '1.5em', + }, + h3: { + fontWeight: 'bold', + fontSize: '1.17em', + }, + h4: { + fontWeight: 'bold', + fontSize: '1em', // Set this default value here to overwrite existing font size when change heading level + }, + h5: { + fontWeight: 'bold', + fontSize: '0.83em', + }, + h6: { + fontWeight: 'bold', + fontSize: '0.67em', + }, + p: { + marginTop: '1em', + marginBottom: '1em', + }, + pre: { + fontFamily: 'monospace', + whiteSpace: 'pre', + marginTop: '1em', + marginBottom: '1em', + }, +}; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/defaultStyles.ts b/packages-content-model/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts similarity index 58% rename from packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/defaultStyles.ts rename to packages-content-model/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts index 7eecb8fd775..73b487e852c 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/defaultStyles.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts @@ -1,4 +1,4 @@ -import { DefaultImplicitFormatMap, DefaultStyleMap } from 'roosterjs-content-model-types'; +import { DefaultStyleMap } from 'roosterjs-content-model-types'; const blockElement: Partial = { display: 'block', @@ -7,7 +7,7 @@ const blockElement: Partial = { /** * @internal */ -export const defaultStyleMap: DefaultStyleMap = { +export const defaultHTMLStyleMap: DefaultStyleMap = { address: blockElement, article: blockElement, aside: blockElement, @@ -123,69 +123,3 @@ export const defaultStyleMap: DefaultStyleMap = { }, ul: blockElement, }; - -/** - * @internal - */ -export const enum PseudoTagNames { - childOfPre = 'pre *', // This value is not a CSS selector, it just to tell this will impact elements under PRE tag. Any unique value here can work actually -} - -/** - * A map from tag name to its default implicit formats - */ -export const defaultImplicitFormatMap: DefaultImplicitFormatMap = { - a: { - underline: true, - }, - blockquote: { - marginTop: '1em', - marginBottom: '1em', - marginLeft: '40px', - marginRight: '40px', - }, - code: { - fontFamily: 'monospace', - }, - h1: { - fontWeight: 'bold', - fontSize: '2em', - }, - h2: { - fontWeight: 'bold', - fontSize: '1.5em', - }, - h3: { - fontWeight: 'bold', - fontSize: '1.17em', - }, - h4: { - fontWeight: 'bold', - fontSize: '1em', // Set this default value here to overwrite existing font size when change heading level - }, - h5: { - fontWeight: 'bold', - fontSize: '0.83em', - }, - h6: { - fontWeight: 'bold', - fontSize: '0.67em', - }, - p: { - marginTop: '1em', - marginBottom: '1em', - }, - pre: { - fontFamily: 'monospace', - whiteSpace: 'pre', - marginTop: '1em', - marginBottom: '1em', - }, - - // For PRE tag, the following styles will be included from the PRE tag. - // Adding this implicit style here so no need to generate these style for child elements - [PseudoTagNames.childOfPre]: { - fontFamily: 'monospace', - whiteSpace: 'pre', - }, -}; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/buildBaseProcessorMap.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/buildBaseProcessorMap.ts new file mode 100644 index 00000000000..470e9d274a0 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/buildBaseProcessorMap.ts @@ -0,0 +1,14 @@ +import { defaultProcessorMap } from './defaultProcessors'; +import { ElementProcessorMap } from 'roosterjs-content-model-types'; + +/** + * Build a DOM processor map with overrides that can be used as base processor map + * @param processorOverrides DOM processor overrides to default processors. + * Note: Inside an override processor it cannot call original processor using context.defaultElementProcessors. + * since here the default processor is also overridden + */ +export function buildBaseProcessorMap( + ...processorOverrides: (Partial | undefined)[] +): ElementProcessorMap { + return Object.assign({}, defaultProcessorMap, ...processorOverrides); +} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext.ts index b6e1c77bf07..c1e9b4ffb28 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext.ts @@ -1,31 +1,82 @@ -import { defaultFormatParsers, getFormatParsers } from '../../formatHandlers/defaultFormatHandlers'; import { defaultProcessorMap } from './defaultProcessors'; -import { defaultStyleMap } from '../../formatHandlers/utils/defaultStyles'; -import { DomToModelContext, DomToModelOption, EditorContext } from 'roosterjs-content-model-types'; +import { getObjectKeys } from 'roosterjs-editor-dom'; import { SelectionRangeEx } from 'roosterjs-editor-types'; +import { + defaultFormatHandlerMap, + defaultFormatKeysPerCategory, +} from '../../formatHandlers/defaultFormatHandlers'; +import { + ContentModelBlockFormat, + DomToModelContext, + DomToModelDecoratorContext, + DomToModelFormatContext, + DomToModelSelectionContext, + DomToModelSettings, + EditorContext, + ElementProcessorMap, + FormatParser, + FormatParsers, + FormatParsersPerCategory, +} from 'roosterjs-content-model-types'; /** - * Create context object form DOM to Content Model conversion + * Create context object for DOM to Content Model conversion + * @param processorOverride Overrides default element processors + * @param formatParserOverride Overrides default format handlers + * @param additionalFormatParsers: Provide additional format parsers for each format type + * @param baseProcessorMap Base DOM processor map, if not passed, default processor map will be used * @param editorContext Context of editor - * @param options Options for this context * @param selection Selection that already exists in content */ export function createDomToModelContext( - editorContext?: EditorContext, - options?: DomToModelOption, - selection?: SelectionRangeEx + processorOverride?: Partial, + formatParserOverride?: Partial, + additionalFormatParsers?: (Partial | undefined)[], + baseProcessorMap?: Readonly, + selection?: SelectionRangeEx, + editorContext?: EditorContext ): DomToModelContext { - const context: DomToModelContext = { - ...editorContext, + return Object.assign( + {}, + editorContext, + createDomToModelSelectionContext(selection), + createDomToModelFormatContext(editorContext?.isRootRtl), + createDomToModelDecoratorContext(), + createDomToModelSettings( + processorOverride, + formatParserOverride, + additionalFormatParsers, + baseProcessorMap + ) + ); +} + +function createDomToModelSelectionContext(rangeEx?: SelectionRangeEx): DomToModelSelectionContext { + const result: DomToModelSelectionContext = { isInSelection: false }; + + if (rangeEx) { + result.rangeEx = rangeEx; + } + + return result; +} - blockFormat: {}, +function createDomToModelFormatContext(isRootRtl?: boolean): DomToModelFormatContext { + const blockFormat: ContentModelBlockFormat = isRootRtl ? { direction: 'rtl' } : {}; + + return { + blockFormat, segmentFormat: {}, - isInSelection: false, listFormat: { levels: [], threadItemCounts: [], }, + }; +} + +function createDomToModelDecoratorContext(): DomToModelDecoratorContext { + return { link: { format: {}, dataset: {}, @@ -37,33 +88,66 @@ export function createDomToModelContext( format: {}, tagName: '', }, + }; +} - elementProcessors: { - ...defaultProcessorMap, - ...(options?.processorOverride || {}), - }, - - defaultStyles: { - ...defaultStyleMap, - ...(options?.defaultStyleOverride || {}), - }, - - formatParsers: getFormatParsers( - options?.formatParserOverride, - options?.additionalFormatParsers - ), +function createDomToModelSettings( + processorOverride?: Partial, + formatParserOverride?: Partial, + additionalFormatParsers?: (Partial | undefined)[], + baseProcessorMap?: Readonly +): DomToModelSettings { + const defaultElementProcessors = baseProcessorMap ?? defaultProcessorMap; - defaultElementProcessors: defaultProcessorMap, - defaultFormatParsers: defaultFormatParsers, + return { + elementProcessors: processorOverride + ? { ...defaultElementProcessors, ...processorOverride } + : defaultElementProcessors, + formatParsers: + formatParserOverride || (additionalFormatParsers?.length ?? 0) > 0 + ? buildFormatParsers(formatParserOverride, additionalFormatParsers) + : defaultFormatParsersPerCategory, + defaultElementProcessors, }; +} - if (editorContext?.isRootRtl) { - context.blockFormat.direction = 'rtl'; - } +const defaultFormatParsers: Readonly = getObjectKeys(defaultFormatHandlerMap).reduce( + (result, key) => { + result[key] = defaultFormatHandlerMap[key].parse as FormatParser; + return result; + }, + {} +); - if (selection) { - context.rangeEx = selection; - } +/** + * @internal Export for test only + * Build format parsers used by DOM to Content Model conversion + * @param override + * @param additionalParsersArray + * @returns + */ +export function buildFormatParsers( + override: Partial = {}, + additionalParsersArray: (Partial | undefined)[] = [] +): FormatParsersPerCategory { + return getObjectKeys(defaultFormatKeysPerCategory).reduce((result, key) => { + const value = defaultFormatKeysPerCategory[key] + .map( + formatKey => + (override[formatKey] === undefined + ? defaultFormatParsers[formatKey] + : override[formatKey]) as FormatParser + ) + .concat( + ...additionalParsersArray.map( + parsers => (parsers?.[key] ?? []) as FormatParser[] + ) + ); - return context; + result[key] = value; + + return result; + }, {} as FormatParsersPerCategory); } + +const defaultFormatParsersPerCategory = buildFormatParsers(); diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts index 874a82291dc..7f54db8b2b0 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts @@ -1,29 +1,20 @@ import { createContentModelDocument } from '../modelApi/creators/createContentModelDocument'; -import { createDomToModelContext } from './context/createDomToModelContext'; import { normalizeContentModel } from '../modelApi/common/normalizeContentModel'; -import { SelectionRangeEx } from 'roosterjs-editor-types'; -import { - ContentModelDocument, - DomToModelOption, - EditorContext, -} from 'roosterjs-content-model-types'; +import { ContentModelDocument, DomToModelContext } from 'roosterjs-content-model-types'; /** * Create Content Model from DOM tree in this editor * @param root Root element of DOM tree to create Content Model from - * @param option The option to customize the behavior of DOM to Content Model conversion - * @param editorContext Context of content model editor - * @param selection Existing selection range in editor + * @param config DOM Processor and format parser configuration + * @param editorContext Context of editor + * @param selection Selection that already exists in content * @returns A ContentModelDocument object that contains all the models created from the give root element */ export function domToContentModel( root: HTMLElement | DocumentFragment, - option?: DomToModelOption, - editorContext?: EditorContext, - selection?: SelectionRangeEx + context: DomToModelContext ): ContentModelDocument { - const model = createContentModelDocument(editorContext?.defaultFormat); - const context = createDomToModelContext(editorContext, option, selection); + const model = createContentModelDocument(context.defaultFormat); context.elementProcessors.child(model, root, context); diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getDefaultStyle.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getDefaultStyle.ts index 8c64f57905b..2bd50107c05 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getDefaultStyle.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getDefaultStyle.ts @@ -1,3 +1,4 @@ +import { defaultHTMLStyleMap } from '../../config/defaultHTMLStyleMap'; import { DefaultStyleMap, DomToModelContext } from 'roosterjs-content-model-types'; /** @@ -13,5 +14,5 @@ export function getDefaultStyle( ): Partial { let tag = element.tagName.toLowerCase() as keyof DefaultStyleMap; - return context.defaultStyles[tag] || {}; + return defaultHTMLStyleMap[tag] || {}; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts index 010fb3b5dba..89adccac954 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts @@ -10,7 +10,6 @@ import { floatFormatHandler } from './common/floatFormatHandler'; import { fontFamilyFormatHandler } from './segment/fontFamilyFormatHandler'; import { fontSizeFormatHandler } from './segment/fontSizeFormatHandler'; import { FormatHandler } from './FormatHandler'; -import { getObjectKeys } from 'roosterjs-editor-dom'; import { htmlAlignFormatHandler } from './block/htmlAlignFormatHandler'; import { idFormatHandler } from './common/idFormatHandler'; import { italicFormatHandler } from './segment/italicFormatHandler'; @@ -38,19 +37,19 @@ import { ContentModelFormatMap, FormatHandlerTypeMap, FormatKey, - FormatApplier, - FormatAppliers, - FormatAppliersPerCategory, - FormatParser, - FormatParsers, - FormatParsersPerCategory, } from 'roosterjs-content-model-types'; -type FormatHandlers = { +/** + * @internal + */ +export type FormatHandlers = { [Key in FormatKey]: FormatHandler; }; -const defaultFormatHandlerMap: FormatHandlers = { +/** + * @internal + */ +export const defaultFormatHandlerMap: FormatHandlers = { backgroundColor: backgroundColorFormatHandler, bold: boldFormatHandler, border: borderFormatHandler, @@ -113,7 +112,10 @@ const sharedContainerFormats: (keyof FormatHandlerTypeMap)[] = [ 'border', ]; -const defaultFormatKeysPerCategory: { +/** + * @internal + */ +export const defaultFormatKeysPerCategory: { [key in keyof ContentModelFormatMap]: (keyof FormatHandlerTypeMap)[]; } = { block: sharedBlockFormats, @@ -196,71 +198,3 @@ const defaultFormatKeysPerCategory: { divider: [...sharedBlockFormats, ...sharedContainerFormats, 'display', 'size', 'htmlAlign'], container: [...sharedContainerFormats, 'htmlAlign', 'size', 'display'], }; - -/** - * @internal - */ -export const defaultFormatParsers: FormatParsers = getObjectKeys(defaultFormatHandlerMap).reduce( - (result, key) => { - result[key] = defaultFormatHandlerMap[key].parse as FormatParser; - return result; - }, - {} -); - -/** - * @internal - */ -export const defaultFormatAppliers: FormatAppliers = getObjectKeys(defaultFormatHandlerMap).reduce( - (result, key) => { - result[key] = defaultFormatHandlerMap[key].apply as FormatApplier; - return result; - }, - {} -); - -/** - * @internal - */ -export function getFormatParsers( - override: Partial = {}, - additionalParsers: Partial = {} -): FormatParsersPerCategory { - return getObjectKeys(defaultFormatKeysPerCategory).reduce((result, key) => { - const value = defaultFormatKeysPerCategory[key] - .map( - formatKey => - (override[formatKey] === undefined - ? defaultFormatParsers[formatKey] - : override[formatKey]) as FormatParser - ) - .concat((additionalParsers[key] as FormatParser[]) || []); - - result[key] = value; - - return result; - }, {} as FormatParsersPerCategory); -} - -/** - * @internal - */ -export function getFormatAppliers( - override: Partial = {}, - additionalAppliers: Partial = {} -): FormatAppliersPerCategory { - return getObjectKeys(defaultFormatKeysPerCategory).reduce((result, key) => { - const value = defaultFormatKeysPerCategory[key] - .map( - formatKey => - (override[formatKey] === undefined - ? defaultFormatAppliers[formatKey] - : override[formatKey]) as FormatApplier - ) - .concat((additionalAppliers[key] as FormatApplier[]) || []); - - result[key] = value; - - return result; - }, {} as FormatAppliersPerCategory); -} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/index.ts b/packages-content-model/roosterjs-content-model-dom/lib/index.ts index 76f505f1e49..5f06d3f2eba 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/index.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/index.ts @@ -48,6 +48,8 @@ export { setParagraphNotImplicit } from './modelApi/block/setParagraphNotImplici export { parseValueWithUnit } from './formatHandlers/utils/parseValueWithUnit'; export { BorderKeys } from './formatHandlers/common/borderFormatHandler'; export { DeprecatedColors } from './formatHandlers/utils/color'; -export { defaultImplicitFormatMap } from './formatHandlers/utils/defaultStyles'; export { createDomToModelContext } from './domToModel/context/createDomToModelContext'; +export { createModelToDomContext } from './modelToDom/context/createModelToDomContext'; +export { buildBaseProcessorMap } from './domToModel/context/buildBaseProcessorMap'; +export { buildBaseHandlerMap } from './modelToDom/context/buildBaseHandlerMap'; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts index 450ce903c76..7fc9643c2fd 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts @@ -1,12 +1,9 @@ -import { createModelToDomContext } from './context/createModelToDomContext'; import { createRange, Position, toArray } from 'roosterjs-editor-dom'; import { isNodeOfType } from '../domUtils/isNodeOfType'; import { ContentModelDocument, - EditorContext, ModelToDomBlockAndSegmentNode, ModelToDomContext, - ModelToDomOption, } from 'roosterjs-content-model-types'; import { NodePosition, @@ -22,8 +19,8 @@ import { * When a DOM node with existing node is passed, it will be merged with content model so that unchanged blocks * won't be touched. * @param model The content model document to generate DOM tree from + * @param config DOM handler and format applier configuration * @param editorContext Content for Content Model editor - * @param option Additional options to customize the behavior of Content Model to DOM conversion * @returns A tuple of the following 3 objects: * 1. Document Fragment that contains the DOM tree generated from the given model * 2. A SelectionRangeEx object that contains selection info from the model if any, or null @@ -33,14 +30,11 @@ export function contentModelToDom( doc: Document, root: Node, model: ContentModelDocument, - editorContext?: EditorContext, - option?: ModelToDomOption + context: ModelToDomContext ): SelectionRangeEx | null { - const modelToDomContext = createModelToDomContext(editorContext, option); + context.modelHandlers.blockGroupChildren(doc, root, model, context); - modelToDomContext.modelHandlers.blockGroupChildren(doc, root, model, modelToDomContext); - - const range = extractSelectionRange(modelToDomContext); + const range = extractSelectionRange(context); root.normalize(); diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/buildBaseHandlerMap.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/buildBaseHandlerMap.ts new file mode 100644 index 00000000000..d73cdec88d9 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/buildBaseHandlerMap.ts @@ -0,0 +1,14 @@ +import { ContentModelHandlerMap } from 'roosterjs-content-model-types'; +import { defaultContentModelHandlers } from './defaultContentModelHandlers'; + +/** + * Build a model handler map with overrides that can be used as base handle map + * @param handlerOverrides Model handler overrides to default handlers. + * Note: Inside an override handler it cannot call original handler using context.defaultModelHandlers. + * since here the default handler is also overridden + */ +export function buildBaseHandlerMap( + ...handlerOverrides: (Partial | undefined)[] +): ContentModelHandlerMap { + return Object.assign({}, defaultContentModelHandlers, ...handlerOverrides); +} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext.ts index 290fbce8d00..889a37085c5 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext.ts @@ -1,51 +1,130 @@ import { defaultContentModelHandlers } from './defaultContentModelHandlers'; -import { defaultImplicitFormatMap } from '../../formatHandlers/utils/defaultStyles'; -import { EditorContext, ModelToDomContext, ModelToDomOption } from 'roosterjs-content-model-types'; +import { getObjectKeys } from 'roosterjs-editor-dom'; import { - defaultFormatAppliers, - getFormatAppliers, + defaultFormatHandlerMap, + defaultFormatKeysPerCategory, } from '../../formatHandlers/defaultFormatHandlers'; +import { + ContentModelHandlerMap, + EditorContext, + FormatApplier, + FormatAppliers, + FormatAppliersPerCategory, + ModelToDomContext, + ModelToDomFormatContext, + ModelToDomSelectionContext, + ModelToDomSettings, + OnNodeCreated, +} from 'roosterjs-content-model-types'; /** - * @internal - * @param editorContext - * @returns + * Create context object fro Content Model to DOM conversion + * @param onNodeCreated Callback invoked when a DOM node is created + * @param handlerOverride Overrides default model handlers + * @param formatApplierOverride Overrides default format appliers + * @param additionalFormatAppliers Provide additional format appliers for each format type + * @param baseModelHandlerMap Base model handler map, if not passed, default handler map will be used + * @param editorContext Context of editor */ export function createModelToDomContext( - editorContext?: EditorContext, - options?: ModelToDomOption + onNodeCreated?: OnNodeCreated, + handlerOverride?: Partial, + formatApplierOverride?: Partial, + additionalFormatAppliers?: (Partial | undefined)[], + baseModelHandlerMap?: Readonly, + editorContext?: EditorContext ): ModelToDomContext { - options = options || {}; + return Object.assign( + {}, + editorContext, + createModelToDomSelectionContext(), + createModelToDomFormatContext(), + createModelToDomSettings( + onNodeCreated, + handlerOverride, + formatApplierOverride, + additionalFormatAppliers, + baseModelHandlerMap + ) + ); +} +function createModelToDomSelectionContext(): ModelToDomSelectionContext { return { - ...editorContext, - regularSelection: { current: { block: null, segment: null, }, }, + }; +} + +function createModelToDomFormatContext(): ModelToDomFormatContext { + return { listFormat: { threadItemCounts: [], nodeStack: [], }, implicitFormat: {}, - formatAppliers: getFormatAppliers( - options.formatApplierOverride, - options.additionalFormatAppliers - ), - modelHandlers: { - ...defaultContentModelHandlers, - ...(options.modelHandlerOverride || {}), - }, - defaultImplicitFormatMap: { - ...defaultImplicitFormatMap, - ...(options.defaultImplicitFormatOverride || {}), - }, + }; +} - defaultModelHandlers: defaultContentModelHandlers, - defaultFormatAppliers: defaultFormatAppliers, - onNodeCreated: options.onNodeCreated, +function createModelToDomSettings( + onNodeCreated?: OnNodeCreated, + handlerOverride?: Partial, + formatApplierOverride?: Partial, + additionalFormatAppliers?: (Partial | undefined)[], + baseModelHandlerMap?: Readonly +): ModelToDomSettings { + const defaultModelHandlers = baseModelHandlerMap ?? defaultContentModelHandlers; + + return { + modelHandlers: handlerOverride + ? { ...defaultModelHandlers, ...handlerOverride } + : defaultModelHandlers, + formatAppliers: + formatApplierOverride || (additionalFormatAppliers?.length ?? 0) > 0 + ? buildFormatAppliers(formatApplierOverride, additionalFormatAppliers) + : defaultFormatAppliersPerCategory, + onNodeCreated, + defaultModelHandlers, }; } + +const defaultFormatAppliers: Readonly = getObjectKeys( + defaultFormatHandlerMap +).reduce((result, key) => { + result[key] = defaultFormatHandlerMap[key].apply as FormatApplier; + return result; +}, {}); + +/** + * @internal Export for test only + * Build format appliers used by Content Model to DOM conversion + */ +export function buildFormatAppliers( + override: Partial = {}, + additionalAppliersArray: (Partial | undefined)[] = [] +): FormatAppliersPerCategory { + return getObjectKeys(defaultFormatKeysPerCategory).reduce((result, key) => { + const value = defaultFormatKeysPerCategory[key] + .map( + formatKey => + (override[formatKey] === undefined + ? defaultFormatAppliers[formatKey] + : override[formatKey]) as FormatApplier + ) + .concat( + ...additionalAppliersArray.map( + appliers => (appliers?.[key] ?? []) as FormatApplier[] + ) + ); + + result[key] = value; + + return result; + }, {} as FormatAppliersPerCategory); +} + +const defaultFormatAppliersPerCategory = buildFormatAppliers(); diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleFormatContainer.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleFormatContainer.ts index 035ca50891f..5eddc15464a 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleFormatContainer.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleFormatContainer.ts @@ -1,14 +1,20 @@ import { applyFormat } from '../utils/applyFormat'; import { isBlockGroupEmpty } from '../../modelApi/common/isEmpty'; -import { PseudoTagNames } from '../../formatHandlers/utils/defaultStyles'; import { reuseCachedElement } from '../utils/reuseCachedElement'; import { stackFormat } from '../utils/stackFormat'; import { + ContentModelBlockFormat, ContentModelBlockHandler, ContentModelFormatContainer, + ContentModelSegmentFormat, ModelToDomContext, } from 'roosterjs-content-model-types'; +const PreChildFormat: ContentModelSegmentFormat & ContentModelBlockFormat = { + fontFamily: 'monospace', + whiteSpace: 'pre', +}; + /** * @internal */ @@ -47,7 +53,7 @@ export const handleFormatContainer: ContentModelBlockHandler { + stackFormat(context, PreChildFormat, () => { context.modelHandlers.blockGroupChildren(doc, containerNode, container, context); }); } else { diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/stackFormat.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/stackFormat.ts index 0d07b36a3d4..666eef9047d 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/stackFormat.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/stackFormat.ts @@ -1,3 +1,4 @@ +import { defaultContentModelFormatMap } from '../../config/defaultContentModelFormatMap'; import { ContentModelBlockFormat, ContentModelSegmentFormat, @@ -14,7 +15,7 @@ export function stackFormat( ) { const newFormat = typeof tagNameOrFormat === 'string' - ? context.defaultImplicitFormatMap[tagNameOrFormat] + ? defaultContentModelFormatMap[tagNameOrFormat] : tagNameOrFormat; if (newFormat) { diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/context/createDomToModelContextTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/context/createDomToModelContextTest.ts index e422c03519b..a0fecc1fd01 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/context/createDomToModelContextTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/context/createDomToModelContextTest.ts @@ -1,34 +1,22 @@ -import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; import { defaultProcessorMap } from '../../../lib/domToModel/context/defaultProcessors'; -import { defaultStyleMap } from '../../../lib/formatHandlers/utils/defaultStyles'; -import { DomToModelListFormat, EditorContext } from 'roosterjs-content-model-types'; +import { EditorContext } from 'roosterjs-content-model-types'; import { - defaultFormatParsers, - getFormatParsers, -} from '../../../lib/formatHandlers/defaultFormatHandlers'; + buildFormatParsers, + createDomToModelContext, +} from '../../../lib/domToModel/context/createDomToModelContext'; describe('createDomToModelContext', () => { - const editorContext: EditorContext = {}; - const listFormat: DomToModelListFormat = { - threadItemCounts: [], - levels: [], - }; - const contextOptions = { - elementProcessors: defaultProcessorMap, - defaultStyles: defaultStyleMap, - formatParsers: getFormatParsers(), - defaultElementProcessors: defaultProcessorMap, - defaultFormatParsers: defaultFormatParsers, - }; it('no param', () => { const context = createDomToModelContext(); expect(context).toEqual({ - ...editorContext, - segmentFormat: {}, - blockFormat: {}, isInSelection: false, - listFormat, + blockFormat: {}, + segmentFormat: {}, + listFormat: { + threadItemCounts: [], + levels: [], + }, link: { format: {}, dataset: {}, @@ -40,23 +28,35 @@ describe('createDomToModelContext', () => { format: {}, tagName: '', }, - ...contextOptions, + elementProcessors: defaultProcessorMap, + formatParsers: buildFormatParsers(), + defaultElementProcessors: defaultProcessorMap, }); }); - it('with content model context', () => { + it('with editor context', () => { const editorContext: EditorContext = { isDarkMode: true, }; - const context = createDomToModelContext(editorContext); + const context = createDomToModelContext( + undefined, + undefined, + undefined, + undefined, + undefined, + editorContext + ); expect(context).toEqual({ ...editorContext, - segmentFormat: {}, - blockFormat: {}, isInSelection: false, - listFormat, + blockFormat: {}, + segmentFormat: {}, + listFormat: { + threadItemCounts: [], + levels: [], + }, link: { format: {}, dataset: {}, @@ -68,23 +68,24 @@ describe('createDomToModelContext', () => { format: {}, tagName: '', }, - ...contextOptions, + elementProcessors: defaultProcessorMap, + formatParsers: buildFormatParsers(), + defaultElementProcessors: defaultProcessorMap, }); }); - it('with content model context', () => { - const editorContext: EditorContext = { - isDarkMode: true, - }; - - const context = createDomToModelContext(editorContext); + it('with selection', () => { + const range = { name: 'SelectionContext' } as any; + const context = createDomToModelContext(undefined, undefined, undefined, undefined, range); expect(context).toEqual({ - ...editorContext, - segmentFormat: {}, - blockFormat: {}, isInSelection: false, - listFormat, + blockFormat: {}, + segmentFormat: {}, + listFormat: { + threadItemCounts: [], + levels: [], + }, link: { format: {}, dataset: {}, @@ -96,19 +97,51 @@ describe('createDomToModelContext', () => { format: {}, tagName: '', }, - ...contextOptions, + elementProcessors: defaultProcessorMap, + formatParsers: buildFormatParsers(), + defaultElementProcessors: defaultProcessorMap, + rangeEx: range, }); }); - it('with selection context', () => { - const selectionContext = { name: 'SelectionContext' } as any; - const context = createDomToModelContext(undefined, undefined, selectionContext); + it('with override', () => { + const mockedAProcessor = 'a' as any; + const mockedBoldParser = 'bold' as any; + const mockedBlockParser = 'block' as any; + const mockedBaseProcessor = 'base' as any; + const context = createDomToModelContext( + { + a: mockedAProcessor, + }, + { + bold: mockedBoldParser, + }, + [ + { + block: mockedBlockParser, + }, + ], + { + base: mockedBaseProcessor, + } as any + ); + + const parsers = buildFormatParsers(); + + parsers.block[4] = mockedBlockParser; + parsers.elementBasedSegment[4] = mockedBoldParser; + parsers.segment[7] = mockedBoldParser; + parsers.segmentOnBlock[7] = mockedBoldParser; + parsers.segmentOnTableCell[7] = mockedBoldParser; expect(context).toEqual({ - segmentFormat: {}, - blockFormat: {}, isInSelection: false, - listFormat, + blockFormat: {}, + segmentFormat: {}, + listFormat: { + threadItemCounts: [], + levels: [], + }, link: { format: {}, dataset: {}, @@ -120,37 +153,14 @@ describe('createDomToModelContext', () => { format: {}, tagName: '', }, - ...contextOptions, - rangeEx: selectionContext, - }); - }); - - it('with override', () => { - const mockedAProcessor = 'a' as any; - const mockedOlStyle = 'ol' as any; - const mockedBoldParser = 'bold' as any; - const mockedBlockParser = 'block' as any; - const context = createDomToModelContext(undefined, { - processorOverride: { + elementProcessors: { + base: mockedBaseProcessor, a: mockedAProcessor, - }, - defaultStyleOverride: { - ol: mockedOlStyle, - }, - formatParserOverride: { - bold: mockedBoldParser, - }, - additionalFormatParsers: { - block: mockedBlockParser, - }, + } as any, + formatParsers: parsers, + defaultElementProcessors: { + base: mockedBaseProcessor, + } as any, }); - - expect(context.elementProcessors.a).toBe(mockedAProcessor); - expect(context.defaultStyles.ol).toBe(mockedOlStyle); - expect(context.formatParsers.segment.indexOf(mockedBoldParser)).toBeGreaterThanOrEqual(0); - expect(context.formatParsers.block).toEqual([ - ...getFormatParsers().block, - mockedBlockParser, - ]); }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/domToContentModelTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/domToContentModelTest.ts index 889ff65b742..399a2e138c5 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/domToContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/domToContentModelTest.ts @@ -1,11 +1,6 @@ -import * as createDomToModelContext from '../../lib/domToModel/context/createDomToModelContext'; import * as normalizeContentModel from '../../lib/modelApi/common/normalizeContentModel'; import { domToContentModel } from '../../lib/domToModel/domToContentModel'; -import { - ContentModelDocument, - DomToModelContext, - EditorContext, -} from 'roosterjs-content-model-types'; +import { ContentModelDocument, DomToModelContext } from 'roosterjs-content-model-types'; describe('domToContentModel', () => { it('Not include root', () => { @@ -18,20 +13,16 @@ describe('domToContentModel', () => { }, defaultStyles: {}, segmentFormat: {}, + isDarkMode: false, + defaultFormat: { + fontSize: '10pt', + }, } as any) as DomToModelContext; - spyOn(createDomToModelContext, 'createDomToModelContext').and.returnValue(mockContext); spyOn(normalizeContentModel, 'normalizeContentModel'); const rootElement = document.createElement('div'); - const options = {}; - const editorContext: EditorContext = { - isDarkMode: false, - defaultFormat: { - fontSize: '10pt', - }, - }; - const model = domToContentModel(rootElement, options, editorContext); + const model = domToContentModel(rootElement, mockContext); const result: ContentModelDocument = { blockGroupType: 'Document', blocks: [], @@ -41,12 +32,6 @@ describe('domToContentModel', () => { }; expect(model).toEqual(result); - expect(createDomToModelContext.createDomToModelContext).toHaveBeenCalledTimes(1); - expect(createDomToModelContext.createDomToModelContext).toHaveBeenCalledWith( - editorContext, - options, - undefined - ); expect(elementProcessor).not.toHaveBeenCalled(); expect(childProcessor).toHaveBeenCalledTimes(1); expect(childProcessor).toHaveBeenCalledWith(result, rootElement, mockContext); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/blockProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/blockProcessorTest.ts index a08131451e3..a2dd04a6856 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/blockProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/blockProcessorTest.ts @@ -14,11 +14,12 @@ describe('blockProcessor', () => { let childSpy: jasmine.Spy; beforeEach(() => { - context = createDomToModelContext(); group = createContentModelDocument(); childSpy = jasmine.createSpy('child'); - context.elementProcessors.child = childSpy; + context = createDomToModelContext({ + child: childSpy, + }); }); function runTest( diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/childProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/childProcessorTest.ts index 4b34c2083bb..2e4c9e5af82 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/childProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/childProcessorTest.ts @@ -17,10 +17,8 @@ describe('childProcessor', () => { beforeEach(() => { textProcessor = jasmine.createSpy('textProcessor'); doc = createContentModelDocument(); - context = createDomToModelContext(undefined, { - processorOverride: { - '#text': textProcessor, - }, + context = createDomToModelContext({ + '#text': textProcessor, }); }); @@ -352,7 +350,9 @@ describe('childProcessor', () => { div.innerHTML = '
  1. test1
test2
  1. test3
'; - context.elementProcessors.div = generalProcessor; + context = createDomToModelContext({ + div: generalProcessor, + }); childProcessor(doc, div, context); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/elementProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/elementProcessorTest.ts index 86f47fa2171..3133910ea32 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/elementProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/elementProcessorTest.ts @@ -15,7 +15,7 @@ describe('elementProcessor', () => { let divProcessor: jasmine.Spy>; let generalProcessor: jasmine.Spy>; let entityProcessor: jasmine.Spy>; - let delimiterProcessor: jasmine.Spy>; + let delimiterProcessor: jasmine.Spy>; beforeEach(() => { group = createContentModelDocument(); @@ -24,13 +24,11 @@ describe('elementProcessor', () => { entityProcessor = jasmine.createSpy('entity processor'); delimiterProcessor = jasmine.createSpy('entity processor'); - context = createDomToModelContext(undefined, { - processorOverride: { - div: divProcessor, - entity: entityProcessor, - '*': generalProcessor, - delimiter: delimiterProcessor, - }, + context = createDomToModelContext({ + div: divProcessor, + entity: entityProcessor, + '*': generalProcessor, + delimiter: delimiterProcessor, }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/formatContainerProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/formatContainerProcessorTest.ts index 06a58f5eb7c..6189fc5af28 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/formatContainerProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/formatContainerProcessorTest.ts @@ -189,9 +189,13 @@ describe('formatContainerProcessor', () => { quote.style.color = 'blue'; quote.style.borderLeft = 'solid 1px black'; + + context = createDomToModelContext({ + child: childProcessor, + }); + context.segmentFormat.textColor = 'green'; context.segmentFormat.fontSize = '20px'; - context.elementProcessors.child = childProcessor; formatContainerProcessor(group, quote, context); @@ -225,14 +229,16 @@ describe('formatContainerProcessor', () => { quote.style.borderLeft = 'solid 1px black'; + context = createDomToModelContext({ + child: childProcessor, + }); + context.blockFormat.backgroundColor = 'red'; context.blockFormat.htmlAlign = 'center'; context.blockFormat.lineHeight = '2'; context.blockFormat.whiteSpace = 'pre'; context.blockFormat.direction = 'rtl'; - context.elementProcessors.child = childProcessor; - formatContainerProcessor(group, quote, context); expect(group).toEqual({ diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts index 904613857e0..2b7ea4c1cac 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts @@ -14,14 +14,12 @@ import { describe('generalProcessor', () => { let context: DomToModelContext; - let childProcessor: jasmine.Spy>; + let childProcessor: jasmine.Spy>; beforeEach(() => { childProcessor = jasmine.createSpy(); - context = createDomToModelContext(undefined, { - processorOverride: { - child: childProcessor, - }, + context = createDomToModelContext({ + child: childProcessor, }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts index a6b4848e0ce..002dd60e089 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts @@ -215,7 +215,7 @@ describe('imageProcessor', () => { img.src = 'http://test.com/testSrc'; - context.formatParsers.dataset = [datasetParser]; + context = createDomToModelContext(undefined, { dataset: datasetParser }); imageProcessor(doc, img, context); @@ -251,7 +251,7 @@ describe('imageProcessor', () => { img.src = 'http://test.com/testSrc'; - context.formatParsers.dataset = [datasetParser]; + context = createDomToModelContext(undefined, { dataset: datasetParser }); imageProcessor(doc, img, context); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/listItemProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/listItemProcessorTest.ts index f8d890cbe37..6a127c3612a 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/listItemProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/listItemProcessorTest.ts @@ -7,10 +7,8 @@ describe('listItemProcessor', () => { let context: DomToModelContext; beforeEach(() => { - context = createDomToModelContext(undefined, { - processorOverride: { - li: listItemProcessor, - }, + context = createDomToModelContext({ + li: listItemProcessor, }); }); @@ -359,14 +357,12 @@ describe('listItemProcessor without format handlers', () => { let context: DomToModelContext; beforeEach(() => { - context = createDomToModelContext(undefined, { - processorOverride: { - li: listItemProcessor, - }, - formatParserOverride: { + context = createDomToModelContext( + { li: listItemProcessor }, + { listItemThread: null, - }, - }); + } + ); }); it('LI without valid context', () => { diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/listProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/listProcessorTest.ts index 653fcd523fb..528d49c2030 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/listProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/listProcessorTest.ts @@ -8,16 +8,14 @@ import { listProcessor } from '../../../lib/domToModel/processors/listProcessor' describe('listProcessor', () => { let context: DomToModelContext; - let childProcessor: jasmine.Spy>; + let childProcessor: jasmine.Spy>; beforeEach(() => { childProcessor = jasmine.createSpy(); - context = createDomToModelContext(undefined, { - processorOverride: { - ul: listProcessor, - ol: listProcessor, - child: childProcessor, - }, + context = createDomToModelContext({ + ul: listProcessor, + ol: listProcessor, + child: childProcessor, }); }); @@ -269,21 +267,21 @@ describe('listProcessor', () => { }); describe('listProcessor without format handlers', () => { - let childProcessor: jasmine.Spy>; + let childProcessor: jasmine.Spy>; let context: DomToModelContext; beforeEach(() => { childProcessor = jasmine.createSpy(); - context = createDomToModelContext(undefined, { - processorOverride: { + context = createDomToModelContext( + { ul: listProcessor, ol: listProcessor, child: childProcessor, }, - formatParserOverride: { + { listLevelThread: null, - }, - }); + } + ); }); it('Single UL element', () => { @@ -447,15 +445,11 @@ describe('listProcessor without format handlers', () => { describe('listProcessor process metadata', () => { let context: DomToModelContext; - let childProcessor: jasmine.Spy>; + let childProcessor: jasmine.Spy>; beforeEach(() => { childProcessor = jasmine.createSpy(); - context = createDomToModelContext(undefined, { - processorOverride: { - child: childProcessor, - }, - }); + context = createDomToModelContext({ child: childProcessor }); }); it('OL without list style type and metadata', () => { diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts index 4b2cb122340..f368648f505 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts @@ -15,15 +15,11 @@ import { describe('tableProcessor', () => { let context: DomToModelContext; - let childProcessor: jasmine.Spy>; + let childProcessor: jasmine.Spy>; beforeEach(() => { childProcessor = jasmine.createSpy(); - context = createDomToModelContext(undefined, { - processorOverride: { - child: childProcessor, - }, - }); + context = createDomToModelContext({ child: childProcessor }); spyOn(getBoundingClientRect, 'getBoundingClientRect').and.returnValue(({ width: 100, @@ -470,7 +466,7 @@ describe('tableProcessor with format', () => { const doc = createContentModelDocument(); const datasetParser = jasmine.createSpy('datasetParser'); - context.formatParsers.dataset = [datasetParser]; + context = createDomToModelContext(undefined, { dataset: datasetParser }); tableProcessor(doc, mockedTable, context); @@ -511,7 +507,7 @@ describe('tableProcessor with format', () => { const doc = createContentModelDocument(); const datasetParser = jasmine.createSpy('datasetParser'); - context.formatParsers.dataset = [datasetParser]; + context = createDomToModelContext(undefined, { dataset: datasetParser }); tableProcessor(doc, mockedTable, context); @@ -523,15 +519,11 @@ describe('tableProcessor with format', () => { describe('tableProcessor', () => { let context: DomToModelContext; - let childProcessor: jasmine.Spy>; + let childProcessor: jasmine.Spy>; beforeEach(() => { childProcessor = jasmine.createSpy(); - context = createDomToModelContext(undefined, { - processorOverride: { - child: childProcessor, - }, - }); + context = createDomToModelContext({ child: childProcessor }); spyOn(getBoundingClientRect, 'getBoundingClientRect').and.returnValue(({ width: 100, diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/utils/getDefaultStyleTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/utils/getDefaultStyleTest.ts index 83accf6c736..7ba866f7e6e 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/utils/getDefaultStyleTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/utils/getDefaultStyleTest.ts @@ -18,22 +18,6 @@ describe('getDefaultStyle', () => { }); }); - it('Get customized default style of DIV', () => { - context = createDomToModelContext(undefined, { - defaultStyleOverride: { - div: { - color: 'red', - }, - }, - }); - const div = document.createElement('div'); - const style = getDefaultStyle(div, context); - - expect(style).toEqual({ - color: 'red', - }); - }); - it('Get default style of customized element', () => { const test = document.createElement('test'); const style = getDefaultStyle(test, context); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/utils/isBlockElementTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/utils/isBlockElementTest.ts index 54793073af7..b2b5c73df0a 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/utils/isBlockElementTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/utils/isBlockElementTest.ts @@ -67,52 +67,6 @@ describe('isBlockElement', () => { expect(result).toBeTrue(); }); - it('Override DIV default style', () => { - const div = document.createElement('div'); - - context = createDomToModelContext(undefined, { - defaultStyleOverride: { - div: { - display: 'inline', - }, - }, - }); - - const result = isBlockElement(div, context); - expect(result).toBeFalse(); - }); - - it('Override SPAN default style', () => { - const span = document.createElement('span'); - - context = createDomToModelContext(undefined, { - defaultStyleOverride: { - span: { - display: 'block', - }, - }, - }); - - const result = isBlockElement(span, context); - expect(result).toBeTrue(); - }); - - it('Double override SPAN', () => { - const span = document.createElement('span'); - span.style.display = 'inline'; - - context = createDomToModelContext(undefined, { - defaultStyleOverride: { - span: { - display: 'block', - }, - }, - }); - - const result = isBlockElement(span, context); - expect(result).toBeFalse(); - }); - it('display = flex', () => { const div = document.createElement('div'); div.style.display = 'flex'; diff --git a/packages-content-model/roosterjs-content-model-dom/test/endToEndTest.ts b/packages-content-model/roosterjs-content-model-dom/test/endToEndTest.ts index a5693f7a613..19bafdbbed0 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/endToEndTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/endToEndTest.ts @@ -1,6 +1,7 @@ import * as createGeneralBlock from '../lib/modelApi/creators/createGeneralBlock'; import DarkColorHandlerImpl from 'roosterjs-editor-core/lib/editor/DarkColorHandlerImpl'; import { contentModelToDom } from '../lib/modelToDom/contentModelToDom'; +import { createDomToModelContext, createModelToDomContext } from '../lib'; import { domToContentModel } from '../lib/domToModel/domToContentModel'; import { expectHtml } from 'roosterjs-editor-api/test/TestHelper'; import { @@ -29,13 +30,21 @@ describe('End to end test for DOM => Model', () => { const div1 = document.createElement('div'); div1.innerHTML = html; - const model = domToContentModel(div1, undefined, context); + const model = domToContentModel( + div1, + createDomToModelContext(undefined, undefined, undefined, undefined, undefined, context) + ); expect(model).toEqual(expectedModel); const div2 = document.createElement('div'); - contentModelToDom(document, div2, model, context); + contentModelToDom( + document, + div2, + model, + createModelToDomContext(undefined, undefined, undefined, undefined, undefined, context) + ); const possibleHTML = [ expectedHtml, //chrome or firefox expectedHTMLFirefox, //firefox diff --git a/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/segment/linkFormatHandlerTest.ts b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/segment/linkFormatHandlerTest.ts index d34d548ec1b..9398dac5a91 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/segment/linkFormatHandlerTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/segment/linkFormatHandlerTest.ts @@ -1,5 +1,6 @@ import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; +import { defaultHTMLStyleMap } from '../../../lib/config/defaultHTMLStyleMap'; import { DomToModelContext, LinkFormat, ModelToDomContext } from 'roosterjs-content-model-types'; import { linkFormatHandler } from '../../../lib/formatHandlers/segment/linkFormatHandler'; @@ -17,7 +18,7 @@ describe('linkFormatHandler.parse', () => { div.setAttribute('href', '/test'); - linkFormatHandler.parse(format, div, context, context.defaultStyles.a!); + linkFormatHandler.parse(format, div, context, defaultHTMLStyleMap.a!); expect(format).toEqual({}); }); @@ -27,7 +28,7 @@ describe('linkFormatHandler.parse', () => { a.href = '/test'; - linkFormatHandler.parse(format, a, context, context.defaultStyles.a!); + linkFormatHandler.parse(format, a, context, defaultHTMLStyleMap.a!); expect(format).toEqual({ href: '/test', @@ -45,7 +46,7 @@ describe('linkFormatHandler.parse', () => { a.target = 'target'; a.name = 'name'; - linkFormatHandler.parse(format, a, context, context.defaultStyles.a!); + linkFormatHandler.parse(format, a, context, defaultHTMLStyleMap.a!); expect(format).toEqual({ anchorClass: 'class', diff --git a/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/segment/textColorFormatHandlerTest.ts b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/segment/textColorFormatHandlerTest.ts index 1e7bcb1a68b..00feb9fd174 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/segment/textColorFormatHandlerTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/segment/textColorFormatHandlerTest.ts @@ -1,6 +1,7 @@ import DarkColorHandlerImpl from 'roosterjs-editor-core/lib/editor/DarkColorHandlerImpl'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; +import { defaultHTMLStyleMap } from '../../../lib/config/defaultHTMLStyleMap'; import { DeprecatedColors } from '../../../lib'; import { expectHtml } from 'roosterjs-editor-dom/test/DomTestHelper'; import { textColorFormatHandler } from '../../../lib/formatHandlers/segment/textColorFormatHandler'; @@ -74,7 +75,7 @@ describe('textColorFormatHandler.parse', () => { it('Color from hyperlink with override', () => { div.style.color = 'red'; - textColorFormatHandler.parse(format, div, context, context.defaultStyles.a!); + textColorFormatHandler.parse(format, div, context, defaultHTMLStyleMap.a!); expect(format).toEqual({ textColor: 'red', diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/context/createModelToDomContextTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/context/createModelToDomContextTest.ts index c029262fdf3..51a71cbf02d 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/context/createModelToDomContextTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/context/createModelToDomContextTest.ts @@ -1,50 +1,64 @@ -import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; import { defaultContentModelHandlers } from '../../../lib/modelToDom/context/defaultContentModelHandlers'; -import { defaultImplicitFormatMap } from '../../../lib/formatHandlers/utils/defaultStyles'; -import { EditorContext, ModelToDomContext } from 'roosterjs-content-model-types'; +import { EditorContext } from 'roosterjs-content-model-types'; import { - defaultFormatAppliers, - getFormatAppliers, -} from '../../../lib/formatHandlers/defaultFormatHandlers'; + buildFormatAppliers, + createModelToDomContext, +} from '../../../lib/modelToDom/context/createModelToDomContext'; describe('createModelToDomContext', () => { - const editorContext: EditorContext = {}; - const defaultResult: ModelToDomContext = { - ...editorContext, - regularSelection: { - current: { - block: null, - segment: null, - }, - }, - listFormat: { - threadItemCounts: [], - nodeStack: [], - }, - implicitFormat: {}, - formatAppliers: getFormatAppliers(), - modelHandlers: defaultContentModelHandlers, - defaultImplicitFormatMap: defaultImplicitFormatMap, - defaultModelHandlers: defaultContentModelHandlers, - defaultFormatAppliers: defaultFormatAppliers, - onNodeCreated: undefined, - }; it('no param', () => { const context = createModelToDomContext(); - expect(context).toEqual(defaultResult); + expect(context).toEqual({ + regularSelection: { + current: { + block: null, + segment: null, + }, + }, + listFormat: { + threadItemCounts: [], + nodeStack: [], + }, + implicitFormat: {}, + modelHandlers: defaultContentModelHandlers, + formatAppliers: buildFormatAppliers(), + defaultModelHandlers: defaultContentModelHandlers, + onNodeCreated: undefined, + }); }); - it('with content model context', () => { + it('with editor context', () => { const editorContext: EditorContext = { isDarkMode: true, }; - const context = createModelToDomContext(editorContext); + const context = createModelToDomContext( + undefined, + undefined, + undefined, + undefined, + undefined, + editorContext + ); expect(context).toEqual({ - ...defaultResult, - ...editorContext, + isDarkMode: true, + regularSelection: { + current: { + block: null, + segment: null, + }, + }, + listFormat: { + threadItemCounts: [], + nodeStack: [], + }, + implicitFormat: {}, + modelHandlers: defaultContentModelHandlers, + formatAppliers: buildFormatAppliers(), + defaultModelHandlers: defaultContentModelHandlers, + onNodeCreated: undefined, }); }); @@ -52,43 +66,56 @@ describe('createModelToDomContext', () => { const mockedBoldApplier = 'bold' as any; const mockedBlockApplier = 'block' as any; const mockedBrHandler = 'br' as any; - const mockedAStyle = 'a' as any; const onNodeCreated = 'OnNodeCreated' as any; - const context = createModelToDomContext(undefined, { - formatApplierOverride: { - bold: mockedBoldApplier, - }, - additionalFormatAppliers: { - block: [mockedBlockApplier], - }, - modelHandlerOverride: { + const mockedBaseHandler = 'base' as any; + + const context = createModelToDomContext( + onNodeCreated, + { br: mockedBrHandler, }, - defaultImplicitFormatOverride: { - a: mockedAStyle, + { + bold: mockedBoldApplier, }, - onNodeCreated, - }); + [ + { + block: [mockedBlockApplier], + }, + ], + { + base: mockedBaseHandler, + } as any + ); + + const appliers = buildFormatAppliers(); + + appliers.block[4] = mockedBlockApplier; + appliers.elementBasedSegment[4] = mockedBoldApplier; + appliers.segment[7] = mockedBoldApplier; + appliers.segmentOnBlock[7] = mockedBoldApplier; + appliers.segmentOnTableCell[7] = mockedBoldApplier; - expect(context.regularSelection).toEqual({ - current: { - block: null, - segment: null, + expect(context).toEqual({ + regularSelection: { + current: { + block: null, + segment: null, + }, }, + listFormat: { + threadItemCounts: [], + nodeStack: [], + }, + implicitFormat: {}, + modelHandlers: { + base: mockedBaseHandler, + br: mockedBrHandler, + } as any, + formatAppliers: appliers, + defaultModelHandlers: { + base: mockedBaseHandler, + } as any, + onNodeCreated, }); - expect(context.listFormat).toEqual({ - threadItemCounts: [], - nodeStack: [], - }); - expect(context.implicitFormat).toEqual({}); - expect(context.formatAppliers.block).toEqual([ - ...getFormatAppliers().block, - mockedBlockApplier, - ]); - expect(context.modelHandlers.br).toBe(mockedBrHandler); - expect(context.defaultImplicitFormatMap.a).toEqual(mockedAStyle); - expect(context.defaultModelHandlers).toEqual(defaultContentModelHandlers); - expect(context.defaultFormatAppliers).toEqual(defaultFormatAppliers); - expect(context.onNodeCreated).toBe(onNodeCreated); }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockGroupChildrenTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockGroupChildrenTest.ts index 08c8857d360..57b4c3b9ff3 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockGroupChildrenTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockGroupChildrenTest.ts @@ -21,13 +21,15 @@ describe('handleBlockGroupChildren', () => { beforeEach(() => { handleBlock = jasmine.createSpy('handleBlock').and.callFake(originalHandleBlock); context = createModelToDomContext( + undefined, { - allowCacheElement: true, + block: handleBlock, }, + undefined, + undefined, + undefined, { - modelHandlerOverride: { - block: handleBlock, - }, + allowCacheElement: true, } ); parent = document.createElement('div'); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockTest.ts index eea6ccd1fc3..8a7d09b0b98 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockTest.ts @@ -32,11 +32,9 @@ describe('handleBlock', () => { handleDivider = jasmine.createSpy('handleDivider'); context = createModelToDomContext(undefined, { - modelHandlerOverride: { - entity: handleEntity, - paragraph: handleParagraph, - divider: handleDivider, - }, + entity: handleEntity, + paragraph: handleParagraph, + divider: handleDivider, }); }); @@ -189,12 +187,10 @@ describe('handleBlockGroup', () => { handleGeneralModel = jasmine.createSpy('handleGeneralModel'); context = createModelToDomContext(undefined, { - modelHandlerOverride: { - blockGroupChildren: handleBlockGroupChildren, - listItem: handleListItem, - formatContainer: handleQuote, - general: handleGeneralModel, - }, + blockGroupChildren: handleBlockGroupChildren, + listItem: handleListItem, + formatContainer: handleQuote, + general: handleGeneralModel, }); parent = document.createElement('div'); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleEntityTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleEntityTest.ts index 6424fedd84e..3cea7f6ea5e 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleEntityTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleEntityTest.ts @@ -7,7 +7,7 @@ describe('handleEntity', () => { let context: ModelToDomContext; beforeEach(() => { - context = createModelToDomContext({ + context = createModelToDomContext(undefined, undefined, undefined, undefined, undefined, { allowCacheElement: true, }); spyOn(addDelimiters, 'default').and.callThrough(); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleFormatContainerTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleFormatContainerTest.ts index f4c8c748d43..1688c7e2422 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleFormatContainerTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleFormatContainerTest.ts @@ -17,13 +17,15 @@ describe('handleFormatContainer', () => { beforeEach(() => { handleBlockGroupChildren = jasmine.createSpy('handleBlockGroupChildren'); context = createModelToDomContext( + undefined, { - allowCacheElement: true, + blockGroupChildren: handleBlockGroupChildren, }, + undefined, + undefined, + undefined, { - modelHandlerOverride: { - blockGroupChildren: handleBlockGroupChildren, - }, + allowCacheElement: true, } ); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleGeneralModelTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleGeneralModelTest.ts index 0f4ee2649e7..edf040dd862 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleGeneralModelTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleGeneralModelTest.ts @@ -26,11 +26,9 @@ describe('handleBlockGroup', () => { handleQuote = jasmine.createSpy('handleQuote'); context = createModelToDomContext(undefined, { - modelHandlerOverride: { - blockGroupChildren: handleBlockGroupChildren, - listItem: handleListItem, - formatContainer: handleQuote, - }, + blockGroupChildren: handleBlockGroupChildren, + listItem: handleListItem, + formatContainer: handleQuote, }); parent = document.createElement('div'); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleImageTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleImageTest.ts index 9dae22c4f25..b1fa72dd6c9 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleImageTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleImageTest.ts @@ -16,11 +16,7 @@ describe('handleSegment', () => { beforeEach(() => { handleBlock = jasmine.createSpy('handleBlock'); - context = createModelToDomContext(undefined, { - modelHandlerOverride: { - block: handleBlock, - }, - }); + context = createModelToDomContext(undefined, { block: handleBlock }); }); function runTest( diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleListItemTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleListItemTest.ts index f7603e99f03..5a4926b7c70 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleListItemTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleListItemTest.ts @@ -24,10 +24,8 @@ describe('handleListItem', () => { handleBlockGroupChildren = jasmine.createSpy('handleBlockGroupChildren'); handleList = jasmine.createSpy('handleList').and.callFake(originalHandleList); context = createModelToDomContext(undefined, { - modelHandlerOverride: { - blockGroupChildren: handleBlockGroupChildren, - list: handleList, - }, + blockGroupChildren: handleBlockGroupChildren, + list: handleList, }); spyOn(applyFormat, 'applyFormat').and.callThrough(); @@ -285,15 +283,16 @@ describe('handleListItem without format handler', () => { beforeEach(() => { handleBlockGroupChildren = jasmine.createSpy('handleBlockGroupChildren'); handleList = jasmine.createSpy('handleList').and.callFake(originalHandleList); - context = createModelToDomContext(undefined, { - modelHandlerOverride: { + context = createModelToDomContext( + undefined, + { blockGroupChildren: handleBlockGroupChildren, list: handleList, }, - formatApplierOverride: { + { listItemThread: null, - }, - }); + } + ); spyOn(applyFormat, 'applyFormat').and.callThrough(); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleListTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleListTest.ts index 6757425bace..f673b870e1e 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleListTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleListTest.ts @@ -368,12 +368,11 @@ describe('handleList without format handlers', () => { let parent: HTMLDivElement; beforeEach(() => { - context = createModelToDomContext(undefined, { - formatApplierOverride: { - listLevelThread: null, - dataset: null, - }, + context = createModelToDomContext(undefined, undefined, { + listLevelThread: null, + dataset: null, }); + parent = document.createElement('div'); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts index 32cbe0cb717..ba1d76e39bd 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts @@ -21,13 +21,13 @@ describe('handleParagraph', () => { parent = document.createElement('div'); handleSegment = jasmine.createSpy('handleSegment'); context = createModelToDomContext( + undefined, + { segment: handleSegment }, + undefined, + undefined, + undefined, { allowCacheElement: true, - }, - { - modelHandlerOverride: { - segment: handleSegment, - }, } ); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleSegmentTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleSegmentTest.ts index dafbf64b120..86a67c58496 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleSegmentTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleSegmentTest.ts @@ -30,13 +30,11 @@ describe('handleSegment', () => { handleImage = jasmine.createSpy('handleImage'); context = createModelToDomContext(undefined, { - modelHandlerOverride: { - br: handleBr, - text: handleText, - general: handleGeneralModel, - entity: handleEntity, - image: handleImage, - }, + br: handleBr, + text: handleText, + general: handleGeneralModel, + entity: handleEntity, + image: handleImage, }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts index 08642467bf4..ab13b5a086a 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts @@ -11,7 +11,9 @@ describe('handleTable', () => { beforeEach(() => { spyOn(handleBlock, 'handleBlock'); - context = createModelToDomContext({ allowCacheElement: true }); + context = createModelToDomContext(undefined, undefined, undefined, undefined, undefined, { + allowCacheElement: true, + }); context.darkColorHandler = new DarkColorHandlerImpl(null!, s => 'darkMock: ' + s); }); @@ -234,7 +236,18 @@ describe('handleTable', () => { it('Regular 1 * 1 table, handle dataset', () => { const datasetApplier = jasmine.createSpy('datasetApplier'); - context.formatAppliers.dataset = [datasetApplier]; + context = createModelToDomContext( + undefined, + undefined, + { + dataset: datasetApplier, + }, + undefined, + undefined, + { + darkColorHandler: context.darkColorHandler, + } + ); const div = document.createElement('div'); handleTable( diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/utils/handleSegmentCommonTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/utils/handleSegmentCommonTest.ts index 4c49f72a74b..282967d73fc 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/utils/handleSegmentCommonTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/utils/handleSegmentCommonTest.ts @@ -14,9 +14,7 @@ describe('handleSegmentCommon', () => { fontWeight: 'bold', }); const onNodeCreated = jasmine.createSpy('onNodeCreated'); - const context = createModelToDomContext(undefined, { - onNodeCreated, - }); + const context = createModelToDomContext(onNodeCreated); context.darkColorHandler = new DarkColorHandlerImpl( document.createElement('div'), s => 'darkMock: ' + s @@ -47,9 +45,7 @@ describe('handleSegmentCommon', () => { const container = document.createElement('span'); const segment = createText('test', {}); const onNodeCreated = jasmine.createSpy('onNodeCreated'); - const context = createModelToDomContext(undefined, { - onNodeCreated, - }); + const context = createModelToDomContext(onNodeCreated); handleSegmentCommon(document, parent, container, segment, context); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/ContentModelEditor.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/ContentModelEditor.ts index 7384aaf62ba..48d29bd5e15 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/ContentModelEditor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/ContentModelEditor.ts @@ -8,6 +8,7 @@ import { ContentModelSegmentFormat, DomToModelOption, ModelToDomOption, + OnNodeCreated, } from 'roosterjs-content-model-types'; /** @@ -43,11 +44,16 @@ export default class ContentModelEditor * Set content with content model * @param model The content model to set * @param option Additional options to customize the behavior of Content Model to DOM conversion + * @param onNodeCreated An optional callback that will be called when a DOM node is created */ - setContentModel(model: ContentModelDocument, option?: ModelToDomOption) { + setContentModel( + model: ContentModelDocument, + option?: ModelToDomOption, + onNodeCreated?: OnNodeCreated + ) { const core = this.getCore(); - core.api.setContentModel(core, model, option); + core.api.setContentModel(core, model, option, onNodeCreated); } /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createContentModel.ts index b95b71a5288..0af83212dbb 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createContentModel.ts @@ -1,8 +1,7 @@ import { cloneModel } from '../../modelApi/common/cloneModel'; -import { domToContentModel } from 'roosterjs-content-model-dom'; +import { createDomToModelContext, domToContentModel } from 'roosterjs-content-model-dom'; import { DomToModelOption } from 'roosterjs-content-model-types'; import { SelectionRangeEx } from 'roosterjs-editor-types'; -import { tablePreProcessor } from '../../domToModel/processors/tablePreProcessor'; import { ContentModelEditorCore, CreateContentModel, @@ -11,7 +10,9 @@ import { /** * @internal * Create Content Model from DOM tree in this editor + * @param core The editor core object * @param option The option to customize the behavior of DOM to Content Model conversion + * @param selectionOverride When passed, use this selection range instead of current selection in editor */ export const createContentModel: CreateContentModel = (core, option, selectionOverride) => { let cachedModel = selectionOverride ? null : core.cachedModel; @@ -26,24 +27,18 @@ export const createContentModel: CreateContentModel = (core, option, selectionOv function internalCreateContentModel( core: ContentModelEditorCore, - option: DomToModelOption | undefined, + option?: DomToModelOption, selectionOverride?: SelectionRangeEx ) { - const context: DomToModelOption = { - ...core.defaultDomToModelOptions, - ...option, - }; - - context.processorOverride = { - table: tablePreProcessor, - ...context.processorOverride, - ...option?.processorOverride, - }; - return domToContentModel( core.contentDiv, - context, - core.api.createEditorContext(core), - selectionOverride || core.api.getSelectionRangeEx(core) + createDomToModelContext( + option?.processorOverride, + { ...core.formatParserOverride, ...option?.formatParserOverride }, + [core.additionalFormatParsers, option?.additionalFormatParsers], + core.baseProcessorMap, + selectionOverride || core.api.getSelectionRangeEx(core), + core.api.createEditorContext(core) + ) ); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setContentModel.ts index a3b89e2e617..690faf26d69 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setContentModel.ts @@ -1,22 +1,27 @@ -import { contentModelToDom } from 'roosterjs-content-model-dom'; +import { contentModelToDom, createModelToDomContext } from 'roosterjs-content-model-dom'; import { SetContentModel } from '../../publicTypes/ContentModelEditorCore'; /** * @internal * Set content with content model + * @param core The editor core object * @param model The content model to set * @param option Additional options to customize the behavior of Content Model to DOM conversion + * @param onNodeCreated An optional callback that will be called when a DOM node is created */ -export const setContentModel: SetContentModel = (core, model, option) => { +export const setContentModel: SetContentModel = (core, model, option, onNodeCreated) => { const range = contentModelToDom( core.contentDiv.ownerDocument, core.contentDiv, model, - core.api.createEditorContext(core), - { - ...core.defaultModelToDomOptions, - ...(option || {}), - } + createModelToDomContext( + onNodeCreated, + option?.modelHandlerOverride, + { ...core.formatApplierOverride, ...option?.formatApplierOverride }, + [core.additionalFormatAppliers, option?.additionalFormatAppliers], + core.baseHandlerMap, + core.api.createEditorContext(core) + ) ); if (!core.lifecycle.shadowEditFragment) { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts index aa8bcaca30c..be96e6b8a43 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts @@ -1,11 +1,15 @@ import paste from '../../publicApi/utils/paste'; import { cloneModel } from '../../modelApi/common/cloneModel'; -import { contentModelToDom, normalizeContentModel } from 'roosterjs-content-model-dom'; import { DeleteResult } from '../../modelApi/edit/utils/DeleteSelectionStep'; import { deleteSelection } from '../../modelApi/edit/deleteSelection'; import { formatWithContentModel } from '../../publicApi/utils/formatWithContentModel'; import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { iterateSelections } from '../../modelApi/selection/iterateSelections'; +import { + contentModelToDom, + createModelToDomContext, + normalizeContentModel, +} from 'roosterjs-content-model-dom'; import type { ContentModelBlock, ContentModelBlockGroup, @@ -117,10 +121,7 @@ export default class ContentModelCopyPastePlugin implements PluginWithState = { + h1: '2em', + h2: '1.5em', + h3: '1.17em', + h4: '1em', + h5: '0.83em', + h6: '0.67em', +}; + /** * Set heading level of selected paragraphs * @param editor The editor to set heading level to @@ -22,13 +27,16 @@ export default function setHeadingLevel( headingLevel > 0 ? (('h' + headingLevel) as HeadingLevelTags | null) : getExistingHeadingTag(para.decorator); - const headingStyle = - (tagName && (defaultImplicitFormatMap[tagName] as ContentModelSegmentFormat)) || {}; if (headingLevel > 0) { para.decorator = { tagName: tagName!, - format: { ...headingStyle }, + format: tagName + ? { + fontWeight: 'bold', + fontSize: HeaderFontSizes[tagName], + } + : {}, }; // Remove existing formats since tags have default font size and weight diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/getFormatState.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/getFormatState.ts index 42dbb47f7e3..44fb1f3c151 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/getFormatState.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/getFormatState.ts @@ -6,6 +6,7 @@ import { getSelectionRootNode } from '../../modelApi/selection/getSelectionRootN import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { retrieveModelFormatState } from '../../modelApi/common/retrieveModelFormatState'; import { + childProcessor, getRegularSelectionOffsets, handleRegularSelection, processChildNode, @@ -86,7 +87,7 @@ export function reducedModelChildProcessor( // No child node from node stack, that means we have reached the deepest node of selection. // Now we can use default child processor to perform full sub tree scanning for content model, // So that all selected node will be included. - context.defaultElementProcessors.child(group, parent, context); + childProcessor(group, parent, context); } } } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatWithContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatWithContentModel.ts index 1d3d9ec6a4d..8cb72fd1e5c 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatWithContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatWithContentModel.ts @@ -45,7 +45,7 @@ export function formatWithContentModel( handleDeletedEntities(editor, context); if (model) { - editor.setContentModel(model, { onNodeCreated }); + editor.setContentModel(model, undefined, onNodeCreated); } if (preservePendingFormat) { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/paste.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/paste.ts index 0196320f264..27ff875fbe4 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/paste.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/paste.ts @@ -1,10 +1,10 @@ -import { domToContentModel } from 'roosterjs-content-model-dom'; +import { ContentModelDocument } from 'roosterjs-content-model-types'; +import { createDomToModelContext, domToContentModel } from 'roosterjs-content-model-dom'; import { formatWithContentModel } from './formatWithContentModel'; import { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { mergeModel } from '../../modelApi/common/mergeModel'; import { NodePosition } from 'roosterjs-editor-types'; -import { ContentModelDocument } from 'roosterjs-content-model-types'; import ContentModelBeforePasteEvent, { ContentModelBeforePasteEventData, } from '../../publicTypes/event/ContentModelBeforePasteEvent'; @@ -66,7 +66,14 @@ export default function paste( eventData ); - const pasteModel = domToContentModel(fragment, domToModelOption); + const pasteModel = domToContentModel( + fragment, + createDomToModelContext( + domToModelOption?.processorOverride, + domToModelOption?.formatParserOverride, + [domToModelOption.additionalFormatParsers] + ) + ); if (pasteModel) { formatWithContentModel( diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/ContentModelEditorCore.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/ContentModelEditorCore.ts index 01a8bedd271..c823c2d2ed4 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/ContentModelEditorCore.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/ContentModelEditorCore.ts @@ -1,10 +1,17 @@ import { CoreApiMap, EditorCore, SelectionRangeEx } from 'roosterjs-editor-types'; import { ContentModelDocument, + ContentModelHandlerMap, ContentModelSegmentFormat, DomToModelOption, EditorContext, + ElementProcessorMap, + FormatAppliers, + FormatAppliersPerCategory, + FormatParsers, + FormatParsersPerCategory, ModelToDomOption, + OnNodeCreated, } from 'roosterjs-content-model-types'; /** @@ -17,6 +24,7 @@ export type CreateEditorContext = (core: ContentModelEditorCore) => EditorContex * Create Content Model from DOM tree in this editor * @param core The ContentModelEditorCore object * @param option The option to customize the behavior of DOM to Content Model conversion + * @param selectionOverride When passed, use this selection range instead of current selection in editor */ export type CreateContentModel = ( core: ContentModelEditorCore, @@ -29,11 +37,13 @@ export type CreateContentModel = ( * @param core The ContentModelEditorCore object * @param model The content model to set * @param option Additional options to customize the behavior of Content Model to DOM conversion + * @param onNodeCreated An optional callback that will be called when a DOM node is created */ export type SetContentModel = ( core: ContentModelEditorCore, model: ContentModelDocument, - option?: ModelToDomOption + option?: ModelToDomOption, + onNodeCreated?: OnNodeCreated ) => void; /** @@ -93,14 +103,34 @@ export interface ContentModelEditorCore extends EditorCore { defaultFormat: ContentModelSegmentFormat; /** - * Default DOM to Content Model options + * Overrides default format handlers */ - defaultDomToModelOptions: DomToModelOption; + formatParserOverride?: Partial; /** - * Default Content Model to DOM options + * Provide additional format parsers for each format type */ - defaultModelToDomOptions: ModelToDomOption; + additionalFormatParsers?: Partial; + + /** + * Base processor map to override the default one + */ + baseProcessorMap?: Readonly; + + /** + * Overrides default format appliers + */ + formatApplierOverride?: Partial; + + /** + * Provide additional format appliers for each format type + */ + additionalFormatAppliers?: Partial; + + /** + * Base handler map to override the default one + */ + baseHandlerMap?: Readonly; /** * Whether adding delimiter for entity is allowed diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/IContentModelEditor.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/IContentModelEditor.ts index e62c05de571..4b002f5bdb7 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/IContentModelEditor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/IContentModelEditor.ts @@ -4,6 +4,7 @@ import { ContentModelSegmentFormat, DomToModelOption, ModelToDomOption, + OnNodeCreated, } from 'roosterjs-content-model-types'; /** @@ -27,8 +28,13 @@ export interface IContentModelEditor extends IEditor { * Set content with content model * @param model The content model to set * @param option Additional options to customize the behavior of Content Model to DOM conversion + * @param onNodeCreated An optional callback that will be called when a DOM node is created */ - setContentModel(model: ContentModelDocument, option?: ModelToDomOption): void; + setContentModel( + model: ContentModelDocument, + option?: ModelToDomOption, + onNodeCreated?: OnNodeCreated + ): void; /** * Cache a content model object. Next time when format with content model, we can reuse it. diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/ContentModelEditorTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/ContentModelEditorTest.ts index 6f7b95debcc..338e3136e03 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/ContentModelEditorTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/ContentModelEditorTest.ts @@ -1,9 +1,13 @@ +import * as buildBaseHandlerMap from 'roosterjs-content-model-dom/lib/modelToDom/context/buildBaseHandlerMap'; +import * as buildBaseProcessorMap from 'roosterjs-content-model-dom/lib/domToModel/context/buildBaseProcessorMap'; import * as contentModelToDom from 'roosterjs-content-model-dom/lib/modelToDom/contentModelToDom'; +import * as createDomToModelContext from 'roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext'; +import * as createModelToDomContext from 'roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext'; import * as domToContentModel from 'roosterjs-content-model-dom/lib/domToModel/domToContentModel'; import ContentModelEditor from '../../lib/editor/ContentModelEditor'; import { ContentModelDocument, EditorContext } from 'roosterjs-content-model-types'; import { EditorPlugin, PluginEventType, SelectionRangeTypes } from 'roosterjs-editor-types'; -import { tablePreProcessor } from '../../lib/domToModel/processors/tablePreProcessor'; +import { tablePreProcessor } from '../../lib/editor/overrides/tablePreProcessor'; const editorContext: EditorContext = { isDarkMode: false, @@ -12,66 +16,86 @@ const editorContext: EditorContext = { describe('ContentModelEditor', () => { it('domToContentModel', () => { + const mockedResult = 'Result' as any; + const mockedContext = 'MockedContext' as any; + const mockedBaseProcessor = 'Processors' as any; + + spyOn(domToContentModel, 'domToContentModel').and.returnValue(mockedResult); + spyOn(createDomToModelContext, 'createDomToModelContext').and.returnValue(mockedContext); + spyOn(buildBaseProcessorMap, 'buildBaseProcessorMap').and.returnValue(mockedBaseProcessor); + const div = document.createElement('div'); const editor = new ContentModelEditor(div); - const mockedResult = 'Result' as any; - spyOn((editor as any).core.api, 'createEditorContext').and.returnValue(editorContext); - spyOn(domToContentModel, 'domToContentModel').and.returnValue(mockedResult); const model = editor.createContentModel(); expect(model).toBe(mockedResult); expect(domToContentModel.domToContentModel).toHaveBeenCalledTimes(1); - expect(domToContentModel.domToContentModel).toHaveBeenCalledWith( - div, + expect(domToContentModel.domToContentModel).toHaveBeenCalledWith(div, mockedContext); + expect(buildBaseProcessorMap.buildBaseProcessorMap).toHaveBeenCalledWith( { - processorOverride: { - table: tablePreProcessor, - }, + table: tablePreProcessor, }, - editorContext, + undefined + ); + expect(createDomToModelContext.createDomToModelContext).toHaveBeenCalledWith( + undefined, + {}, + [undefined, undefined], + mockedBaseProcessor, { type: SelectionRangeTypes.Normal, ranges: [], areAllCollapsed: true, - } + }, + editorContext ); }); - it('domToContentModel, with Reuse Content Model dont add disableCacheElement option', () => { + it('domToContentModel, with Reuse Content Model do not add disableCacheElement option', () => { const div = document.createElement('div'); - const editor = new ContentModelEditor(div); const mockedResult = 'Result' as any; + const mockedContext = 'MockedContext' as any; + const mockedBaseProcessor = 'Processors' as any; + + spyOn(buildBaseProcessorMap, 'buildBaseProcessorMap').and.returnValue(mockedBaseProcessor); + + const editor = new ContentModelEditor(div); spyOn((editor as any).core.api, 'createEditorContext').and.returnValue(editorContext); spyOn(domToContentModel, 'domToContentModel').and.returnValue(mockedResult); + spyOn(createDomToModelContext, 'createDomToModelContext').and.returnValue(mockedContext); const model = editor.createContentModel(); expect(model).toBe(mockedResult); expect(domToContentModel.domToContentModel).toHaveBeenCalledTimes(1); - expect(domToContentModel.domToContentModel).toHaveBeenCalledWith( - div, - { - processorOverride: { - table: tablePreProcessor, - }, - }, - editorContext, + expect(domToContentModel.domToContentModel).toHaveBeenCalledWith(div, mockedContext); + expect(createDomToModelContext.createDomToModelContext).toHaveBeenCalledWith( + undefined, + {}, + [undefined, undefined], + mockedBaseProcessor, { type: SelectionRangeTypes.Normal, ranges: [], areAllCollapsed: true, - } + }, + editorContext + ); + expect(buildBaseProcessorMap.buildBaseProcessorMap).toHaveBeenCalledWith( + { + table: tablePreProcessor, + }, + undefined ); }); it('setContentModel with normal selection', () => { const div = document.createElement('div'); - const editor = new ContentModelEditor(div); const mockedFragment = 'Fragment' as any; const mockedRange = { type: SelectionRangeTypes.Normal, @@ -81,9 +105,16 @@ describe('ContentModelEditor', () => { const mockedResult = [mockedFragment, mockedRange, mockedPairs] as any; const mockedModel = 'MockedModel' as any; + const mockedContext = 'MockedContext' as any; + const mockedHandlerMap = 'Handlers' as any; + + spyOn(buildBaseHandlerMap, 'buildBaseHandlerMap').and.returnValue(mockedHandlerMap); + + const editor = new ContentModelEditor(div); spyOn((editor as any).core.api, 'createEditorContext').and.returnValue(editorContext); spyOn(contentModelToDom, 'contentModelToDom').and.returnValue(mockedResult); + spyOn(createModelToDomContext, 'createModelToDomContext').and.returnValue(mockedContext); editor.setContentModel(mockedModel); @@ -92,14 +123,21 @@ describe('ContentModelEditor', () => { document, div, mockedModel, - editorContext, - {} + mockedContext + ); + expect(buildBaseHandlerMap.buildBaseHandlerMap).toHaveBeenCalledWith(undefined); + expect(createModelToDomContext.createModelToDomContext).toHaveBeenCalledWith( + undefined, + undefined, + {}, + [undefined, undefined], + mockedHandlerMap, + editorContext ); }); it('setContentModel', () => { const div = document.createElement('div'); - const editor = new ContentModelEditor(div); const mockedFragment = 'Fragment' as any; const mockedRange = { type: SelectionRangeTypes.Normal, @@ -109,9 +147,16 @@ describe('ContentModelEditor', () => { const mockedResult = [mockedFragment, mockedRange, mockedPairs] as any; const mockedModel = 'MockedModel' as any; + const mockedContext = 'MockedContext' as any; + const mockedHandlerMap = 'Handlers' as any; + + spyOn(buildBaseHandlerMap, 'buildBaseHandlerMap').and.returnValue(mockedHandlerMap); + + const editor = new ContentModelEditor(div); spyOn((editor as any).core.api, 'createEditorContext').and.returnValue(editorContext); spyOn(contentModelToDom, 'contentModelToDom').and.returnValue(mockedResult); + spyOn(createModelToDomContext, 'createModelToDomContext').and.returnValue(mockedContext); editor.setContentModel(mockedModel); @@ -120,8 +165,15 @@ describe('ContentModelEditor', () => { document, div, mockedModel, - editorContext, - {} + mockedContext + ); + expect(createModelToDomContext.createModelToDomContext).toHaveBeenCalledWith( + undefined, + undefined, + {}, + [undefined, undefined], + mockedHandlerMap, + editorContext ); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createContentModelTest.ts index ef735e679a4..37fc1a26698 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createContentModelTest.ts @@ -2,9 +2,8 @@ import * as cloneModel from '../../../lib/modelApi/common/cloneModel'; import * as domToContentModel from 'roosterjs-content-model-dom/lib/domToModel/domToContentModel'; import { ContentModelEditorCore } from '../../../lib/publicTypes/ContentModelEditorCore'; import { createContentModel } from '../../../lib/editor/coreApi/createContentModel'; -import { DomToModelOption } from 'roosterjs-content-model-types'; +import { createDomToModelContext } from 'roosterjs-content-model-dom'; import { SelectionRangeTypes } from 'roosterjs-editor-types'; -import { tablePreProcessor } from '../../../lib/domToModel/processors/tablePreProcessor'; const mockedEditorContext = 'EDITORCONTEXT' as any; const mockedModel = 'MODEL' as any; @@ -42,30 +41,28 @@ describe('createContentModel', () => { }); it('Reuse model, no cache, no shadow edit', () => { - const option: DomToModelOption = {}; - core.cachedModel = undefined; - const model = createContentModel(core, option); + const model = createContentModel(core); expect(createEditorContext).toHaveBeenCalledWith(core); expect(getSelectionRangeEx).toHaveBeenCalledWith(core); expect(domToContentModelSpy).toHaveBeenCalledWith( mockedDiv, - { - processorOverride: { - table: tablePreProcessor, - }, - }, - mockedEditorContext, - null + createDomToModelContext( + undefined, + undefined, + undefined, + undefined, + undefined, + mockedEditorContext + ) ); expect(model).toBe(mockedModel); }); it('Reuse model, no shadow edit', () => { - const option: DomToModelOption = {}; - const model = createContentModel(core, option); + const model = createContentModel(core); expect(createEditorContext).not.toHaveBeenCalled(); expect(getSelectionRangeEx).not.toHaveBeenCalled(); @@ -74,11 +71,9 @@ describe('createContentModel', () => { }); it('Reuse model, with cache, with shadow edit', () => { - const option: DomToModelOption = {}; - core.lifecycle.shadowEditFragment = {} as any; - const model = createContentModel(core, option); + const model = createContentModel(core); expect(cloneModelSpy).toHaveBeenCalledWith(mockedCachedMode, { includeCachedElement: true, @@ -128,16 +123,10 @@ describe('createContentModel with selection', () => { expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith( MockedDiv, - { - processorOverride: { - table: tablePreProcessor, - }, - }, - undefined, - { + createDomToModelContext(undefined, undefined, undefined, undefined, { type: SelectionRangeTypes.Normal, ranges: [MockedRange], - } + } as any) ); }); @@ -160,20 +149,14 @@ describe('createContentModel with selection', () => { expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith( MockedDiv, - { - processorOverride: { - table: tablePreProcessor, - }, - }, - undefined, - { + createDomToModelContext(undefined, undefined, undefined, undefined, { type: SelectionRangeTypes.TableSelection, table: MockedContainer, coordinates: { firstCell: MockedFirstCell, lastCell: MockedLastCell, }, - } + } as any) ); }); @@ -190,16 +173,10 @@ describe('createContentModel with selection', () => { expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith( MockedDiv, - { - processorOverride: { - table: tablePreProcessor, - }, - }, - undefined, - { + createDomToModelContext(undefined, undefined, undefined, undefined, { type: SelectionRangeTypes.ImageSelection, image: MockedContainer, - } + } as any) ); }); @@ -214,16 +191,10 @@ describe('createContentModel with selection', () => { expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith( MockedDiv, - { - processorOverride: { - table: tablePreProcessor, - }, - }, - undefined, - { + createDomToModelContext(undefined, undefined, undefined, undefined, { type: SelectionRangeTypes.Normal, ranges: [], - } + } as any) ); }); @@ -237,15 +208,9 @@ describe('createContentModel with selection', () => { expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith( MockedDiv, - { - processorOverride: { - table: tablePreProcessor, - }, - }, - undefined, - { + createDomToModelContext(undefined, undefined, undefined, undefined, { type: SelectionRangeTypes.TableSelection, - } + } as any) ); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/setContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/setContentModelTest.ts index 6d4c8ddaaea..12bf01e70b4 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/setContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/setContentModelTest.ts @@ -1,10 +1,13 @@ import * as contentModelToDom from 'roosterjs-content-model-dom/lib/modelToDom/contentModelToDom'; +import * as createModelToDomContext from 'roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext'; import { ContentModelEditorCore } from '../../../lib/publicTypes/ContentModelEditorCore'; +import { ModelToDomOption } from 'roosterjs-content-model-types'; import { setContentModel } from '../../../lib/editor/coreApi/setContentModel'; const mockedRange = 'RANGE' as any; const mockedDoc = 'DOCUMENT' as any; const mockedModel = 'MODEL' as any; +const mockedEditorContext = 'EDITORCONTEXT' as any; const mockedContext = 'CONTEXT' as any; const mockedDiv = { ownerDocument: mockedDoc } as any; @@ -12,6 +15,7 @@ describe('setContentModel', () => { let core: ContentModelEditorCore; let contentModelToDomSpy: jasmine.Spy; let createEditorContext: jasmine.Spy; + let createModelToDomContextSpy: jasmine.Spy; let select: jasmine.Spy; let getSelectionRange: jasmine.Spy; @@ -21,7 +25,11 @@ describe('setContentModel', () => { ); createEditorContext = jasmine .createSpy('createEditorContext') - .and.returnValue(mockedContext); + .and.returnValue(mockedEditorContext); + createModelToDomContextSpy = spyOn( + createModelToDomContext, + 'createModelToDomContext' + ).and.returnValue(mockedContext); select = jasmine.createSpy('select'); getSelectionRange = jasmine.createSpy('getSelectionRange'); @@ -39,47 +47,64 @@ describe('setContentModel', () => { it('no default option, no shadow edit', () => { setContentModel(core, mockedModel); - expect(createEditorContext).toHaveBeenCalledWith(core); + expect(createModelToDomContextSpy).toHaveBeenCalledWith( + undefined, + undefined, + {}, + [undefined, undefined], + undefined, + mockedEditorContext + ); expect(contentModelToDomSpy).toHaveBeenCalledWith( mockedDoc, mockedDiv, mockedModel, - mockedContext, - {} + mockedContext ); expect(select).toHaveBeenCalledWith(core, mockedRange); }); it('with default option, no shadow edit', () => { - const defaultOption = { o: 'OPTION' } as any; - core.defaultModelToDomOptions = defaultOption; setContentModel(core, mockedModel); - expect(createEditorContext).toHaveBeenCalledWith(core); + expect(createModelToDomContextSpy).toHaveBeenCalledWith( + undefined, + undefined, + {}, + [undefined, undefined], + undefined, + mockedEditorContext + ); expect(contentModelToDomSpy).toHaveBeenCalledWith( mockedDoc, mockedDiv, mockedModel, - mockedContext, - defaultOption + mockedContext ); expect(select).toHaveBeenCalledWith(core, mockedRange); }); it('with default option, no shadow edit, with additional option', () => { - const defaultOption = { o: 'OPTION' } as any; - const additionalOption = { o: 'OPTION1', o2: 'OPTION2' } as any; + const override = { + block: 'MOCK' as any, + }; + const additionalOption: ModelToDomOption = { modelHandlerOverride: override }; - core.defaultModelToDomOptions = defaultOption; setContentModel(core, mockedModel, additionalOption); - expect(createEditorContext).toHaveBeenCalledWith(core); + expect(createModelToDomContextSpy).toHaveBeenCalledWith( + undefined, + override, + {}, + [undefined, undefined], + undefined, + mockedEditorContext + ); expect(contentModelToDomSpy).toHaveBeenCalledWith( mockedDoc, mockedDiv, mockedModel, - mockedContext, - additionalOption + mockedContext ); expect(select).toHaveBeenCalledWith(core, mockedRange); }); @@ -89,13 +114,19 @@ describe('setContentModel', () => { setContentModel(core, mockedModel); - expect(createEditorContext).toHaveBeenCalledWith(core); + expect(createModelToDomContextSpy).toHaveBeenCalledWith( + undefined, + undefined, + {}, + [undefined, undefined], + undefined, + mockedEditorContext + ); expect(contentModelToDomSpy).toHaveBeenCalledWith( mockedDoc, mockedDiv, mockedModel, - mockedContext, - {} + mockedContext ); expect(select).not.toHaveBeenCalled(); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/createContentModelEditorCoreTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/createContentModelEditorCoreTest.ts index 635b67a9f2d..66940e1a67e 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/createContentModelEditorCoreTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/createContentModelEditorCoreTest.ts @@ -1,8 +1,11 @@ +import * as buildBaseHandlerMap from 'roosterjs-content-model-dom/lib/modelToDom/context/buildBaseHandlerMap'; +import * as buildBaseProcessorMap from 'roosterjs-content-model-dom/lib/domToModel/context/buildBaseProcessorMap'; import * as createEditorCore from 'roosterjs-editor-core/lib/editor/createEditorCore'; import * as isFeatureEnabled from 'roosterjs-editor-core/lib/editor/isFeatureEnabled'; import ContentModelEditPlugin from '../../lib/editor/plugins/ContentModelEditPlugin'; import ContentModelFormatPlugin from '../../lib/editor/plugins/ContentModelFormatPlugin'; import ContentModelTypeInContainerPlugin from '../../lib/editor/corePlugins/ContentModelTypeInContainerPlugin'; +import { ContentModelEditorOptions } from '../../lib/publicTypes/IContentModelEditor'; import { createContentModel } from '../../lib/editor/coreApi/createContentModel'; import { createContentModelEditorCore } from '../../lib/editor/createContentModelEditorCore'; import { createEditorContext } from '../../lib/editor/coreApi/createEditorContext'; @@ -12,6 +15,8 @@ import { setContentModel } from '../../lib/editor/coreApi/setContentModel'; import { switchShadowEdit } from '../../lib/editor/coreApi/switchShadowEdit'; const mockedSwitchShadowEdit = 'SHADOWEDIT' as any; +const mockedProcessorMap = 'PROCESSORMAP' as any; +const mockedHandlerMap = 'HANDLERMAP' as any; describe('createContentModelEditorCore', () => { let createEditorCoreSpy: jasmine.Spy; @@ -21,6 +26,9 @@ describe('createContentModelEditorCore', () => { let copyPastePlugin = 'copyPastePlugin' as any; beforeEach(() => { + spyOn(buildBaseProcessorMap, 'buildBaseProcessorMap').and.returnValue(mockedProcessorMap); + spyOn(buildBaseHandlerMap, 'buildBaseHandlerMap').and.returnValue(mockedHandlerMap); + contentDiv = { style: {}, } as any; @@ -76,8 +84,12 @@ describe('createContentModelEditorCore', () => { createContentModel, setContentModel, }, - defaultDomToModelOptions: {}, - defaultModelToDomOptions: {}, + formatParserOverride: undefined, + additionalFormatParsers: undefined, + formatApplierOverride: undefined, + additionalFormatAppliers: undefined, + baseProcessorMap: mockedProcessorMap, + baseHandlerMap: mockedHandlerMap, defaultFormat: { fontWeight: undefined, italic: undefined, @@ -95,12 +107,12 @@ describe('createContentModelEditorCore', () => { }); it('With additional option', () => { - const defaultDomToModelOptions = { a: '1' } as any; - const defaultModelToDomOptions = { b: '2' } as any; + const processorOverride = { a: '1' } as any; + const modelHandlerOverride = { b: '2' } as any; - const options = { - defaultDomToModelOptions, - defaultModelToDomOptions, + const options: ContentModelEditorOptions = { + defaultDomToModelOptions: { processorOverride }, + defaultModelToDomOptions: { modelHandlerOverride }, corePluginOverride: { copyPaste: copyPastePlugin, }, @@ -108,8 +120,8 @@ describe('createContentModelEditorCore', () => { const core = createContentModelEditorCore(contentDiv, options); expect(createEditorCoreSpy).toHaveBeenCalledWith(contentDiv, { - defaultDomToModelOptions, - defaultModelToDomOptions, + defaultDomToModelOptions: { processorOverride }, + defaultModelToDomOptions: { modelHandlerOverride }, plugins: [new ContentModelFormatPlugin(), new ContentModelEditPlugin()], corePluginOverride: { typeInContainer: new ContentModelTypeInContainerPlugin(), @@ -135,8 +147,12 @@ describe('createContentModelEditorCore', () => { createContentModel, setContentModel, }, - defaultDomToModelOptions, - defaultModelToDomOptions, + formatParserOverride: undefined, + additionalFormatParsers: undefined, + formatApplierOverride: undefined, + additionalFormatAppliers: undefined, + baseProcessorMap: mockedProcessorMap, + baseHandlerMap: mockedHandlerMap, defaultFormat: { fontWeight: undefined, italic: undefined, @@ -205,8 +221,12 @@ describe('createContentModelEditorCore', () => { createContentModel, setContentModel, }, - defaultDomToModelOptions: {}, - defaultModelToDomOptions: {}, + formatParserOverride: undefined, + additionalFormatParsers: undefined, + formatApplierOverride: undefined, + additionalFormatAppliers: undefined, + baseProcessorMap: mockedProcessorMap, + baseHandlerMap: mockedHandlerMap, defaultFormat: { fontWeight: 'bold', italic: true, @@ -257,8 +277,12 @@ describe('createContentModelEditorCore', () => { createContentModel, setContentModel, }, - defaultDomToModelOptions: {}, - defaultModelToDomOptions: {}, + formatParserOverride: undefined, + additionalFormatParsers: undefined, + formatApplierOverride: undefined, + additionalFormatAppliers: undefined, + baseProcessorMap: mockedProcessorMap, + baseHandlerMap: mockedHandlerMap, defaultFormat: { fontWeight: undefined, italic: undefined, @@ -317,8 +341,12 @@ describe('createContentModelEditorCore', () => { createContentModel, setContentModel, }, - defaultDomToModelOptions: {}, - defaultModelToDomOptions: {}, + formatParserOverride: undefined, + additionalFormatParsers: undefined, + formatApplierOverride: undefined, + additionalFormatAppliers: undefined, + baseProcessorMap: mockedProcessorMap, + baseHandlerMap: mockedHandlerMap, defaultFormat: { fontWeight: undefined, italic: undefined, diff --git a/packages-content-model/roosterjs-content-model-editor/test/domToModel/processors/tablePreProcessorTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/overrides/tablePreProcessorTest.ts similarity index 98% rename from packages-content-model/roosterjs-content-model-editor/test/domToModel/processors/tablePreProcessorTest.ts rename to packages-content-model/roosterjs-content-model-editor/test/editor/overrides/tablePreProcessorTest.ts index 348a0688197..89441aa5f49 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/domToModel/processors/tablePreProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/overrides/tablePreProcessorTest.ts @@ -1,7 +1,7 @@ import * as tableProcessor from 'roosterjs-content-model-dom/lib/domToModel/processors/tableProcessor'; import { createContentModelDocument, createDomToModelContext } from 'roosterjs-content-model-dom'; import { SelectionRangeTypes } from 'roosterjs-editor-types'; -import { tablePreProcessor } from '../../../lib/domToModel/processors/tablePreProcessor'; +import { tablePreProcessor } from '../../../lib/editor/overrides/tablePreProcessor'; describe('tablePreProcessor', () => { it('Table without metadata, use Entity', () => { diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts index 639319f96b4..27ee5400fdc 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts @@ -6,6 +6,7 @@ import * as extractClipboardItemsFile from 'roosterjs-editor-dom/lib/clipboard/e import * as iterateSelectionsFile from '../../../lib/modelApi/selection/iterateSelections'; import * as normalizeContentModel from 'roosterjs-content-model-dom/lib/modelApi/common/normalizeContentModel'; import * as PasteFile from '../../../lib/publicApi/utils/paste'; +import { createModelToDomContext } from 'roosterjs-content-model-dom'; import { DeleteResult } from '../../../lib/modelApi/edit/utils/DeleteSelectionStep'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; import ContentModelCopyPastePlugin, { @@ -180,8 +181,7 @@ describe('ContentModelCopyPastePlugin |', () => { document, div, pasteModelValue, - undefined, - { onNodeCreated } + createModelToDomContext(onNodeCreated) ); expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); @@ -239,8 +239,7 @@ describe('ContentModelCopyPastePlugin |', () => { document, div, pasteModelValue, - undefined, - { onNodeCreated } + createModelToDomContext(onNodeCreated) ); expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); @@ -293,8 +292,7 @@ describe('ContentModelCopyPastePlugin |', () => { document, div, pasteModelValue, - undefined, - { onNodeCreated } + createModelToDomContext(onNodeCreated) ); expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); @@ -378,8 +376,7 @@ describe('ContentModelCopyPastePlugin |', () => { document, div, pasteModelValue, - undefined, - { onNodeCreated } + createModelToDomContext(onNodeCreated) ); expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); @@ -393,9 +390,7 @@ describe('ContentModelCopyPastePlugin |', () => { // On Cut Spy expect(undoSnapShotSpy).toHaveBeenCalled(); - expect(setContentModelSpy).toHaveBeenCalledWith(modelValue, { - onNodeCreated: undefined, - }); + expect(setContentModelSpy).toHaveBeenCalledWith(modelValue, undefined); }); it('Selection not Collapsed and table selection', () => { @@ -440,8 +435,7 @@ describe('ContentModelCopyPastePlugin |', () => { document, div, pasteModelValue, - undefined, - { onNodeCreated } + createModelToDomContext(onNodeCreated) ); expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); @@ -457,9 +451,7 @@ describe('ContentModelCopyPastePlugin |', () => { // On Cut Spy expect(undoSnapShotSpy).toHaveBeenCalled(); expect(deleteSelectionsFile.deleteSelection).toHaveBeenCalled(); - expect(setContentModelSpy).toHaveBeenCalledWith(modelValue, { - onNodeCreated: undefined, - }); + expect(setContentModelSpy).toHaveBeenCalledWith(modelValue, undefined); expect(normalizeContentModel.normalizeContentModel).toHaveBeenCalledWith(modelValue); }); @@ -501,8 +493,7 @@ describe('ContentModelCopyPastePlugin |', () => { document, div, pasteModelValue, - undefined, - { onNodeCreated } + createModelToDomContext(onNodeCreated) ); expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); @@ -517,9 +508,7 @@ describe('ContentModelCopyPastePlugin |', () => { // On Cut Spy expect(undoSnapShotSpy).toHaveBeenCalled(); expect(deleteSelectionsFile.deleteSelection).toHaveBeenCalled(); - expect(setContentModelSpy).toHaveBeenCalledWith(modelValue, { - onNodeCreated: undefined, - }); + expect(setContentModelSpy).toHaveBeenCalledWith(modelValue, undefined); expect(normalizeContentModel.normalizeContentModel).toHaveBeenCalledWith(modelValue); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelFormatPluginTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelFormatPluginTest.ts index d25c9c4bff7..0d4d3f1aa75 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelFormatPluginTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelFormatPluginTest.ts @@ -183,7 +183,8 @@ describe('ContentModelFormatPlugin', () => { }, ], }, - { onNodeCreated: undefined } + undefined, + undefined ); expect(pendingFormat.clearPendingFormat).toHaveBeenCalledTimes(1); expect(pendingFormat.clearPendingFormat).toHaveBeenCalledWith(editor); @@ -250,7 +251,8 @@ describe('ContentModelFormatPlugin', () => { }, ], }, - { onNodeCreated: undefined } + undefined, + undefined ); expect(pendingFormat.clearPendingFormat).toHaveBeenCalledTimes(1); expect(pendingFormat.clearPendingFormat).toHaveBeenCalledWith(editor); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromWacTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromWacTest.ts index 74e44490a06..65e49dd0371 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromWacTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromWacTest.ts @@ -1,7 +1,6 @@ import * as processPastedContentWacComponents from '../../../../../lib/editor/plugins/PastePlugin/WacComponents/processPastedContentWacComponents'; import paste from '../../../../../lib/publicApi/utils/paste'; import { ClipboardData } from 'roosterjs-editor-types'; -import { DomToModelOption } from 'roosterjs-content-model-types'; import { expectEqual, initEditor } from './testUtils'; import { IContentModelEditor } from '../../../../../lib/publicTypes/IContentModelEditor'; import { tableProcessor } from 'roosterjs-content-model-dom'; @@ -56,7 +55,7 @@ describe(ID, () => { paste(editor, clipboardData); - const model = editor.createContentModel({ + const model = editor.createContentModel({ processorOverride: { table: tableProcessor, }, diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromWordTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromWordTest.ts index f95dee1e6ec..88ea7f55d75 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromWordTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromWordTest.ts @@ -2,7 +2,6 @@ import * as wordFile from '../../../../../lib/editor/plugins/PastePlugin/WordDes import paste from '../../../../../lib/publicApi/utils/paste'; import { ClipboardData } from 'roosterjs-editor-types'; import { cloneModel } from '../../../../../lib/modelApi/common/cloneModel'; -import { DomToModelOption } from 'roosterjs-content-model-types'; import { expectEqual, initEditor } from './testUtils'; import { IContentModelEditor } from '../../../../../lib/publicTypes/IContentModelEditor'; import { itChromeOnly } from 'roosterjs-editor-dom/test/DomTestHelper'; @@ -40,7 +39,7 @@ describe(ID, () => { paste(editor, clipboardData); const model = cloneModel( - editor.createContentModel({ + editor.createContentModel({ processorOverride: { table: tableProcessor, }, @@ -116,7 +115,7 @@ describe(ID, () => { paste(editor, clipboardData); - const model = editor.createContentModel({ + const model = editor.createContentModel({ processorOverride: { table: tableProcessor, }, diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/linkParserTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/linkParserTest.ts index afc5e6f2e16..b546707155f 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/linkParserTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/linkParserTest.ts @@ -1,8 +1,13 @@ import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { contentModelToDom, domToContentModel } from 'roosterjs-content-model-dom'; import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; import { moveChildNodes } from 'roosterjs-editor-dom'; import { parseLink } from '../../../../lib/editor/plugins/PastePlugin/utils/linkParser'; +import { + contentModelToDom, + createDomToModelContext, + createModelToDomContext, + domToContentModel, +} from 'roosterjs-content-model-dom'; let div: HTMLElement; let fragment: DocumentFragment; @@ -22,7 +27,13 @@ describe('link parser test', () => { link: [parseLink], }; - const model = domToContentModel(fragment, event.domToModelOption); + const model = domToContentModel( + fragment, + createDomToModelContext(undefined, event.domToModelOption.formatParserOverride, [ + event.domToModelOption.additionalFormatParsers, + ]) + ); + if (expectedModel) { expect(model).toEqual(expectedModel); } @@ -31,10 +42,9 @@ describe('link parser test', () => { document, div, model, - { + createModelToDomContext(undefined, undefined, undefined, undefined, undefined, { isDarkMode: false, - }, - {} + }) ); //Assert diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromExcelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromExcelTest.ts index bc27ff924c2..98a912f24f2 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromExcelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromExcelTest.ts @@ -1,9 +1,14 @@ import * as PastePluginFile from '../../../../lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel'; import { Browser, moveChildNodes } from 'roosterjs-editor-dom'; import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { contentModelToDom, domToContentModel } from 'roosterjs-content-model-dom'; import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; import { processPastedContentFromExcel } from '../../../../lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel'; +import { + contentModelToDom, + createDomToModelContext, + createModelToDomContext, + domToContentModel, +} from 'roosterjs-content-model-dom'; let div: HTMLElement; let fragment: DocumentFragment; @@ -22,9 +27,14 @@ describe('processPastedContentFromExcelTest', () => { event.clipboardData.html = source; processPastedContentFromExcel(event, (s: string) => s); - const model = domToContentModel(fragment, { - ...event.domToModelOption, - }); + const model = domToContentModel( + fragment, + createDomToModelContext( + event.domToModelOption.processorOverride, + event.domToModelOption.formatParserOverride, + [event.domToModelOption.additionalFormatParsers] + ) + ); if (expectedModel) { expect(model).toEqual(expectedModel); } @@ -33,10 +43,9 @@ describe('processPastedContentFromExcelTest', () => { document, div, model, - { + createModelToDomContext(undefined, undefined, undefined, undefined, undefined, { isDarkMode: false, - }, - {} + }) ); //Assert diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWacTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWacTest.ts index 19453740ffb..5be6d436754 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWacTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWacTest.ts @@ -1,9 +1,14 @@ import { Browser, moveChildNodes } from 'roosterjs-editor-dom'; import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { contentModelToDom, domToContentModel } from 'roosterjs-content-model-dom'; import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; import { itChromeOnly } from 'roosterjs-editor-dom/test/DomTestHelper'; import { processPastedContentWacComponents } from '../../../../lib/editor/plugins/PastePlugin/WacComponents/processPastedContentWacComponents'; +import { + contentModelToDom, + createDomToModelContext, + createModelToDomContext, + domToContentModel, +} from 'roosterjs-content-model-dom'; let div: HTMLElement; let fragment: DocumentFragment; @@ -20,9 +25,14 @@ describe('processPastedContentFromWacTest', () => { const event = createBeforePasteEventMock(fragment); processPastedContentWacComponents(event); - const model = domToContentModel(fragment, { - ...event.domToModelOption, - }); + const model = domToContentModel( + fragment, + createDomToModelContext( + event.domToModelOption.processorOverride, + event.domToModelOption.formatParserOverride, + [event.domToModelOption.additionalFormatParsers] + ) + ); if (expectedModel) { expect(model).toEqual(expectedModel); } @@ -31,10 +41,9 @@ describe('processPastedContentFromWacTest', () => { document, div, model, - { + createModelToDomContext(undefined, undefined, undefined, undefined, undefined, { isDarkMode: false, - }, - {} + }) ); //Assert @@ -124,9 +133,14 @@ describe('wordOnlineHandler', () => { const event = createBeforePasteEventMock(fragment); processPastedContentWacComponents(event); - const model = domToContentModel(fragment, { - ...event.domToModelOption, - }); + const model = domToContentModel( + fragment, + createDomToModelContext( + event.domToModelOption.processorOverride, + event.domToModelOption.formatParserOverride, + [event.domToModelOption.additionalFormatParsers] + ) + ); if (expectedModel) { expect(model).toEqual(expectedModel); } @@ -135,10 +149,9 @@ describe('wordOnlineHandler', () => { document, div, model, - { + createModelToDomContext(undefined, undefined, undefined, undefined, undefined, { isDarkMode: false, - }, - {} + }) ); //Assert diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWordDesktopTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWordDesktopTest.ts index ff88b07d850..bad85fcd367 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWordDesktopTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWordDesktopTest.ts @@ -1,10 +1,15 @@ import ContentModelBeforePasteEvent from '../../../../lib/publicTypes/event/ContentModelBeforePasteEvent'; import { ClipboardData, PluginEventType } from 'roosterjs-editor-types'; import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { contentModelToDom, domToContentModel } from 'roosterjs-content-model-dom'; import { expectHtml } from 'roosterjs-editor-api/test/TestHelper'; import { moveChildNodes } from 'roosterjs-editor-dom'; import { processPastedContentFromWordDesktop } from '../../../../lib/editor/plugins/PastePlugin/WordDesktop/processPastedContentFromWordDesktop'; +import { + contentModelToDom, + createDomToModelContext, + createModelToDomContext, + domToContentModel, +} from 'roosterjs-content-model-dom'; describe('processPastedContentFromWordDesktopTest', () => { let div: HTMLElement; @@ -25,9 +30,14 @@ describe('processPastedContentFromWordDesktopTest', () => { const event = createBeforePasteEventMock(fragment); processPastedContentFromWordDesktop(event); - const model = domToContentModel(fragment, { - ...event.domToModelOption, - }); + const model = domToContentModel( + fragment, + createDomToModelContext( + event.domToModelOption.processorOverride, + event.domToModelOption.formatParserOverride, + [event.domToModelOption.additionalFormatParsers] + ) + ); if (expectedModel) { expect(model).toEqual(expectedModel); } @@ -36,10 +46,9 @@ describe('processPastedContentFromWordDesktopTest', () => { document, div, model, - { + createModelToDomContext(undefined, undefined, undefined, undefined, undefined, { isDarkMode: false, - }, - {} + }) ); //Assert diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/setAlignmentTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/setAlignmentTest.ts index 1aff35e23a8..847be8f8b05 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/setAlignmentTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/setAlignmentTest.ts @@ -448,7 +448,8 @@ describe('setAlignment in table', () => { blockGroupType: 'Document', blocks: [expectedTable], }, - { onNodeCreated: undefined } + undefined, + undefined ); } } @@ -839,7 +840,8 @@ describe('setAlignment in list', () => { blockGroupType: 'Document', blocks: [expectedList], }, - { onNodeCreated: undefined } + undefined, + undefined ); } } diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/getFormatStateTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/getFormatStateTest.ts index bb228f4fae5..a74dd9201ef 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/getFormatStateTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/getFormatStateTest.ts @@ -1,11 +1,11 @@ import * as getPendingFormat from '../../../lib/modelApi/format/pendingFormat'; import * as retrieveModelFormatState from '../../../lib/modelApi/common/retrieveModelFormatState'; -import getFormatState, { - reducedModelChildProcessor, -} from '../../../lib/publicApi/format/getFormatState'; import { DomToModelContext } from 'roosterjs-content-model-types'; import { FormatState, SelectionRangeTypes } from 'roosterjs-editor-types'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; +import getFormatState, { + reducedModelChildProcessor, +} from '../../../lib/publicApi/format/getFormatState'; import { createContentModelDocument, createDomToModelContext, @@ -44,9 +44,7 @@ describe('getFormatState', () => { editorDiv.innerHTML = html; const selectedNode = editorDiv.querySelector('#' + selectedNodeId); - const context = createDomToModelContext(undefined, { - ...(options || {}), - }); + const context = createDomToModelContext(options.processorOverride); if (selectedNode) { context.rangeEx = { @@ -187,11 +185,7 @@ describe('reducedModelChildProcessor', () => { let context: DomToModelContext; beforeEach(() => { - context = createDomToModelContext(undefined, { - processorOverride: { - child: reducedModelChildProcessor, - }, - }); + context = createDomToModelContext({ child: reducedModelChildProcessor }); }); it('Empty DOM', () => { diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/adjustLinkSelectionTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/adjustLinkSelectionTest.ts index 485077fc64c..c6013bda439 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/adjustLinkSelectionTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/adjustLinkSelectionTest.ts @@ -40,9 +40,7 @@ describe('adjustLinkSelection', () => { if (expectedModel) { expect(setContentModel).toHaveBeenCalledTimes(1); - expect(setContentModel).toHaveBeenCalledWith(expectedModel, { - onNodeCreated: undefined, - }); + expect(setContentModel).toHaveBeenCalledWith(expectedModel, undefined, undefined); } else { expect(setContentModel).not.toHaveBeenCalled(); } diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/insertLinkTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/insertLinkTest.ts index 2a46ec23de4..bdeeb85aae4 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/insertLinkTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/insertLinkTest.ts @@ -45,7 +45,7 @@ describe('insertLink', () => { if (expectedModel) { expect(setContentModel).toHaveBeenCalledTimes(1); expect(setContentModel.calls.argsFor(0)[0]).toEqual(expectedModel); - expect(typeof setContentModel.calls.argsFor(0)[1]!.onNodeCreated).toEqual('function'); + expect(typeof setContentModel.calls.argsFor(0)[2]).toEqual('function'); } else { expect(setContentModel).not.toHaveBeenCalled(); } diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/removeLinkTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/removeLinkTest.ts index 63eb83eaf86..692abc3ec83 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/removeLinkTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/removeLinkTest.ts @@ -33,9 +33,7 @@ describe('removeLink', () => { if (expectedModel) { expect(setContentModel).toHaveBeenCalledTimes(1); - expect(setContentModel).toHaveBeenCalledWith(expectedModel, { - onNodeCreated: undefined, - }); + expect(setContentModel).toHaveBeenCalledWith(expectedModel, undefined, undefined); } else { expect(setContentModel).not.toHaveBeenCalled(); } diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/changeFontSizeTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/changeFontSizeTest.ts index 25846d896db..b8917c891dd 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/changeFontSizeTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/changeFontSizeTest.ts @@ -1,8 +1,8 @@ import * as pendingFormat from '../../../lib/modelApi/format/pendingFormat'; import changeFontSize from '../../../lib/publicApi/segment/changeFontSize'; import { ContentModelDocument } from 'roosterjs-content-model-types'; +import { createDomToModelContext, domToContentModel } from 'roosterjs-content-model-dom'; import { createRange } from 'roosterjs-editor-dom'; -import { domToContentModel } from 'roosterjs-content-model-dom'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; import { segmentTestCommon } from './segmentTestCommon'; import { SelectionRangeTypes } from 'roosterjs-editor-types'; @@ -343,11 +343,14 @@ describe('changeFontSize', () => { const editor = ({ createContentModel: (option: any) => - domToContentModel(div, option, undefined, { - type: SelectionRangeTypes.Normal, - ranges: [createRange(sub)], - areAllCollapsed: false, - }), + domToContentModel( + div, + createDomToModelContext(undefined, undefined, undefined, undefined, { + type: SelectionRangeTypes.Normal, + ranges: [createRange(sub)], + areAllCollapsed: false, + }) + ), addUndoSnapshot, focus: jasmine.createSpy(), setContentModel, @@ -374,7 +377,8 @@ describe('changeFontSize', () => { }, ], }, - { onNodeCreated: undefined } + undefined, + undefined ); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/table/setTableCellShadeTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/table/setTableCellShadeTest.ts index f037f540a66..a9aa5ef207c 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/table/setTableCellShadeTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/table/setTableCellShadeTest.ts @@ -42,7 +42,8 @@ describe('setTableCellShade', () => { blockGroupType: 'Document', blocks: [expectedTable], }, - { onNodeCreated: undefined } + undefined, + undefined ); } else { expect(setContentModel).not.toHaveBeenCalled(); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatWithContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatWithContentModelTest.ts index ee12942f250..2d6166eb90c 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatWithContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatWithContentModelTest.ts @@ -75,7 +75,7 @@ describe('formatWithContentModel', () => { formatApiName: apiName, }); expect(setContentModel).toHaveBeenCalledTimes(1); - expect(setContentModel).toHaveBeenCalledWith(mockedModel, { onNodeCreated: undefined }); + expect(setContentModel).toHaveBeenCalledWith(mockedModel, undefined, undefined); expect(focus).toHaveBeenCalledTimes(1); }); @@ -176,7 +176,7 @@ describe('formatWithContentModel', () => { }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(addUndoSnapshot).toHaveBeenCalled(); - expect(setContentModel).toHaveBeenCalledWith(mockedModel, { onNodeCreated }); + expect(setContentModel).toHaveBeenCalledWith(mockedModel, undefined, onNodeCreated); }); it('Has getChangeData', () => { @@ -191,7 +191,7 @@ describe('formatWithContentModel', () => { rawEvent: undefined, }); expect(createContentModel).toHaveBeenCalledTimes(1); - expect(setContentModel).toHaveBeenCalledWith(mockedModel, { onNodeCreated: undefined }); + expect(setContentModel).toHaveBeenCalledWith(mockedModel, undefined, undefined); expect(addUndoSnapshot).toHaveBeenCalled(); const wrappedCallback = addUndoSnapshot.calls.argsFor(0)[0] as any; diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/pasteTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/pasteTest.ts index 8f6bed17ffb..35c73f0edf0 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/pasteTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/pasteTest.ts @@ -9,7 +9,7 @@ import * as WacComponents from '../../../lib/editor/plugins/PastePlugin/WacCompo import * as WordDesktopFile from '../../../lib/editor/plugins/PastePlugin/WordDesktop/processPastedContentFromWordDesktop'; import ContentModelEditor from '../../../lib/editor/ContentModelEditor'; import ContentModelPastePlugin from '../../../lib/editor/plugins/PastePlugin/ContentModelPastePlugin'; -import { ContentModelDocument, DomToModelOption } from 'roosterjs-content-model-types'; +import { ContentModelDocument } from 'roosterjs-content-model-types'; import { createContentModelDocument, tableProcessor } from 'roosterjs-content-model-dom'; import { expectEqual, initEditor } from '../../editor/plugins/paste/e2e/testUtils'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; @@ -581,7 +581,7 @@ describe('Paste with clipboardData', () => { paste(editor, clipboardData); - const model = editor.createContentModel({ + const model = editor.createContentModel({ processorOverride: { table: tableProcessor, }, @@ -621,7 +621,7 @@ describe('Paste with clipboardData', () => { paste(editor, clipboardData); - const model = editor.createContentModel({ + const model = editor.createContentModel({ processorOverride: { table: tableProcessor, }, @@ -653,7 +653,7 @@ describe('Paste with clipboardData', () => { paste(editor, clipboardData); - const model = editor.createContentModel({ + const model = editor.createContentModel({ processorOverride: { table: tableProcessor, }, diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelOption.ts b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelOption.ts index 7814b6f8ad4..9a358ca0b9f 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelOption.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelOption.ts @@ -1,9 +1,4 @@ -import { - DefaultStyleMap, - ElementProcessorMap, - FormatParsers, - FormatParsersPerCategory, -} from './DomToModelSettings'; +import { ElementProcessorMap, FormatParsers, FormatParsersPerCategory } from './DomToModelSettings'; /** * Options for creating DomToModelContext @@ -14,11 +9,6 @@ export interface DomToModelOption { */ processorOverride?: Partial; - /** - * Overrides default element styles - */ - defaultStyleOverride?: DefaultStyleMap; - /** * Overrides default format handlers */ diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSettings.ts b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSettings.ts index 0041452c466..665cbe8f332 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSettings.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSettings.ts @@ -107,11 +107,6 @@ export interface DomToModelSettings { */ elementProcessors: ElementProcessorMap; - /** - * Map of default styles - */ - defaultStyles: DefaultStyleMap; - /** * Map of format parsers */ @@ -122,10 +117,4 @@ export interface DomToModelSettings { * This provides a way to call original processor from an overridden processor function */ defaultElementProcessors: Readonly; - - /** - * Default format parsers before overriding. - * This provides a way to call original format parser from an overridden parser function - */ - defaultFormatParsers: Readonly; } diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomOption.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomOption.ts index 86fae8a178c..953ee74ea47 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomOption.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomOption.ts @@ -1,9 +1,7 @@ import { ContentModelHandlerMap, - DefaultImplicitFormatMap, FormatAppliers, FormatAppliersPerCategory, - OnNodeCreated, } from './ModelToDomSettings'; /** @@ -24,16 +22,4 @@ export interface ModelToDomOption { * Overrides default model handlers */ modelHandlerOverride?: Partial; - - /** - * Overrides default element styles - */ - defaultImplicitFormatOverride?: DefaultImplicitFormatMap; - - /** - * An optional callback that will be called when a DOM node is created - * @param modelElement The related Content Model element - * @param node The node created for this model element - */ - onNodeCreated?: OnNodeCreated; } diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSettings.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSettings.ts index b96a7867480..a5d8bdc4ac8 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSettings.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSettings.ts @@ -22,9 +22,9 @@ import { FormatHandlerTypeMap, FormatKey } from '../format/FormatHandlerTypeMap' import { ModelToDomContext } from './ModelToDomContext'; /** - * Default implicit format map from tag name (lower case) to segment format + * Default implicit Content Model format map from tag name (lower case) to segment format */ -export type DefaultImplicitFormatMap = Record< +export type DefaultContentModelFormatMap = Record< string, Readonly >; @@ -164,23 +164,12 @@ export interface ModelToDomSettings { */ formatAppliers: FormatAppliersPerCategory; - /** - * Map of default implicit format for segment - */ - defaultImplicitFormatMap: DefaultImplicitFormatMap; - /** * Default Content Model to DOM handlers before overriding. * This provides a way to call original handler from an overridden handler function */ defaultModelHandlers: Readonly; - /** - * Default format parsers before overriding. - * This provides a way to call original format applier from an overridden applier function - */ - defaultFormatAppliers: Readonly; - /** * An optional callback that will be called when a DOM node is created * @param modelElement The related Content Model element diff --git a/packages-content-model/roosterjs-content-model-types/lib/index.ts b/packages-content-model/roosterjs-content-model-types/lib/index.ts index 22e154bf4a5..e72fe4c467b 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/index.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/index.ts @@ -97,7 +97,7 @@ export { Selectable } from './selection/Selectable'; export { ContentModelHandlerMap, - DefaultImplicitFormatMap, + DefaultContentModelFormatMap, FormatAppliers, FormatAppliersPerCategory, OnNodeCreated,