roosterjs-content-model-core 9.22.0 → 9.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/lib/coreApi/createEditorContext/createEditorContext.js +1 -1
  2. package/lib/coreApi/createEditorContext/createEditorContext.js.map +1 -1
  3. package/lib/coreApi/formatContentModel/formatContentModel.js +1 -0
  4. package/lib/coreApi/formatContentModel/formatContentModel.js.map +1 -1
  5. package/lib/corePlugin/cache/CachePlugin.js +14 -8
  6. package/lib/corePlugin/cache/CachePlugin.js.map +1 -1
  7. package/lib/corePlugin/cache/ParagraphMapImpl.d.ts +14 -0
  8. package/lib/corePlugin/cache/ParagraphMapImpl.js +68 -0
  9. package/lib/corePlugin/cache/ParagraphMapImpl.js.map +1 -0
  10. package/lib-amd/coreApi/createEditorContext/createEditorContext.js +1 -1
  11. package/lib-amd/coreApi/createEditorContext/createEditorContext.js.map +1 -1
  12. package/lib-amd/coreApi/formatContentModel/formatContentModel.js +1 -0
  13. package/lib-amd/coreApi/formatContentModel/formatContentModel.js.map +1 -1
  14. package/lib-amd/corePlugin/cache/CachePlugin.js +14 -9
  15. package/lib-amd/corePlugin/cache/CachePlugin.js.map +1 -1
  16. package/lib-amd/corePlugin/cache/ParagraphMapImpl.d.ts +14 -0
  17. package/lib-amd/corePlugin/cache/ParagraphMapImpl.js +69 -0
  18. package/lib-amd/corePlugin/cache/ParagraphMapImpl.js.map +1 -0
  19. package/lib-mjs/coreApi/createEditorContext/createEditorContext.js +1 -1
  20. package/lib-mjs/coreApi/createEditorContext/createEditorContext.js.map +1 -1
  21. package/lib-mjs/coreApi/formatContentModel/formatContentModel.js +1 -0
  22. package/lib-mjs/coreApi/formatContentModel/formatContentModel.js.map +1 -1
  23. package/lib-mjs/corePlugin/cache/CachePlugin.js +14 -8
  24. package/lib-mjs/corePlugin/cache/CachePlugin.js.map +1 -1
  25. package/lib-mjs/corePlugin/cache/ParagraphMapImpl.d.ts +14 -0
  26. package/lib-mjs/corePlugin/cache/ParagraphMapImpl.js +64 -0
  27. package/lib-mjs/corePlugin/cache/ParagraphMapImpl.js.map +1 -0
  28. package/package.json +3 -3
@@ -11,7 +11,7 @@ var createEditorContext = function (core, saveIndex) {
11
11
  var _a, _b;
12
12
  var lifecycle = core.lifecycle, format = core.format, darkColorHandler = core.darkColorHandler, logicalRoot = core.logicalRoot, cache = core.cache, domHelper = core.domHelper;
13
13
  saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;
14
- var context = (0, tslib_1.__assign)({ isDarkMode: lifecycle.isDarkMode, defaultFormat: format.defaultFormat, pendingFormat: (_a = format.pendingFormat) !== null && _a !== void 0 ? _a : undefined, darkColorHandler: darkColorHandler, addDelimiterForEntity: true, allowCacheElement: true, domIndexer: saveIndex ? cache.domIndexer : undefined, zoomScale: domHelper.calculateZoomScale(), experimentalFeatures: (_b = core.experimentalFeatures) !== null && _b !== void 0 ? _b : [] }, (0, getRootComputedStyleForContext_1.getRootComputedStyleForContext)(logicalRoot.ownerDocument));
14
+ var context = (0, tslib_1.__assign)({ isDarkMode: lifecycle.isDarkMode, defaultFormat: format.defaultFormat, pendingFormat: (_a = format.pendingFormat) !== null && _a !== void 0 ? _a : undefined, darkColorHandler: darkColorHandler, addDelimiterForEntity: true, allowCacheElement: true, domIndexer: saveIndex ? cache.domIndexer : undefined, zoomScale: domHelper.calculateZoomScale(), experimentalFeatures: (_b = core.experimentalFeatures) !== null && _b !== void 0 ? _b : [], paragraphMap: core.cache.paragraphMap }, (0, getRootComputedStyleForContext_1.getRootComputedStyleForContext)(logicalRoot.ownerDocument));
15
15
  if (core.domHelper.isRightToLeft()) {
16
16
  context.isRootRtl = true;
17
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"createEditorContext.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/createEditorContext/createEditorContext.ts"],"names":[],"mappings":";;;;AAAA,mFAAkF;AAGlF;;;GAGG;AACI,IAAM,mBAAmB,GAAwB,UAAC,IAAI,EAAE,SAAS;;IAC5D,IAAA,SAAS,GAA8D,IAAI,UAAlE,EAAE,MAAM,GAAsD,IAAI,OAA1D,EAAE,gBAAgB,GAAoC,IAAI,iBAAxC,EAAE,WAAW,GAAuB,IAAI,YAA3B,EAAE,KAAK,GAAgB,IAAI,MAApB,EAAE,SAAS,GAAK,IAAI,UAAT,CAAU;IAEpF,SAAS,GAAG,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;IAE5D,IAAM,OAAO,2BACT,UAAU,EAAE,SAAS,CAAC,UAAU,EAChC,aAAa,EAAE,MAAM,CAAC,aAAa,EACnC,aAAa,EAAE,MAAA,MAAM,CAAC,aAAa,mCAAI,SAAS,EAChD,gBAAgB,EAAE,gBAAgB,EAClC,qBAAqB,EAAE,IAAI,EAC3B,iBAAiB,EAAE,IAAI,EACvB,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EACpD,SAAS,EAAE,SAAS,CAAC,kBAAkB,EAAE,EACzC,oBAAoB,EAAE,MAAA,IAAI,CAAC,oBAAoB,mCAAI,EAAE,IAClD,IAAA,+DAA8B,EAAC,WAAW,CAAC,aAAa,CAAC,CAC/D,CAAC;IAEF,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE;QAChC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;KAC5B;IAED,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC;AAvBW,QAAA,mBAAmB,uBAuB9B","sourcesContent":["import { getRootComputedStyleForContext } from './getRootComputedStyleForContext';\nimport type { EditorContext, CreateEditorContext } from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * Create a EditorContext object used by ContentModel API\n */\nexport const createEditorContext: CreateEditorContext = (core, saveIndex) => {\n const { lifecycle, format, darkColorHandler, logicalRoot, cache, domHelper } = core;\n\n saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;\n\n const context: EditorContext = {\n isDarkMode: lifecycle.isDarkMode,\n defaultFormat: format.defaultFormat,\n pendingFormat: format.pendingFormat ?? undefined,\n darkColorHandler: darkColorHandler,\n addDelimiterForEntity: true,\n allowCacheElement: true,\n domIndexer: saveIndex ? cache.domIndexer : undefined,\n zoomScale: domHelper.calculateZoomScale(),\n experimentalFeatures: core.experimentalFeatures ?? [],\n ...getRootComputedStyleForContext(logicalRoot.ownerDocument),\n };\n\n if (core.domHelper.isRightToLeft()) {\n context.isRootRtl = true;\n }\n\n return context;\n};\n"]}
1
+ {"version":3,"file":"createEditorContext.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/createEditorContext/createEditorContext.ts"],"names":[],"mappings":";;;;AAAA,mFAAkF;AAGlF;;;GAGG;AACI,IAAM,mBAAmB,GAAwB,UAAC,IAAI,EAAE,SAAS;;IAC5D,IAAA,SAAS,GAA8D,IAAI,UAAlE,EAAE,MAAM,GAAsD,IAAI,OAA1D,EAAE,gBAAgB,GAAoC,IAAI,iBAAxC,EAAE,WAAW,GAAuB,IAAI,YAA3B,EAAE,KAAK,GAAgB,IAAI,MAApB,EAAE,SAAS,GAAK,IAAI,UAAT,CAAU;IAEpF,SAAS,GAAG,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;IAE5D,IAAM,OAAO,2BACT,UAAU,EAAE,SAAS,CAAC,UAAU,EAChC,aAAa,EAAE,MAAM,CAAC,aAAa,EACnC,aAAa,EAAE,MAAA,MAAM,CAAC,aAAa,mCAAI,SAAS,EAChD,gBAAgB,EAAE,gBAAgB,EAClC,qBAAqB,EAAE,IAAI,EAC3B,iBAAiB,EAAE,IAAI,EACvB,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EACpD,SAAS,EAAE,SAAS,CAAC,kBAAkB,EAAE,EACzC,oBAAoB,EAAE,MAAA,IAAI,CAAC,oBAAoB,mCAAI,EAAE,EACrD,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,IAClC,IAAA,+DAA8B,EAAC,WAAW,CAAC,aAAa,CAAC,CAC/D,CAAC;IAEF,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE;QAChC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;KAC5B;IAED,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC;AAxBW,QAAA,mBAAmB,uBAwB9B","sourcesContent":["import { getRootComputedStyleForContext } from './getRootComputedStyleForContext';\nimport type { EditorContext, CreateEditorContext } from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * Create a EditorContext object used by ContentModel API\n */\nexport const createEditorContext: CreateEditorContext = (core, saveIndex) => {\n const { lifecycle, format, darkColorHandler, logicalRoot, cache, domHelper } = core;\n\n saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;\n\n const context: EditorContext = {\n isDarkMode: lifecycle.isDarkMode,\n defaultFormat: format.defaultFormat,\n pendingFormat: format.pendingFormat ?? undefined,\n darkColorHandler: darkColorHandler,\n addDelimiterForEntity: true,\n allowCacheElement: true,\n domIndexer: saveIndex ? cache.domIndexer : undefined,\n zoomScale: domHelper.calculateZoomScale(),\n experimentalFeatures: core.experimentalFeatures ?? [],\n paragraphMap: core.cache.paragraphMap,\n ...getRootComputedStyleForContext(logicalRoot.ownerDocument),\n };\n\n if (core.domHelper.isRightToLeft()) {\n context.isRootRtl = true;\n }\n\n return context;\n};\n"]}
@@ -23,6 +23,7 @@ var formatContentModel = function (core, formatter, options, domToModelOptions)
23
23
  deletedEntities: [],
24
24
  rawEvent: rawEvent,
25
25
  newImages: [],
26
+ paragraphIndexer: core.cache.paragraphMap,
26
27
  };
27
28
  var hasFocus = core.domHelper.hasFocus();
28
29
  var changed = formatter(model, context);
@@ -1 +1 @@
1
- {"version":3,"file":"formatContentModel.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeSource } from 'roosterjs-content-model-dom';\nimport { scrollCaretIntoView } from './scrollCaretIntoView';\nimport type {\n ChangedEntity,\n ContentChangedEvent,\n DOMSelection,\n FormatContentModel,\n FormatContentModelContext,\n EditorCore,\n} from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * The general API to do format change with Content Model\n * It will grab a Content Model for current editor content, and invoke a callback function\n * to do format change. Then according to the return value, write back the modified content model into editor.\n * If there is cached model, it will be used and updated.\n * @param core The EditorCore object\n * @param formatter Formatter function, see ContentModelFormatter\n * @param options More options, see FormatContentModelOptions\n */\nexport const formatContentModel: FormatContentModel = (\n core,\n formatter,\n options,\n domToModelOptions\n) => {\n const { onNodeCreated, rawEvent, selectionOverride, scrollCaretIntoView: scroll } =\n options || {};\n const model = core.api.createContentModel(core, domToModelOptions, selectionOverride);\n const context: FormatContentModelContext = {\n newEntities: [],\n deletedEntities: [],\n rawEvent,\n newImages: [],\n };\n\n const hasFocus = core.domHelper.hasFocus();\n\n const changed = formatter(model, context);\n const { skipUndoSnapshot, clearModelCache, entityStates, canUndoByBackspace } = context;\n\n if (changed) {\n const isNested = core.undo.isNested;\n const shouldAddSnapshot =\n (!skipUndoSnapshot || skipUndoSnapshot == 'DoNotSkip') && !isNested;\n const shouldMarkNewContent =\n (skipUndoSnapshot === true || skipUndoSnapshot == 'MarkNewContent') && !isNested;\n let selection: DOMSelection | undefined;\n\n if (shouldAddSnapshot) {\n core.undo.isNested = true;\n\n core.api.addUndoSnapshot(core, !!canUndoByBackspace, entityStates);\n }\n\n try {\n handleImages(core, context);\n\n selection =\n core.api.setContentModel(\n core,\n model,\n hasFocus ? undefined : { ignoreSelection: true }, // If editor did not have focus before format, do not set focus after format\n onNodeCreated\n ) ?? undefined;\n\n handlePendingFormat(core, context, selection);\n\n if (scroll && (selection?.type == 'range' || selection?.type == 'image')) {\n scrollCaretIntoView(core, selection);\n }\n\n const eventData: ContentChangedEvent = {\n eventType: 'contentChanged',\n contentModel: clearModelCache ? undefined : model,\n selection: clearModelCache ? undefined : selection,\n source: options?.changeSource || ChangeSource.Format,\n data: options?.getChangeData?.(),\n formatApiName: options?.apiName,\n changedEntities: getChangedEntities(context, rawEvent),\n };\n\n core.api.triggerEvent(core, eventData, true /*broadcast*/);\n\n if (canUndoByBackspace && selection?.type == 'range') {\n core.undo.autoCompleteInsertPoint = {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n };\n }\n\n if (shouldAddSnapshot) {\n core.api.addUndoSnapshot(core, false /*canUndoByBackspace*/, entityStates);\n }\n\n if (shouldMarkNewContent) {\n core.undo.snapshotsManager.hasNewContent = true;\n }\n } finally {\n if (!isNested) {\n core.undo.isNested = false;\n }\n }\n } else {\n if (clearModelCache) {\n core.cache.cachedModel = undefined;\n core.cache.cachedSelection = undefined;\n }\n\n handlePendingFormat(core, context, core.api.getDOMSelection(core));\n }\n\n if (context.announceData) {\n core.api.announce(core, context.announceData);\n }\n};\n\nfunction handleImages(core: EditorCore, context: FormatContentModelContext) {\n if (context.newImages.length > 0) {\n const width = core.domHelper.getClientWidth();\n const minMaxImageSize = 10;\n const maxWidth = Math.max(width, minMaxImageSize);\n context.newImages.forEach(image => {\n image.format.maxWidth = `${maxWidth}px`;\n });\n }\n}\n\nfunction handlePendingFormat(\n core: EditorCore,\n context: FormatContentModelContext,\n selection?: DOMSelection | null\n) {\n const pendingFormat =\n context.newPendingFormat == 'preserve'\n ? core.format.pendingFormat?.format\n : context.newPendingFormat;\n const pendingParagraphFormat =\n context.newPendingParagraphFormat == 'preserve'\n ? core.format.pendingFormat?.paragraphFormat\n : context.newPendingParagraphFormat;\n\n if (\n (pendingFormat || pendingParagraphFormat) &&\n selection?.type == 'range' &&\n selection.range.collapsed\n ) {\n core.format.pendingFormat = {\n format: pendingFormat ? { ...pendingFormat } : undefined,\n paragraphFormat: pendingParagraphFormat ? { ...pendingParagraphFormat } : undefined,\n insertPoint: {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n },\n };\n }\n}\n\nfunction getChangedEntities(\n context: FormatContentModelContext,\n rawEvent?: Event\n): ChangedEntity[] | undefined {\n return context.autoDetectChangedEntities\n ? undefined\n : context.newEntities\n .map(\n (entity): ChangedEntity => ({\n entity,\n operation: 'newEntity',\n rawEvent,\n })\n )\n .concat(\n context.deletedEntities.map(entry => ({\n entity: entry.entity,\n operation: entry.operation,\n rawEvent,\n }))\n );\n}\n"]}
1
+ {"version":3,"file":"formatContentModel.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeSource } from 'roosterjs-content-model-dom';\nimport { scrollCaretIntoView } from './scrollCaretIntoView';\nimport type {\n ChangedEntity,\n ContentChangedEvent,\n DOMSelection,\n FormatContentModel,\n FormatContentModelContext,\n EditorCore,\n} from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * The general API to do format change with Content Model\n * It will grab a Content Model for current editor content, and invoke a callback function\n * to do format change. Then according to the return value, write back the modified content model into editor.\n * If there is cached model, it will be used and updated.\n * @param core The EditorCore object\n * @param formatter Formatter function, see ContentModelFormatter\n * @param options More options, see FormatContentModelOptions\n */\nexport const formatContentModel: FormatContentModel = (\n core,\n formatter,\n options,\n domToModelOptions\n) => {\n const { onNodeCreated, rawEvent, selectionOverride, scrollCaretIntoView: scroll } =\n options || {};\n const model = core.api.createContentModel(core, domToModelOptions, selectionOverride);\n const context: FormatContentModelContext = {\n newEntities: [],\n deletedEntities: [],\n rawEvent,\n newImages: [],\n paragraphIndexer: core.cache.paragraphMap,\n };\n\n const hasFocus = core.domHelper.hasFocus();\n\n const changed = formatter(model, context);\n const { skipUndoSnapshot, clearModelCache, entityStates, canUndoByBackspace } = context;\n\n if (changed) {\n const isNested = core.undo.isNested;\n const shouldAddSnapshot =\n (!skipUndoSnapshot || skipUndoSnapshot == 'DoNotSkip') && !isNested;\n const shouldMarkNewContent =\n (skipUndoSnapshot === true || skipUndoSnapshot == 'MarkNewContent') && !isNested;\n let selection: DOMSelection | undefined;\n\n if (shouldAddSnapshot) {\n core.undo.isNested = true;\n\n core.api.addUndoSnapshot(core, !!canUndoByBackspace, entityStates);\n }\n\n try {\n handleImages(core, context);\n\n selection =\n core.api.setContentModel(\n core,\n model,\n hasFocus ? undefined : { ignoreSelection: true }, // If editor did not have focus before format, do not set focus after format\n onNodeCreated\n ) ?? undefined;\n\n handlePendingFormat(core, context, selection);\n\n if (scroll && (selection?.type == 'range' || selection?.type == 'image')) {\n scrollCaretIntoView(core, selection);\n }\n\n const eventData: ContentChangedEvent = {\n eventType: 'contentChanged',\n contentModel: clearModelCache ? undefined : model,\n selection: clearModelCache ? undefined : selection,\n source: options?.changeSource || ChangeSource.Format,\n data: options?.getChangeData?.(),\n formatApiName: options?.apiName,\n changedEntities: getChangedEntities(context, rawEvent),\n };\n\n core.api.triggerEvent(core, eventData, true /*broadcast*/);\n\n if (canUndoByBackspace && selection?.type == 'range') {\n core.undo.autoCompleteInsertPoint = {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n };\n }\n\n if (shouldAddSnapshot) {\n core.api.addUndoSnapshot(core, false /*canUndoByBackspace*/, entityStates);\n }\n\n if (shouldMarkNewContent) {\n core.undo.snapshotsManager.hasNewContent = true;\n }\n } finally {\n if (!isNested) {\n core.undo.isNested = false;\n }\n }\n } else {\n if (clearModelCache) {\n core.cache.cachedModel = undefined;\n core.cache.cachedSelection = undefined;\n }\n\n handlePendingFormat(core, context, core.api.getDOMSelection(core));\n }\n\n if (context.announceData) {\n core.api.announce(core, context.announceData);\n }\n};\n\nfunction handleImages(core: EditorCore, context: FormatContentModelContext) {\n if (context.newImages.length > 0) {\n const width = core.domHelper.getClientWidth();\n const minMaxImageSize = 10;\n const maxWidth = Math.max(width, minMaxImageSize);\n context.newImages.forEach(image => {\n image.format.maxWidth = `${maxWidth}px`;\n });\n }\n}\n\nfunction handlePendingFormat(\n core: EditorCore,\n context: FormatContentModelContext,\n selection?: DOMSelection | null\n) {\n const pendingFormat =\n context.newPendingFormat == 'preserve'\n ? core.format.pendingFormat?.format\n : context.newPendingFormat;\n const pendingParagraphFormat =\n context.newPendingParagraphFormat == 'preserve'\n ? core.format.pendingFormat?.paragraphFormat\n : context.newPendingParagraphFormat;\n\n if (\n (pendingFormat || pendingParagraphFormat) &&\n selection?.type == 'range' &&\n selection.range.collapsed\n ) {\n core.format.pendingFormat = {\n format: pendingFormat ? { ...pendingFormat } : undefined,\n paragraphFormat: pendingParagraphFormat ? { ...pendingParagraphFormat } : undefined,\n insertPoint: {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n },\n };\n }\n}\n\nfunction getChangedEntities(\n context: FormatContentModelContext,\n rawEvent?: Event\n): ChangedEntity[] | undefined {\n return context.autoDetectChangedEntities\n ? undefined\n : context.newEntities\n .map(\n (entity): ChangedEntity => ({\n entity,\n operation: 'newEntity',\n rawEvent,\n })\n )\n .concat(\n context.deletedEntities.map(entry => ({\n entity: entry.entity,\n operation: entry.operation,\n rawEvent,\n }))\n );\n}\n"]}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createCachePlugin = void 0;
4
4
  var areSameSelections_1 = require("./areSameSelections");
5
+ var ParagraphMapImpl_1 = require("./ParagraphMapImpl");
5
6
  var textMutationObserver_1 = require("./textMutationObserver");
6
7
  var domIndexerImpl_1 = require("./domIndexerImpl");
7
8
  var updateCache_1 = require("./updateCache");
@@ -47,13 +48,15 @@ var CachePlugin = /** @class */ (function () {
47
48
  _this.updateCachedModel(_this.editor);
48
49
  }
49
50
  };
50
- this.state = option.disableCache
51
- ? {}
52
- : {
53
- domIndexer: new domIndexerImpl_1.DomIndexerImpl(option.experimentalFeatures &&
54
- option.experimentalFeatures.indexOf('PersistCache') >= 0),
55
- textMutationObserver: (0, textMutationObserver_1.createTextMutationObserver)(contentDiv, this.onMutation),
56
- };
51
+ this.state = {};
52
+ if (!option.disableCache) {
53
+ this.state.domIndexer = new domIndexerImpl_1.DomIndexerImpl(option.experimentalFeatures &&
54
+ option.experimentalFeatures.indexOf('PersistCache') >= 0);
55
+ this.state.textMutationObserver = (0, textMutationObserver_1.createTextMutationObserver)(contentDiv, this.onMutation);
56
+ }
57
+ if (option.enableParagraphMap) {
58
+ this.state.paragraphMap = (0, ParagraphMapImpl_1.createParagraphMap)();
59
+ }
57
60
  }
58
61
  /**
59
62
  * Get name of this plugin
@@ -135,10 +138,13 @@ var CachePlugin = /** @class */ (function () {
135
138
  }
136
139
  };
137
140
  CachePlugin.prototype.invalidateCache = function () {
138
- var _a;
141
+ var _a, _b;
139
142
  if (!((_a = this.editor) === null || _a === void 0 ? void 0 : _a.isInShadowEdit())) {
140
143
  this.state.cachedModel = undefined;
141
144
  this.state.cachedSelection = undefined;
145
+ // Clear paragraph indexer to prevent stale references to old paragraphs
146
+ // It will be rebuild next time when we create a new Content Model
147
+ (_b = this.state.paragraphMap) === null || _b === void 0 ? void 0 : _b.clear();
142
148
  }
143
149
  };
144
150
  CachePlugin.prototype.updateCachedModel = function (editor, forceUpdate) {
@@ -1 +1 @@
1
- {"version":3,"file":"CachePlugin.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { areSameSelections } from './areSameSelections';\nimport { createTextMutationObserver } from './textMutationObserver';\nimport { DomIndexerImpl } from './domIndexerImpl';\nimport { updateCache } from './updateCache';\nimport type { Mutation } from './MutationType';\nimport type {\n CachePluginState,\n IEditor,\n PluginEvent,\n PluginWithState,\n EditorOptions,\n} from 'roosterjs-content-model-types';\n\n/**\n * ContentModel cache plugin manages cached Content Model, and refresh the cache when necessary\n */\nclass CachePlugin implements PluginWithState<CachePluginState> {\n private editor: IEditor | null = null;\n private state: CachePluginState;\n\n /**\n * Construct a new instance of CachePlugin class\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\n constructor(option: EditorOptions, contentDiv: HTMLDivElement) {\n this.state = option.disableCache\n ? {}\n : {\n domIndexer: new DomIndexerImpl(\n option.experimentalFeatures &&\n option.experimentalFeatures.indexOf('PersistCache') >= 0\n ),\n textMutationObserver: createTextMutationObserver(contentDiv, this.onMutation),\n };\n }\n\n /**\n * Get name of this plugin\n */\n getName() {\n return 'Cache';\n }\n\n /**\n * The first method that editor will call to a plugin when editor is initializing.\n * It will pass in the editor instance, plugin should take this chance to save the\n * editor reference so that it can call to any editor method or format API later.\n * @param editor The editor object\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.editor.getDocument().addEventListener('selectionchange', this.onNativeSelectionChange);\n\n this.state.textMutationObserver?.startObserving();\n }\n\n /**\n * The last method that editor will call to a plugin before it is disposed.\n * Plugin can take this chance to clear the reference to editor. After this method is\n * called, plugin should not call to any editor method since it will result in error.\n */\n dispose() {\n this.state.textMutationObserver?.stopObserving();\n\n if (this.editor) {\n this.editor\n .getDocument()\n .removeEventListener('selectionchange', this.onNativeSelectionChange);\n this.editor = null;\n }\n }\n\n /**\n * Get plugin state object\n */\n getState(): CachePluginState {\n return this.state;\n }\n\n /**\n * Core method for a plugin. Once an event happens in editor, editor will call this\n * method of each plugin to handle the event as long as the event is not handled\n * exclusively by another plugin.\n * @param event The event to handle:\n */\n onPluginEvent(event: PluginEvent) {\n if (!this.editor) {\n return;\n }\n\n switch (event.eventType) {\n case 'logicalRootChanged':\n this.invalidateCache();\n\n if (this.state.textMutationObserver) {\n this.state.textMutationObserver.stopObserving();\n this.state.textMutationObserver = createTextMutationObserver(\n event.logicalRoot,\n this.onMutation\n );\n this.state.textMutationObserver.startObserving();\n }\n break;\n\n case 'keyDown':\n case 'input':\n if (!this.state.textMutationObserver) {\n // When updating cache is not enabled, need to clear the cache to make sure other plugins can get an up-to-date content model\n this.invalidateCache();\n }\n break;\n\n case 'selectionChanged':\n this.updateCachedModel(this.editor);\n break;\n\n case 'contentChanged':\n const { contentModel, selection } = event;\n\n if (contentModel && this.state.domIndexer) {\n updateCache(this.state, contentModel, selection);\n } else {\n this.invalidateCache();\n }\n\n break;\n }\n }\n\n private onMutation = (mutation: Mutation) => {\n if (this.editor) {\n switch (mutation.type) {\n case 'childList':\n if (\n !this.state.domIndexer?.reconcileChildList(\n mutation.addedNodes,\n mutation.removedNodes\n )\n ) {\n this.invalidateCache();\n }\n break;\n\n case 'text':\n this.updateCachedModel(this.editor, true /*forceUpdate*/);\n break;\n\n case 'elementId':\n const element = mutation.element;\n\n if (!this.state.domIndexer?.reconcileElementId(element)) {\n this.invalidateCache();\n }\n\n break;\n\n case 'unknown':\n this.invalidateCache();\n break;\n }\n }\n };\n\n private onNativeSelectionChange = () => {\n if (this.editor?.hasFocus()) {\n this.updateCachedModel(this.editor);\n }\n };\n\n private invalidateCache() {\n if (!this.editor?.isInShadowEdit()) {\n this.state.cachedModel = undefined;\n this.state.cachedSelection = undefined;\n }\n }\n\n private updateCachedModel(editor: IEditor, forceUpdate?: boolean) {\n if (editor.isInShadowEdit()) {\n return;\n }\n\n const cachedSelection = this.state.cachedSelection;\n this.state.cachedSelection = undefined; // Clear it to force getDOMSelection() retrieve the latest selection range\n\n const newRangeEx = editor.getDOMSelection() || undefined;\n const model = this.state.cachedModel;\n const isSelectionChanged =\n forceUpdate ||\n !cachedSelection ||\n !newRangeEx ||\n !areSameSelections(newRangeEx, cachedSelection);\n\n if (isSelectionChanged) {\n if (\n !model ||\n !newRangeEx ||\n !this.state.domIndexer?.reconcileSelection(model, newRangeEx, cachedSelection)\n ) {\n this.invalidateCache();\n } else {\n updateCache(this.state, model, newRangeEx);\n }\n } else {\n this.state.cachedSelection = cachedSelection;\n }\n }\n}\n\n/**\n * @internal\n * Create a new instance of CachePlugin class.\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\nexport function createCachePlugin(\n option: EditorOptions,\n contentDiv: HTMLDivElement\n): PluginWithState<CachePluginState> {\n return new CachePlugin(option, contentDiv);\n}\n"]}
1
+ {"version":3,"file":"CachePlugin.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { areSameSelections } from './areSameSelections';\nimport { createParagraphMap } from './ParagraphMapImpl';\nimport { createTextMutationObserver } from './textMutationObserver';\nimport { DomIndexerImpl } from './domIndexerImpl';\nimport { updateCache } from './updateCache';\nimport type { Mutation } from './MutationType';\nimport type {\n CachePluginState,\n IEditor,\n PluginEvent,\n PluginWithState,\n EditorOptions,\n} from 'roosterjs-content-model-types';\n\n/**\n * ContentModel cache plugin manages cached Content Model, and refresh the cache when necessary\n */\nclass CachePlugin implements PluginWithState<CachePluginState> {\n private editor: IEditor | null = null;\n private state: CachePluginState;\n\n /**\n * Construct a new instance of CachePlugin class\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\n constructor(option: EditorOptions, contentDiv: HTMLDivElement) {\n this.state = {};\n\n if (!option.disableCache) {\n this.state.domIndexer = new DomIndexerImpl(\n option.experimentalFeatures &&\n option.experimentalFeatures.indexOf('PersistCache') >= 0\n );\n this.state.textMutationObserver = createTextMutationObserver(\n contentDiv,\n this.onMutation\n );\n }\n\n if (option.enableParagraphMap) {\n this.state.paragraphMap = createParagraphMap();\n }\n }\n\n /**\n * Get name of this plugin\n */\n getName() {\n return 'Cache';\n }\n\n /**\n * The first method that editor will call to a plugin when editor is initializing.\n * It will pass in the editor instance, plugin should take this chance to save the\n * editor reference so that it can call to any editor method or format API later.\n * @param editor The editor object\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.editor.getDocument().addEventListener('selectionchange', this.onNativeSelectionChange);\n\n this.state.textMutationObserver?.startObserving();\n }\n\n /**\n * The last method that editor will call to a plugin before it is disposed.\n * Plugin can take this chance to clear the reference to editor. After this method is\n * called, plugin should not call to any editor method since it will result in error.\n */\n dispose() {\n this.state.textMutationObserver?.stopObserving();\n\n if (this.editor) {\n this.editor\n .getDocument()\n .removeEventListener('selectionchange', this.onNativeSelectionChange);\n this.editor = null;\n }\n }\n\n /**\n * Get plugin state object\n */\n getState(): CachePluginState {\n return this.state;\n }\n\n /**\n * Core method for a plugin. Once an event happens in editor, editor will call this\n * method of each plugin to handle the event as long as the event is not handled\n * exclusively by another plugin.\n * @param event The event to handle:\n */\n onPluginEvent(event: PluginEvent) {\n if (!this.editor) {\n return;\n }\n\n switch (event.eventType) {\n case 'logicalRootChanged':\n this.invalidateCache();\n\n if (this.state.textMutationObserver) {\n this.state.textMutationObserver.stopObserving();\n this.state.textMutationObserver = createTextMutationObserver(\n event.logicalRoot,\n this.onMutation\n );\n this.state.textMutationObserver.startObserving();\n }\n break;\n\n case 'keyDown':\n case 'input':\n if (!this.state.textMutationObserver) {\n // When updating cache is not enabled, need to clear the cache to make sure other plugins can get an up-to-date content model\n this.invalidateCache();\n }\n break;\n\n case 'selectionChanged':\n this.updateCachedModel(this.editor);\n break;\n\n case 'contentChanged':\n const { contentModel, selection } = event;\n\n if (contentModel && this.state.domIndexer) {\n updateCache(this.state, contentModel, selection);\n } else {\n this.invalidateCache();\n }\n\n break;\n }\n }\n\n private onMutation = (mutation: Mutation) => {\n if (this.editor) {\n switch (mutation.type) {\n case 'childList':\n if (\n !this.state.domIndexer?.reconcileChildList(\n mutation.addedNodes,\n mutation.removedNodes\n )\n ) {\n this.invalidateCache();\n }\n break;\n\n case 'text':\n this.updateCachedModel(this.editor, true /*forceUpdate*/);\n break;\n\n case 'elementId':\n const element = mutation.element;\n\n if (!this.state.domIndexer?.reconcileElementId(element)) {\n this.invalidateCache();\n }\n\n break;\n\n case 'unknown':\n this.invalidateCache();\n break;\n }\n }\n };\n\n private onNativeSelectionChange = () => {\n if (this.editor?.hasFocus()) {\n this.updateCachedModel(this.editor);\n }\n };\n\n private invalidateCache() {\n if (!this.editor?.isInShadowEdit()) {\n this.state.cachedModel = undefined;\n this.state.cachedSelection = undefined;\n\n // Clear paragraph indexer to prevent stale references to old paragraphs\n // It will be rebuild next time when we create a new Content Model\n this.state.paragraphMap?.clear();\n }\n }\n\n private updateCachedModel(editor: IEditor, forceUpdate?: boolean) {\n if (editor.isInShadowEdit()) {\n return;\n }\n\n const cachedSelection = this.state.cachedSelection;\n this.state.cachedSelection = undefined; // Clear it to force getDOMSelection() retrieve the latest selection range\n\n const newRangeEx = editor.getDOMSelection() || undefined;\n const model = this.state.cachedModel;\n const isSelectionChanged =\n forceUpdate ||\n !cachedSelection ||\n !newRangeEx ||\n !areSameSelections(newRangeEx, cachedSelection);\n\n if (isSelectionChanged) {\n if (\n !model ||\n !newRangeEx ||\n !this.state.domIndexer?.reconcileSelection(model, newRangeEx, cachedSelection)\n ) {\n this.invalidateCache();\n } else {\n updateCache(this.state, model, newRangeEx);\n }\n } else {\n this.state.cachedSelection = cachedSelection;\n }\n }\n}\n\n/**\n * @internal\n * Create a new instance of CachePlugin class.\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\nexport function createCachePlugin(\n option: EditorOptions,\n contentDiv: HTMLDivElement\n): PluginWithState<CachePluginState> {\n return new CachePlugin(option, contentDiv);\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { ParagraphIndexer, ParagraphMap, ReadonlyContentModelParagraph } from 'roosterjs-content-model-types';
2
+ /**
3
+ * @internal, used by test code only
4
+ */
5
+ export interface ParagraphMapReset {
6
+ _reset(): void;
7
+ _getMap(): {
8
+ [key: string]: ReadonlyContentModelParagraph;
9
+ };
10
+ }
11
+ /**
12
+ * @internal
13
+ */
14
+ export declare function createParagraphMap(): ParagraphMap & ParagraphIndexer;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createParagraphMap = void 0;
4
+ var roosterjs_content_model_dom_1 = require("roosterjs-content-model-dom");
5
+ var idPrefix = 'paragraph';
6
+ var ParagraphMapImpl = /** @class */ (function () {
7
+ function ParagraphMapImpl() {
8
+ this.nextId = 0;
9
+ this.paragraphMap = {};
10
+ ParagraphMapImpl.prefixNum++;
11
+ }
12
+ ParagraphMapImpl.prototype.assignMarkerToModel = function (element, paragraph) {
13
+ var marker = (0, roosterjs_content_model_dom_1.getParagraphMarker)(element);
14
+ var paragraphWithMarker = paragraph;
15
+ if (marker) {
16
+ paragraphWithMarker._marker = marker;
17
+ this.paragraphMap[marker] = paragraph;
18
+ }
19
+ else {
20
+ paragraphWithMarker._marker = this.generateId();
21
+ this.applyMarkerToDom(element, paragraph);
22
+ }
23
+ };
24
+ ParagraphMapImpl.prototype.applyMarkerToDom = function (element, paragraph) {
25
+ var paragraphWithMarker = paragraph;
26
+ if (!paragraphWithMarker._marker) {
27
+ paragraphWithMarker._marker = this.generateId();
28
+ }
29
+ var marker = paragraphWithMarker._marker;
30
+ if (marker) {
31
+ (0, roosterjs_content_model_dom_1.setParagraphMarker)(element, marker);
32
+ this.paragraphMap[marker] = paragraph;
33
+ }
34
+ };
35
+ /**
36
+ * Get paragraph using a previously marked paragraph
37
+ * @param markedParagraph The previously marked paragraph to get
38
+ */
39
+ ParagraphMapImpl.prototype.getParagraphFromMarker = function (markerParagraph) {
40
+ var marker = markerParagraph._marker;
41
+ return marker ? this.paragraphMap[marker] || null : null;
42
+ };
43
+ ParagraphMapImpl.prototype.clear = function () {
44
+ this.paragraphMap = {};
45
+ };
46
+ //#region For test code only
47
+ ParagraphMapImpl.prototype._reset = function () {
48
+ ParagraphMapImpl.prefixNum = 0;
49
+ this.nextId = 0;
50
+ };
51
+ ParagraphMapImpl.prototype._getMap = function () {
52
+ return this.paragraphMap;
53
+ };
54
+ //#endregion
55
+ ParagraphMapImpl.prototype.generateId = function () {
56
+ return idPrefix + "_" + ParagraphMapImpl.prefixNum + "_" + this.nextId++;
57
+ };
58
+ ParagraphMapImpl.prefixNum = 0;
59
+ return ParagraphMapImpl;
60
+ }());
61
+ /**
62
+ * @internal
63
+ */
64
+ function createParagraphMap() {
65
+ return new ParagraphMapImpl();
66
+ }
67
+ exports.createParagraphMap = createParagraphMap;
68
+ //# sourceMappingURL=ParagraphMapImpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParagraphMapImpl.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/ParagraphMapImpl.ts"],"names":[],"mappings":";;;AAAA,2EAAqF;AAqBrF,IAAM,QAAQ,GAAG,WAAW,CAAC;AAE7B;IAKI;QAHQ,WAAM,GAAG,CAAC,CAAC;QACX,iBAAY,GAAqD,EAAE,CAAC;QAGxE,gBAAgB,CAAC,SAAS,EAAE,CAAC;IACjC,CAAC;IAED,8CAAmB,GAAnB,UAAoB,OAAoB,EAAE,SAAgC;QACtE,IAAM,MAAM,GAAG,IAAA,gDAAkB,EAAC,OAAO,CAAC,CAAC;QAC3C,IAAM,mBAAmB,GAAG,SAAgC,CAAC;QAE7D,IAAI,MAAM,EAAE;YACR,mBAAmB,CAAC,OAAO,GAAG,MAAM,CAAC;YAErC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;SACzC;aAAM;YACH,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAEhD,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;SAC7C;IACL,CAAC;IAED,2CAAgB,GAAhB,UAAiB,OAAoB,EAAE,SAAgC;QACnE,IAAM,mBAAmB,GAAG,SAAgC,CAAC;QAE7D,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE;YAC9B,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;SACnD;QAED,IAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC;QAE3C,IAAI,MAAM,EAAE;YACR,IAAA,gDAAkB,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAEpC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;SACzC;IACL,CAAC;IAED;;;OAGG;IACH,iDAAsB,GAAtB,UACI,eAA8C;QAE9C,IAAM,MAAM,GAAI,eAAuC,CAAC,OAAO,CAAC;QAEhE,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;IAED,gCAAK,GAAL;QACI,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IAC3B,CAAC;IAED,4BAA4B;IAC5B,iCAAM,GAAN;QACI,gBAAgB,CAAC,SAAS,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,CAAC;IAED,kCAAO,GAAP;QACI,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IACD,YAAY;IAEJ,qCAAU,GAAlB;QACI,OAAU,QAAQ,SAAI,gBAAgB,CAAC,SAAS,SAAI,IAAI,CAAC,MAAM,EAAI,CAAC;IACxE,CAAC;IApEc,0BAAS,GAAG,CAAC,CAAC;IAqEjC,uBAAC;CAAA,AAtED,IAsEC;AAED;;GAEG;AACH,SAAgB,kBAAkB;IAC9B,OAAO,IAAI,gBAAgB,EAAE,CAAC;AAClC,CAAC;AAFD,gDAEC","sourcesContent":["import { getParagraphMarker, setParagraphMarker } from 'roosterjs-content-model-dom';\nimport type {\n ContentModelParagraph,\n ContentModelParagraphCommon,\n ParagraphIndexer,\n ParagraphMap,\n ReadonlyContentModelParagraph,\n} from 'roosterjs-content-model-types';\n\ninterface ParagraphWithMarker extends ContentModelParagraphCommon {\n _marker?: string;\n}\n\n/**\n * @internal, used by test code only\n */\nexport interface ParagraphMapReset {\n _reset(): void;\n _getMap(): { [key: string]: ReadonlyContentModelParagraph };\n}\n\nconst idPrefix = 'paragraph';\n\nclass ParagraphMapImpl implements ParagraphMap, ParagraphIndexer, ParagraphMapReset {\n private static prefixNum = 0;\n private nextId = 0;\n private paragraphMap: { [key: string]: ReadonlyContentModelParagraph } = {};\n\n constructor() {\n ParagraphMapImpl.prefixNum++;\n }\n\n assignMarkerToModel(element: HTMLElement, paragraph: ContentModelParagraph): void {\n const marker = getParagraphMarker(element);\n const paragraphWithMarker = paragraph as ParagraphWithMarker;\n\n if (marker) {\n paragraphWithMarker._marker = marker;\n\n this.paragraphMap[marker] = paragraph;\n } else {\n paragraphWithMarker._marker = this.generateId();\n\n this.applyMarkerToDom(element, paragraph);\n }\n }\n\n applyMarkerToDom(element: HTMLElement, paragraph: ContentModelParagraph): void {\n const paragraphWithMarker = paragraph as ParagraphWithMarker;\n\n if (!paragraphWithMarker._marker) {\n paragraphWithMarker._marker = this.generateId();\n }\n\n const marker = paragraphWithMarker._marker;\n\n if (marker) {\n setParagraphMarker(element, marker);\n\n this.paragraphMap[marker] = paragraph;\n }\n }\n\n /**\n * Get paragraph using a previously marked paragraph\n * @param markedParagraph The previously marked paragraph to get\n */\n getParagraphFromMarker(\n markerParagraph: ReadonlyContentModelParagraph\n ): ReadonlyContentModelParagraph | null {\n const marker = (markerParagraph as ParagraphWithMarker)._marker;\n\n return marker ? this.paragraphMap[marker] || null : null;\n }\n\n clear() {\n this.paragraphMap = {};\n }\n\n //#region For test code only\n _reset() {\n ParagraphMapImpl.prefixNum = 0;\n this.nextId = 0;\n }\n\n _getMap() {\n return this.paragraphMap;\n }\n //#endregion\n\n private generateId() {\n return `${idPrefix}_${ParagraphMapImpl.prefixNum}_${this.nextId++}`;\n }\n}\n\n/**\n * @internal\n */\nexport function createParagraphMap(): ParagraphMap & ParagraphIndexer {\n return new ParagraphMapImpl();\n}\n"]}
@@ -10,7 +10,7 @@ define(["require", "exports", "tslib", "./getRootComputedStyleForContext"], func
10
10
  var _a, _b;
11
11
  var lifecycle = core.lifecycle, format = core.format, darkColorHandler = core.darkColorHandler, logicalRoot = core.logicalRoot, cache = core.cache, domHelper = core.domHelper;
12
12
  saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;
13
- var context = (0, tslib_1.__assign)({ isDarkMode: lifecycle.isDarkMode, defaultFormat: format.defaultFormat, pendingFormat: (_a = format.pendingFormat) !== null && _a !== void 0 ? _a : undefined, darkColorHandler: darkColorHandler, addDelimiterForEntity: true, allowCacheElement: true, domIndexer: saveIndex ? cache.domIndexer : undefined, zoomScale: domHelper.calculateZoomScale(), experimentalFeatures: (_b = core.experimentalFeatures) !== null && _b !== void 0 ? _b : [] }, (0, getRootComputedStyleForContext_1.getRootComputedStyleForContext)(logicalRoot.ownerDocument));
13
+ var context = (0, tslib_1.__assign)({ isDarkMode: lifecycle.isDarkMode, defaultFormat: format.defaultFormat, pendingFormat: (_a = format.pendingFormat) !== null && _a !== void 0 ? _a : undefined, darkColorHandler: darkColorHandler, addDelimiterForEntity: true, allowCacheElement: true, domIndexer: saveIndex ? cache.domIndexer : undefined, zoomScale: domHelper.calculateZoomScale(), experimentalFeatures: (_b = core.experimentalFeatures) !== null && _b !== void 0 ? _b : [], paragraphMap: core.cache.paragraphMap }, (0, getRootComputedStyleForContext_1.getRootComputedStyleForContext)(logicalRoot.ownerDocument));
14
14
  if (core.domHelper.isRightToLeft()) {
15
15
  context.isRootRtl = true;
16
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"createEditorContext.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/createEditorContext/createEditorContext.ts"],"names":[],"mappings":";;;;IAGA;;;OAGG;IACI,IAAM,mBAAmB,GAAwB,UAAC,IAAI,EAAE,SAAS;;QAC5D,IAAA,SAAS,GAA8D,IAAI,UAAlE,EAAE,MAAM,GAAsD,IAAI,OAA1D,EAAE,gBAAgB,GAAoC,IAAI,iBAAxC,EAAE,WAAW,GAAuB,IAAI,YAA3B,EAAE,KAAK,GAAgB,IAAI,MAApB,EAAE,SAAS,GAAK,IAAI,UAAT,CAAU;QAEpF,SAAS,GAAG,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;QAE5D,IAAM,OAAO,2BACT,UAAU,EAAE,SAAS,CAAC,UAAU,EAChC,aAAa,EAAE,MAAM,CAAC,aAAa,EACnC,aAAa,EAAE,MAAA,MAAM,CAAC,aAAa,mCAAI,SAAS,EAChD,gBAAgB,EAAE,gBAAgB,EAClC,qBAAqB,EAAE,IAAI,EAC3B,iBAAiB,EAAE,IAAI,EACvB,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EACpD,SAAS,EAAE,SAAS,CAAC,kBAAkB,EAAE,EACzC,oBAAoB,EAAE,MAAA,IAAI,CAAC,oBAAoB,mCAAI,EAAE,IAClD,IAAA,+DAA8B,EAAC,WAAW,CAAC,aAAa,CAAC,CAC/D,CAAC;QAEF,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE;YAChC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;SAC5B;QAED,OAAO,OAAO,CAAC;IACnB,CAAC,CAAC;IAvBW,QAAA,mBAAmB,uBAuB9B","sourcesContent":["import { getRootComputedStyleForContext } from './getRootComputedStyleForContext';\nimport type { EditorContext, CreateEditorContext } from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * Create a EditorContext object used by ContentModel API\n */\nexport const createEditorContext: CreateEditorContext = (core, saveIndex) => {\n const { lifecycle, format, darkColorHandler, logicalRoot, cache, domHelper } = core;\n\n saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;\n\n const context: EditorContext = {\n isDarkMode: lifecycle.isDarkMode,\n defaultFormat: format.defaultFormat,\n pendingFormat: format.pendingFormat ?? undefined,\n darkColorHandler: darkColorHandler,\n addDelimiterForEntity: true,\n allowCacheElement: true,\n domIndexer: saveIndex ? cache.domIndexer : undefined,\n zoomScale: domHelper.calculateZoomScale(),\n experimentalFeatures: core.experimentalFeatures ?? [],\n ...getRootComputedStyleForContext(logicalRoot.ownerDocument),\n };\n\n if (core.domHelper.isRightToLeft()) {\n context.isRootRtl = true;\n }\n\n return context;\n};\n"]}
1
+ {"version":3,"file":"createEditorContext.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/createEditorContext/createEditorContext.ts"],"names":[],"mappings":";;;;IAGA;;;OAGG;IACI,IAAM,mBAAmB,GAAwB,UAAC,IAAI,EAAE,SAAS;;QAC5D,IAAA,SAAS,GAA8D,IAAI,UAAlE,EAAE,MAAM,GAAsD,IAAI,OAA1D,EAAE,gBAAgB,GAAoC,IAAI,iBAAxC,EAAE,WAAW,GAAuB,IAAI,YAA3B,EAAE,KAAK,GAAgB,IAAI,MAApB,EAAE,SAAS,GAAK,IAAI,UAAT,CAAU;QAEpF,SAAS,GAAG,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;QAE5D,IAAM,OAAO,2BACT,UAAU,EAAE,SAAS,CAAC,UAAU,EAChC,aAAa,EAAE,MAAM,CAAC,aAAa,EACnC,aAAa,EAAE,MAAA,MAAM,CAAC,aAAa,mCAAI,SAAS,EAChD,gBAAgB,EAAE,gBAAgB,EAClC,qBAAqB,EAAE,IAAI,EAC3B,iBAAiB,EAAE,IAAI,EACvB,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EACpD,SAAS,EAAE,SAAS,CAAC,kBAAkB,EAAE,EACzC,oBAAoB,EAAE,MAAA,IAAI,CAAC,oBAAoB,mCAAI,EAAE,EACrD,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,IAClC,IAAA,+DAA8B,EAAC,WAAW,CAAC,aAAa,CAAC,CAC/D,CAAC;QAEF,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE;YAChC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;SAC5B;QAED,OAAO,OAAO,CAAC;IACnB,CAAC,CAAC;IAxBW,QAAA,mBAAmB,uBAwB9B","sourcesContent":["import { getRootComputedStyleForContext } from './getRootComputedStyleForContext';\nimport type { EditorContext, CreateEditorContext } from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * Create a EditorContext object used by ContentModel API\n */\nexport const createEditorContext: CreateEditorContext = (core, saveIndex) => {\n const { lifecycle, format, darkColorHandler, logicalRoot, cache, domHelper } = core;\n\n saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;\n\n const context: EditorContext = {\n isDarkMode: lifecycle.isDarkMode,\n defaultFormat: format.defaultFormat,\n pendingFormat: format.pendingFormat ?? undefined,\n darkColorHandler: darkColorHandler,\n addDelimiterForEntity: true,\n allowCacheElement: true,\n domIndexer: saveIndex ? cache.domIndexer : undefined,\n zoomScale: domHelper.calculateZoomScale(),\n experimentalFeatures: core.experimentalFeatures ?? [],\n paragraphMap: core.cache.paragraphMap,\n ...getRootComputedStyleForContext(logicalRoot.ownerDocument),\n };\n\n if (core.domHelper.isRightToLeft()) {\n context.isRootRtl = true;\n }\n\n return context;\n};\n"]}
@@ -21,6 +21,7 @@ define(["require", "exports", "tslib", "roosterjs-content-model-dom", "./scrollC
21
21
  deletedEntities: [],
22
22
  rawEvent: rawEvent,
23
23
  newImages: [],
24
+ paragraphIndexer: core.cache.paragraphMap,
24
25
  };
25
26
  var hasFocus = core.domHelper.hasFocus();
26
27
  var changed = formatter(model, context);
@@ -1 +1 @@
1
- {"version":3,"file":"formatContentModel.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeSource } from 'roosterjs-content-model-dom';\nimport { scrollCaretIntoView } from './scrollCaretIntoView';\nimport type {\n ChangedEntity,\n ContentChangedEvent,\n DOMSelection,\n FormatContentModel,\n FormatContentModelContext,\n EditorCore,\n} from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * The general API to do format change with Content Model\n * It will grab a Content Model for current editor content, and invoke a callback function\n * to do format change. Then according to the return value, write back the modified content model into editor.\n * If there is cached model, it will be used and updated.\n * @param core The EditorCore object\n * @param formatter Formatter function, see ContentModelFormatter\n * @param options More options, see FormatContentModelOptions\n */\nexport const formatContentModel: FormatContentModel = (\n core,\n formatter,\n options,\n domToModelOptions\n) => {\n const { onNodeCreated, rawEvent, selectionOverride, scrollCaretIntoView: scroll } =\n options || {};\n const model = core.api.createContentModel(core, domToModelOptions, selectionOverride);\n const context: FormatContentModelContext = {\n newEntities: [],\n deletedEntities: [],\n rawEvent,\n newImages: [],\n };\n\n const hasFocus = core.domHelper.hasFocus();\n\n const changed = formatter(model, context);\n const { skipUndoSnapshot, clearModelCache, entityStates, canUndoByBackspace } = context;\n\n if (changed) {\n const isNested = core.undo.isNested;\n const shouldAddSnapshot =\n (!skipUndoSnapshot || skipUndoSnapshot == 'DoNotSkip') && !isNested;\n const shouldMarkNewContent =\n (skipUndoSnapshot === true || skipUndoSnapshot == 'MarkNewContent') && !isNested;\n let selection: DOMSelection | undefined;\n\n if (shouldAddSnapshot) {\n core.undo.isNested = true;\n\n core.api.addUndoSnapshot(core, !!canUndoByBackspace, entityStates);\n }\n\n try {\n handleImages(core, context);\n\n selection =\n core.api.setContentModel(\n core,\n model,\n hasFocus ? undefined : { ignoreSelection: true }, // If editor did not have focus before format, do not set focus after format\n onNodeCreated\n ) ?? undefined;\n\n handlePendingFormat(core, context, selection);\n\n if (scroll && (selection?.type == 'range' || selection?.type == 'image')) {\n scrollCaretIntoView(core, selection);\n }\n\n const eventData: ContentChangedEvent = {\n eventType: 'contentChanged',\n contentModel: clearModelCache ? undefined : model,\n selection: clearModelCache ? undefined : selection,\n source: options?.changeSource || ChangeSource.Format,\n data: options?.getChangeData?.(),\n formatApiName: options?.apiName,\n changedEntities: getChangedEntities(context, rawEvent),\n };\n\n core.api.triggerEvent(core, eventData, true /*broadcast*/);\n\n if (canUndoByBackspace && selection?.type == 'range') {\n core.undo.autoCompleteInsertPoint = {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n };\n }\n\n if (shouldAddSnapshot) {\n core.api.addUndoSnapshot(core, false /*canUndoByBackspace*/, entityStates);\n }\n\n if (shouldMarkNewContent) {\n core.undo.snapshotsManager.hasNewContent = true;\n }\n } finally {\n if (!isNested) {\n core.undo.isNested = false;\n }\n }\n } else {\n if (clearModelCache) {\n core.cache.cachedModel = undefined;\n core.cache.cachedSelection = undefined;\n }\n\n handlePendingFormat(core, context, core.api.getDOMSelection(core));\n }\n\n if (context.announceData) {\n core.api.announce(core, context.announceData);\n }\n};\n\nfunction handleImages(core: EditorCore, context: FormatContentModelContext) {\n if (context.newImages.length > 0) {\n const width = core.domHelper.getClientWidth();\n const minMaxImageSize = 10;\n const maxWidth = Math.max(width, minMaxImageSize);\n context.newImages.forEach(image => {\n image.format.maxWidth = `${maxWidth}px`;\n });\n }\n}\n\nfunction handlePendingFormat(\n core: EditorCore,\n context: FormatContentModelContext,\n selection?: DOMSelection | null\n) {\n const pendingFormat =\n context.newPendingFormat == 'preserve'\n ? core.format.pendingFormat?.format\n : context.newPendingFormat;\n const pendingParagraphFormat =\n context.newPendingParagraphFormat == 'preserve'\n ? core.format.pendingFormat?.paragraphFormat\n : context.newPendingParagraphFormat;\n\n if (\n (pendingFormat || pendingParagraphFormat) &&\n selection?.type == 'range' &&\n selection.range.collapsed\n ) {\n core.format.pendingFormat = {\n format: pendingFormat ? { ...pendingFormat } : undefined,\n paragraphFormat: pendingParagraphFormat ? { ...pendingParagraphFormat } : undefined,\n insertPoint: {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n },\n };\n }\n}\n\nfunction getChangedEntities(\n context: FormatContentModelContext,\n rawEvent?: Event\n): ChangedEntity[] | undefined {\n return context.autoDetectChangedEntities\n ? undefined\n : context.newEntities\n .map(\n (entity): ChangedEntity => ({\n entity,\n operation: 'newEntity',\n rawEvent,\n })\n )\n .concat(\n context.deletedEntities.map(entry => ({\n entity: entry.entity,\n operation: entry.operation,\n rawEvent,\n }))\n );\n}\n"]}
1
+ {"version":3,"file":"formatContentModel.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeSource } from 'roosterjs-content-model-dom';\nimport { scrollCaretIntoView } from './scrollCaretIntoView';\nimport type {\n ChangedEntity,\n ContentChangedEvent,\n DOMSelection,\n FormatContentModel,\n FormatContentModelContext,\n EditorCore,\n} from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * The general API to do format change with Content Model\n * It will grab a Content Model for current editor content, and invoke a callback function\n * to do format change. Then according to the return value, write back the modified content model into editor.\n * If there is cached model, it will be used and updated.\n * @param core The EditorCore object\n * @param formatter Formatter function, see ContentModelFormatter\n * @param options More options, see FormatContentModelOptions\n */\nexport const formatContentModel: FormatContentModel = (\n core,\n formatter,\n options,\n domToModelOptions\n) => {\n const { onNodeCreated, rawEvent, selectionOverride, scrollCaretIntoView: scroll } =\n options || {};\n const model = core.api.createContentModel(core, domToModelOptions, selectionOverride);\n const context: FormatContentModelContext = {\n newEntities: [],\n deletedEntities: [],\n rawEvent,\n newImages: [],\n paragraphIndexer: core.cache.paragraphMap,\n };\n\n const hasFocus = core.domHelper.hasFocus();\n\n const changed = formatter(model, context);\n const { skipUndoSnapshot, clearModelCache, entityStates, canUndoByBackspace } = context;\n\n if (changed) {\n const isNested = core.undo.isNested;\n const shouldAddSnapshot =\n (!skipUndoSnapshot || skipUndoSnapshot == 'DoNotSkip') && !isNested;\n const shouldMarkNewContent =\n (skipUndoSnapshot === true || skipUndoSnapshot == 'MarkNewContent') && !isNested;\n let selection: DOMSelection | undefined;\n\n if (shouldAddSnapshot) {\n core.undo.isNested = true;\n\n core.api.addUndoSnapshot(core, !!canUndoByBackspace, entityStates);\n }\n\n try {\n handleImages(core, context);\n\n selection =\n core.api.setContentModel(\n core,\n model,\n hasFocus ? undefined : { ignoreSelection: true }, // If editor did not have focus before format, do not set focus after format\n onNodeCreated\n ) ?? undefined;\n\n handlePendingFormat(core, context, selection);\n\n if (scroll && (selection?.type == 'range' || selection?.type == 'image')) {\n scrollCaretIntoView(core, selection);\n }\n\n const eventData: ContentChangedEvent = {\n eventType: 'contentChanged',\n contentModel: clearModelCache ? undefined : model,\n selection: clearModelCache ? undefined : selection,\n source: options?.changeSource || ChangeSource.Format,\n data: options?.getChangeData?.(),\n formatApiName: options?.apiName,\n changedEntities: getChangedEntities(context, rawEvent),\n };\n\n core.api.triggerEvent(core, eventData, true /*broadcast*/);\n\n if (canUndoByBackspace && selection?.type == 'range') {\n core.undo.autoCompleteInsertPoint = {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n };\n }\n\n if (shouldAddSnapshot) {\n core.api.addUndoSnapshot(core, false /*canUndoByBackspace*/, entityStates);\n }\n\n if (shouldMarkNewContent) {\n core.undo.snapshotsManager.hasNewContent = true;\n }\n } finally {\n if (!isNested) {\n core.undo.isNested = false;\n }\n }\n } else {\n if (clearModelCache) {\n core.cache.cachedModel = undefined;\n core.cache.cachedSelection = undefined;\n }\n\n handlePendingFormat(core, context, core.api.getDOMSelection(core));\n }\n\n if (context.announceData) {\n core.api.announce(core, context.announceData);\n }\n};\n\nfunction handleImages(core: EditorCore, context: FormatContentModelContext) {\n if (context.newImages.length > 0) {\n const width = core.domHelper.getClientWidth();\n const minMaxImageSize = 10;\n const maxWidth = Math.max(width, minMaxImageSize);\n context.newImages.forEach(image => {\n image.format.maxWidth = `${maxWidth}px`;\n });\n }\n}\n\nfunction handlePendingFormat(\n core: EditorCore,\n context: FormatContentModelContext,\n selection?: DOMSelection | null\n) {\n const pendingFormat =\n context.newPendingFormat == 'preserve'\n ? core.format.pendingFormat?.format\n : context.newPendingFormat;\n const pendingParagraphFormat =\n context.newPendingParagraphFormat == 'preserve'\n ? core.format.pendingFormat?.paragraphFormat\n : context.newPendingParagraphFormat;\n\n if (\n (pendingFormat || pendingParagraphFormat) &&\n selection?.type == 'range' &&\n selection.range.collapsed\n ) {\n core.format.pendingFormat = {\n format: pendingFormat ? { ...pendingFormat } : undefined,\n paragraphFormat: pendingParagraphFormat ? { ...pendingParagraphFormat } : undefined,\n insertPoint: {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n },\n };\n }\n}\n\nfunction getChangedEntities(\n context: FormatContentModelContext,\n rawEvent?: Event\n): ChangedEntity[] | undefined {\n return context.autoDetectChangedEntities\n ? undefined\n : context.newEntities\n .map(\n (entity): ChangedEntity => ({\n entity,\n operation: 'newEntity',\n rawEvent,\n })\n )\n .concat(\n context.deletedEntities.map(entry => ({\n entity: entry.entity,\n operation: entry.operation,\n rawEvent,\n }))\n );\n}\n"]}
@@ -1,4 +1,4 @@
1
- define(["require", "exports", "./areSameSelections", "./textMutationObserver", "./domIndexerImpl", "./updateCache"], function (require, exports, areSameSelections_1, textMutationObserver_1, domIndexerImpl_1, updateCache_1) {
1
+ define(["require", "exports", "./areSameSelections", "./ParagraphMapImpl", "./textMutationObserver", "./domIndexerImpl", "./updateCache"], function (require, exports, areSameSelections_1, ParagraphMapImpl_1, textMutationObserver_1, domIndexerImpl_1, updateCache_1) {
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.createCachePlugin = void 0;
@@ -44,13 +44,15 @@ define(["require", "exports", "./areSameSelections", "./textMutationObserver", "
44
44
  _this.updateCachedModel(_this.editor);
45
45
  }
46
46
  };
47
- this.state = option.disableCache
48
- ? {}
49
- : {
50
- domIndexer: new domIndexerImpl_1.DomIndexerImpl(option.experimentalFeatures &&
51
- option.experimentalFeatures.indexOf('PersistCache') >= 0),
52
- textMutationObserver: (0, textMutationObserver_1.createTextMutationObserver)(contentDiv, this.onMutation),
53
- };
47
+ this.state = {};
48
+ if (!option.disableCache) {
49
+ this.state.domIndexer = new domIndexerImpl_1.DomIndexerImpl(option.experimentalFeatures &&
50
+ option.experimentalFeatures.indexOf('PersistCache') >= 0);
51
+ this.state.textMutationObserver = (0, textMutationObserver_1.createTextMutationObserver)(contentDiv, this.onMutation);
52
+ }
53
+ if (option.enableParagraphMap) {
54
+ this.state.paragraphMap = (0, ParagraphMapImpl_1.createParagraphMap)();
55
+ }
54
56
  }
55
57
  /**
56
58
  * Get name of this plugin
@@ -132,10 +134,13 @@ define(["require", "exports", "./areSameSelections", "./textMutationObserver", "
132
134
  }
133
135
  };
134
136
  CachePlugin.prototype.invalidateCache = function () {
135
- var _a;
137
+ var _a, _b;
136
138
  if (!((_a = this.editor) === null || _a === void 0 ? void 0 : _a.isInShadowEdit())) {
137
139
  this.state.cachedModel = undefined;
138
140
  this.state.cachedSelection = undefined;
141
+ // Clear paragraph indexer to prevent stale references to old paragraphs
142
+ // It will be rebuild next time when we create a new Content Model
143
+ (_b = this.state.paragraphMap) === null || _b === void 0 ? void 0 : _b.clear();
139
144
  }
140
145
  };
141
146
  CachePlugin.prototype.updateCachedModel = function (editor, forceUpdate) {
@@ -1 +1 @@
1
- {"version":3,"file":"CachePlugin.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { areSameSelections } from './areSameSelections';\nimport { createTextMutationObserver } from './textMutationObserver';\nimport { DomIndexerImpl } from './domIndexerImpl';\nimport { updateCache } from './updateCache';\nimport type { Mutation } from './MutationType';\nimport type {\n CachePluginState,\n IEditor,\n PluginEvent,\n PluginWithState,\n EditorOptions,\n} from 'roosterjs-content-model-types';\n\n/**\n * ContentModel cache plugin manages cached Content Model, and refresh the cache when necessary\n */\nclass CachePlugin implements PluginWithState<CachePluginState> {\n private editor: IEditor | null = null;\n private state: CachePluginState;\n\n /**\n * Construct a new instance of CachePlugin class\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\n constructor(option: EditorOptions, contentDiv: HTMLDivElement) {\n this.state = option.disableCache\n ? {}\n : {\n domIndexer: new DomIndexerImpl(\n option.experimentalFeatures &&\n option.experimentalFeatures.indexOf('PersistCache') >= 0\n ),\n textMutationObserver: createTextMutationObserver(contentDiv, this.onMutation),\n };\n }\n\n /**\n * Get name of this plugin\n */\n getName() {\n return 'Cache';\n }\n\n /**\n * The first method that editor will call to a plugin when editor is initializing.\n * It will pass in the editor instance, plugin should take this chance to save the\n * editor reference so that it can call to any editor method or format API later.\n * @param editor The editor object\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.editor.getDocument().addEventListener('selectionchange', this.onNativeSelectionChange);\n\n this.state.textMutationObserver?.startObserving();\n }\n\n /**\n * The last method that editor will call to a plugin before it is disposed.\n * Plugin can take this chance to clear the reference to editor. After this method is\n * called, plugin should not call to any editor method since it will result in error.\n */\n dispose() {\n this.state.textMutationObserver?.stopObserving();\n\n if (this.editor) {\n this.editor\n .getDocument()\n .removeEventListener('selectionchange', this.onNativeSelectionChange);\n this.editor = null;\n }\n }\n\n /**\n * Get plugin state object\n */\n getState(): CachePluginState {\n return this.state;\n }\n\n /**\n * Core method for a plugin. Once an event happens in editor, editor will call this\n * method of each plugin to handle the event as long as the event is not handled\n * exclusively by another plugin.\n * @param event The event to handle:\n */\n onPluginEvent(event: PluginEvent) {\n if (!this.editor) {\n return;\n }\n\n switch (event.eventType) {\n case 'logicalRootChanged':\n this.invalidateCache();\n\n if (this.state.textMutationObserver) {\n this.state.textMutationObserver.stopObserving();\n this.state.textMutationObserver = createTextMutationObserver(\n event.logicalRoot,\n this.onMutation\n );\n this.state.textMutationObserver.startObserving();\n }\n break;\n\n case 'keyDown':\n case 'input':\n if (!this.state.textMutationObserver) {\n // When updating cache is not enabled, need to clear the cache to make sure other plugins can get an up-to-date content model\n this.invalidateCache();\n }\n break;\n\n case 'selectionChanged':\n this.updateCachedModel(this.editor);\n break;\n\n case 'contentChanged':\n const { contentModel, selection } = event;\n\n if (contentModel && this.state.domIndexer) {\n updateCache(this.state, contentModel, selection);\n } else {\n this.invalidateCache();\n }\n\n break;\n }\n }\n\n private onMutation = (mutation: Mutation) => {\n if (this.editor) {\n switch (mutation.type) {\n case 'childList':\n if (\n !this.state.domIndexer?.reconcileChildList(\n mutation.addedNodes,\n mutation.removedNodes\n )\n ) {\n this.invalidateCache();\n }\n break;\n\n case 'text':\n this.updateCachedModel(this.editor, true /*forceUpdate*/);\n break;\n\n case 'elementId':\n const element = mutation.element;\n\n if (!this.state.domIndexer?.reconcileElementId(element)) {\n this.invalidateCache();\n }\n\n break;\n\n case 'unknown':\n this.invalidateCache();\n break;\n }\n }\n };\n\n private onNativeSelectionChange = () => {\n if (this.editor?.hasFocus()) {\n this.updateCachedModel(this.editor);\n }\n };\n\n private invalidateCache() {\n if (!this.editor?.isInShadowEdit()) {\n this.state.cachedModel = undefined;\n this.state.cachedSelection = undefined;\n }\n }\n\n private updateCachedModel(editor: IEditor, forceUpdate?: boolean) {\n if (editor.isInShadowEdit()) {\n return;\n }\n\n const cachedSelection = this.state.cachedSelection;\n this.state.cachedSelection = undefined; // Clear it to force getDOMSelection() retrieve the latest selection range\n\n const newRangeEx = editor.getDOMSelection() || undefined;\n const model = this.state.cachedModel;\n const isSelectionChanged =\n forceUpdate ||\n !cachedSelection ||\n !newRangeEx ||\n !areSameSelections(newRangeEx, cachedSelection);\n\n if (isSelectionChanged) {\n if (\n !model ||\n !newRangeEx ||\n !this.state.domIndexer?.reconcileSelection(model, newRangeEx, cachedSelection)\n ) {\n this.invalidateCache();\n } else {\n updateCache(this.state, model, newRangeEx);\n }\n } else {\n this.state.cachedSelection = cachedSelection;\n }\n }\n}\n\n/**\n * @internal\n * Create a new instance of CachePlugin class.\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\nexport function createCachePlugin(\n option: EditorOptions,\n contentDiv: HTMLDivElement\n): PluginWithState<CachePluginState> {\n return new CachePlugin(option, contentDiv);\n}\n"]}
1
+ {"version":3,"file":"CachePlugin.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { areSameSelections } from './areSameSelections';\nimport { createParagraphMap } from './ParagraphMapImpl';\nimport { createTextMutationObserver } from './textMutationObserver';\nimport { DomIndexerImpl } from './domIndexerImpl';\nimport { updateCache } from './updateCache';\nimport type { Mutation } from './MutationType';\nimport type {\n CachePluginState,\n IEditor,\n PluginEvent,\n PluginWithState,\n EditorOptions,\n} from 'roosterjs-content-model-types';\n\n/**\n * ContentModel cache plugin manages cached Content Model, and refresh the cache when necessary\n */\nclass CachePlugin implements PluginWithState<CachePluginState> {\n private editor: IEditor | null = null;\n private state: CachePluginState;\n\n /**\n * Construct a new instance of CachePlugin class\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\n constructor(option: EditorOptions, contentDiv: HTMLDivElement) {\n this.state = {};\n\n if (!option.disableCache) {\n this.state.domIndexer = new DomIndexerImpl(\n option.experimentalFeatures &&\n option.experimentalFeatures.indexOf('PersistCache') >= 0\n );\n this.state.textMutationObserver = createTextMutationObserver(\n contentDiv,\n this.onMutation\n );\n }\n\n if (option.enableParagraphMap) {\n this.state.paragraphMap = createParagraphMap();\n }\n }\n\n /**\n * Get name of this plugin\n */\n getName() {\n return 'Cache';\n }\n\n /**\n * The first method that editor will call to a plugin when editor is initializing.\n * It will pass in the editor instance, plugin should take this chance to save the\n * editor reference so that it can call to any editor method or format API later.\n * @param editor The editor object\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.editor.getDocument().addEventListener('selectionchange', this.onNativeSelectionChange);\n\n this.state.textMutationObserver?.startObserving();\n }\n\n /**\n * The last method that editor will call to a plugin before it is disposed.\n * Plugin can take this chance to clear the reference to editor. After this method is\n * called, plugin should not call to any editor method since it will result in error.\n */\n dispose() {\n this.state.textMutationObserver?.stopObserving();\n\n if (this.editor) {\n this.editor\n .getDocument()\n .removeEventListener('selectionchange', this.onNativeSelectionChange);\n this.editor = null;\n }\n }\n\n /**\n * Get plugin state object\n */\n getState(): CachePluginState {\n return this.state;\n }\n\n /**\n * Core method for a plugin. Once an event happens in editor, editor will call this\n * method of each plugin to handle the event as long as the event is not handled\n * exclusively by another plugin.\n * @param event The event to handle:\n */\n onPluginEvent(event: PluginEvent) {\n if (!this.editor) {\n return;\n }\n\n switch (event.eventType) {\n case 'logicalRootChanged':\n this.invalidateCache();\n\n if (this.state.textMutationObserver) {\n this.state.textMutationObserver.stopObserving();\n this.state.textMutationObserver = createTextMutationObserver(\n event.logicalRoot,\n this.onMutation\n );\n this.state.textMutationObserver.startObserving();\n }\n break;\n\n case 'keyDown':\n case 'input':\n if (!this.state.textMutationObserver) {\n // When updating cache is not enabled, need to clear the cache to make sure other plugins can get an up-to-date content model\n this.invalidateCache();\n }\n break;\n\n case 'selectionChanged':\n this.updateCachedModel(this.editor);\n break;\n\n case 'contentChanged':\n const { contentModel, selection } = event;\n\n if (contentModel && this.state.domIndexer) {\n updateCache(this.state, contentModel, selection);\n } else {\n this.invalidateCache();\n }\n\n break;\n }\n }\n\n private onMutation = (mutation: Mutation) => {\n if (this.editor) {\n switch (mutation.type) {\n case 'childList':\n if (\n !this.state.domIndexer?.reconcileChildList(\n mutation.addedNodes,\n mutation.removedNodes\n )\n ) {\n this.invalidateCache();\n }\n break;\n\n case 'text':\n this.updateCachedModel(this.editor, true /*forceUpdate*/);\n break;\n\n case 'elementId':\n const element = mutation.element;\n\n if (!this.state.domIndexer?.reconcileElementId(element)) {\n this.invalidateCache();\n }\n\n break;\n\n case 'unknown':\n this.invalidateCache();\n break;\n }\n }\n };\n\n private onNativeSelectionChange = () => {\n if (this.editor?.hasFocus()) {\n this.updateCachedModel(this.editor);\n }\n };\n\n private invalidateCache() {\n if (!this.editor?.isInShadowEdit()) {\n this.state.cachedModel = undefined;\n this.state.cachedSelection = undefined;\n\n // Clear paragraph indexer to prevent stale references to old paragraphs\n // It will be rebuild next time when we create a new Content Model\n this.state.paragraphMap?.clear();\n }\n }\n\n private updateCachedModel(editor: IEditor, forceUpdate?: boolean) {\n if (editor.isInShadowEdit()) {\n return;\n }\n\n const cachedSelection = this.state.cachedSelection;\n this.state.cachedSelection = undefined; // Clear it to force getDOMSelection() retrieve the latest selection range\n\n const newRangeEx = editor.getDOMSelection() || undefined;\n const model = this.state.cachedModel;\n const isSelectionChanged =\n forceUpdate ||\n !cachedSelection ||\n !newRangeEx ||\n !areSameSelections(newRangeEx, cachedSelection);\n\n if (isSelectionChanged) {\n if (\n !model ||\n !newRangeEx ||\n !this.state.domIndexer?.reconcileSelection(model, newRangeEx, cachedSelection)\n ) {\n this.invalidateCache();\n } else {\n updateCache(this.state, model, newRangeEx);\n }\n } else {\n this.state.cachedSelection = cachedSelection;\n }\n }\n}\n\n/**\n * @internal\n * Create a new instance of CachePlugin class.\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\nexport function createCachePlugin(\n option: EditorOptions,\n contentDiv: HTMLDivElement\n): PluginWithState<CachePluginState> {\n return new CachePlugin(option, contentDiv);\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { ParagraphIndexer, ParagraphMap, ReadonlyContentModelParagraph } from 'roosterjs-content-model-types';
2
+ /**
3
+ * @internal, used by test code only
4
+ */
5
+ export interface ParagraphMapReset {
6
+ _reset(): void;
7
+ _getMap(): {
8
+ [key: string]: ReadonlyContentModelParagraph;
9
+ };
10
+ }
11
+ /**
12
+ * @internal
13
+ */
14
+ export declare function createParagraphMap(): ParagraphMap & ParagraphIndexer;
@@ -0,0 +1,69 @@
1
+ define(["require", "exports", "roosterjs-content-model-dom"], function (require, exports, roosterjs_content_model_dom_1) {
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.createParagraphMap = void 0;
5
+ var idPrefix = 'paragraph';
6
+ var ParagraphMapImpl = /** @class */ (function () {
7
+ function ParagraphMapImpl() {
8
+ this.nextId = 0;
9
+ this.paragraphMap = {};
10
+ ParagraphMapImpl.prefixNum++;
11
+ }
12
+ ParagraphMapImpl.prototype.assignMarkerToModel = function (element, paragraph) {
13
+ var marker = (0, roosterjs_content_model_dom_1.getParagraphMarker)(element);
14
+ var paragraphWithMarker = paragraph;
15
+ if (marker) {
16
+ paragraphWithMarker._marker = marker;
17
+ this.paragraphMap[marker] = paragraph;
18
+ }
19
+ else {
20
+ paragraphWithMarker._marker = this.generateId();
21
+ this.applyMarkerToDom(element, paragraph);
22
+ }
23
+ };
24
+ ParagraphMapImpl.prototype.applyMarkerToDom = function (element, paragraph) {
25
+ var paragraphWithMarker = paragraph;
26
+ if (!paragraphWithMarker._marker) {
27
+ paragraphWithMarker._marker = this.generateId();
28
+ }
29
+ var marker = paragraphWithMarker._marker;
30
+ if (marker) {
31
+ (0, roosterjs_content_model_dom_1.setParagraphMarker)(element, marker);
32
+ this.paragraphMap[marker] = paragraph;
33
+ }
34
+ };
35
+ /**
36
+ * Get paragraph using a previously marked paragraph
37
+ * @param markedParagraph The previously marked paragraph to get
38
+ */
39
+ ParagraphMapImpl.prototype.getParagraphFromMarker = function (markerParagraph) {
40
+ var marker = markerParagraph._marker;
41
+ return marker ? this.paragraphMap[marker] || null : null;
42
+ };
43
+ ParagraphMapImpl.prototype.clear = function () {
44
+ this.paragraphMap = {};
45
+ };
46
+ //#region For test code only
47
+ ParagraphMapImpl.prototype._reset = function () {
48
+ ParagraphMapImpl.prefixNum = 0;
49
+ this.nextId = 0;
50
+ };
51
+ ParagraphMapImpl.prototype._getMap = function () {
52
+ return this.paragraphMap;
53
+ };
54
+ //#endregion
55
+ ParagraphMapImpl.prototype.generateId = function () {
56
+ return idPrefix + "_" + ParagraphMapImpl.prefixNum + "_" + this.nextId++;
57
+ };
58
+ ParagraphMapImpl.prefixNum = 0;
59
+ return ParagraphMapImpl;
60
+ }());
61
+ /**
62
+ * @internal
63
+ */
64
+ function createParagraphMap() {
65
+ return new ParagraphMapImpl();
66
+ }
67
+ exports.createParagraphMap = createParagraphMap;
68
+ });
69
+ //# sourceMappingURL=ParagraphMapImpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParagraphMapImpl.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/ParagraphMapImpl.ts"],"names":[],"mappings":";;;;IAqBA,IAAM,QAAQ,GAAG,WAAW,CAAC;IAE7B;QAKI;YAHQ,WAAM,GAAG,CAAC,CAAC;YACX,iBAAY,GAAqD,EAAE,CAAC;YAGxE,gBAAgB,CAAC,SAAS,EAAE,CAAC;QACjC,CAAC;QAED,8CAAmB,GAAnB,UAAoB,OAAoB,EAAE,SAAgC;YACtE,IAAM,MAAM,GAAG,IAAA,gDAAkB,EAAC,OAAO,CAAC,CAAC;YAC3C,IAAM,mBAAmB,GAAG,SAAgC,CAAC;YAE7D,IAAI,MAAM,EAAE;gBACR,mBAAmB,CAAC,OAAO,GAAG,MAAM,CAAC;gBAErC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;aACzC;iBAAM;gBACH,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;gBAEhD,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;aAC7C;QACL,CAAC;QAED,2CAAgB,GAAhB,UAAiB,OAAoB,EAAE,SAAgC;YACnE,IAAM,mBAAmB,GAAG,SAAgC,CAAC;YAE7D,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE;gBAC9B,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;aACnD;YAED,IAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC;YAE3C,IAAI,MAAM,EAAE;gBACR,IAAA,gDAAkB,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAEpC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;aACzC;QACL,CAAC;QAED;;;WAGG;QACH,iDAAsB,GAAtB,UACI,eAA8C;YAE9C,IAAM,MAAM,GAAI,eAAuC,CAAC,OAAO,CAAC;YAEhE,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7D,CAAC;QAED,gCAAK,GAAL;YACI,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QAC3B,CAAC;QAED,4BAA4B;QAC5B,iCAAM,GAAN;YACI,gBAAgB,CAAC,SAAS,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QACpB,CAAC;QAED,kCAAO,GAAP;YACI,OAAO,IAAI,CAAC,YAAY,CAAC;QAC7B,CAAC;QACD,YAAY;QAEJ,qCAAU,GAAlB;YACI,OAAU,QAAQ,SAAI,gBAAgB,CAAC,SAAS,SAAI,IAAI,CAAC,MAAM,EAAI,CAAC;QACxE,CAAC;QApEc,0BAAS,GAAG,CAAC,CAAC;QAqEjC,uBAAC;KAAA,AAtED,IAsEC;IAED;;OAEG;IACH,SAAgB,kBAAkB;QAC9B,OAAO,IAAI,gBAAgB,EAAE,CAAC;IAClC,CAAC;IAFD,gDAEC","sourcesContent":["import { getParagraphMarker, setParagraphMarker } from 'roosterjs-content-model-dom';\nimport type {\n ContentModelParagraph,\n ContentModelParagraphCommon,\n ParagraphIndexer,\n ParagraphMap,\n ReadonlyContentModelParagraph,\n} from 'roosterjs-content-model-types';\n\ninterface ParagraphWithMarker extends ContentModelParagraphCommon {\n _marker?: string;\n}\n\n/**\n * @internal, used by test code only\n */\nexport interface ParagraphMapReset {\n _reset(): void;\n _getMap(): { [key: string]: ReadonlyContentModelParagraph };\n}\n\nconst idPrefix = 'paragraph';\n\nclass ParagraphMapImpl implements ParagraphMap, ParagraphIndexer, ParagraphMapReset {\n private static prefixNum = 0;\n private nextId = 0;\n private paragraphMap: { [key: string]: ReadonlyContentModelParagraph } = {};\n\n constructor() {\n ParagraphMapImpl.prefixNum++;\n }\n\n assignMarkerToModel(element: HTMLElement, paragraph: ContentModelParagraph): void {\n const marker = getParagraphMarker(element);\n const paragraphWithMarker = paragraph as ParagraphWithMarker;\n\n if (marker) {\n paragraphWithMarker._marker = marker;\n\n this.paragraphMap[marker] = paragraph;\n } else {\n paragraphWithMarker._marker = this.generateId();\n\n this.applyMarkerToDom(element, paragraph);\n }\n }\n\n applyMarkerToDom(element: HTMLElement, paragraph: ContentModelParagraph): void {\n const paragraphWithMarker = paragraph as ParagraphWithMarker;\n\n if (!paragraphWithMarker._marker) {\n paragraphWithMarker._marker = this.generateId();\n }\n\n const marker = paragraphWithMarker._marker;\n\n if (marker) {\n setParagraphMarker(element, marker);\n\n this.paragraphMap[marker] = paragraph;\n }\n }\n\n /**\n * Get paragraph using a previously marked paragraph\n * @param markedParagraph The previously marked paragraph to get\n */\n getParagraphFromMarker(\n markerParagraph: ReadonlyContentModelParagraph\n ): ReadonlyContentModelParagraph | null {\n const marker = (markerParagraph as ParagraphWithMarker)._marker;\n\n return marker ? this.paragraphMap[marker] || null : null;\n }\n\n clear() {\n this.paragraphMap = {};\n }\n\n //#region For test code only\n _reset() {\n ParagraphMapImpl.prefixNum = 0;\n this.nextId = 0;\n }\n\n _getMap() {\n return this.paragraphMap;\n }\n //#endregion\n\n private generateId() {\n return `${idPrefix}_${ParagraphMapImpl.prefixNum}_${this.nextId++}`;\n }\n}\n\n/**\n * @internal\n */\nexport function createParagraphMap(): ParagraphMap & ParagraphIndexer {\n return new ParagraphMapImpl();\n}\n"]}
@@ -8,7 +8,7 @@ export var createEditorContext = function (core, saveIndex) {
8
8
  var _a, _b;
9
9
  var lifecycle = core.lifecycle, format = core.format, darkColorHandler = core.darkColorHandler, logicalRoot = core.logicalRoot, cache = core.cache, domHelper = core.domHelper;
10
10
  saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;
11
- var context = __assign({ isDarkMode: lifecycle.isDarkMode, defaultFormat: format.defaultFormat, pendingFormat: (_a = format.pendingFormat) !== null && _a !== void 0 ? _a : undefined, darkColorHandler: darkColorHandler, addDelimiterForEntity: true, allowCacheElement: true, domIndexer: saveIndex ? cache.domIndexer : undefined, zoomScale: domHelper.calculateZoomScale(), experimentalFeatures: (_b = core.experimentalFeatures) !== null && _b !== void 0 ? _b : [] }, getRootComputedStyleForContext(logicalRoot.ownerDocument));
11
+ var context = __assign({ isDarkMode: lifecycle.isDarkMode, defaultFormat: format.defaultFormat, pendingFormat: (_a = format.pendingFormat) !== null && _a !== void 0 ? _a : undefined, darkColorHandler: darkColorHandler, addDelimiterForEntity: true, allowCacheElement: true, domIndexer: saveIndex ? cache.domIndexer : undefined, zoomScale: domHelper.calculateZoomScale(), experimentalFeatures: (_b = core.experimentalFeatures) !== null && _b !== void 0 ? _b : [], paragraphMap: core.cache.paragraphMap }, getRootComputedStyleForContext(logicalRoot.ownerDocument));
12
12
  if (core.domHelper.isRightToLeft()) {
13
13
  context.isRootRtl = true;
14
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"createEditorContext.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/createEditorContext/createEditorContext.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAGlF;;;GAGG;AACH,MAAM,CAAC,IAAM,mBAAmB,GAAwB,UAAC,IAAI,EAAE,SAAS;;IAC5D,IAAA,SAAS,GAA8D,IAAI,UAAlE,EAAE,MAAM,GAAsD,IAAI,OAA1D,EAAE,gBAAgB,GAAoC,IAAI,iBAAxC,EAAE,WAAW,GAAuB,IAAI,YAA3B,EAAE,KAAK,GAAgB,IAAI,MAApB,EAAE,SAAS,GAAK,IAAI,UAAT,CAAU;IAEpF,SAAS,GAAG,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;IAE5D,IAAM,OAAO,cACT,UAAU,EAAE,SAAS,CAAC,UAAU,EAChC,aAAa,EAAE,MAAM,CAAC,aAAa,EACnC,aAAa,EAAE,MAAA,MAAM,CAAC,aAAa,mCAAI,SAAS,EAChD,gBAAgB,EAAE,gBAAgB,EAClC,qBAAqB,EAAE,IAAI,EAC3B,iBAAiB,EAAE,IAAI,EACvB,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EACpD,SAAS,EAAE,SAAS,CAAC,kBAAkB,EAAE,EACzC,oBAAoB,EAAE,MAAA,IAAI,CAAC,oBAAoB,mCAAI,EAAE,IAClD,8BAA8B,CAAC,WAAW,CAAC,aAAa,CAAC,CAC/D,CAAC;IAEF,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE;QAChC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;KAC5B;IAED,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC","sourcesContent":["import { getRootComputedStyleForContext } from './getRootComputedStyleForContext';\nimport type { EditorContext, CreateEditorContext } from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * Create a EditorContext object used by ContentModel API\n */\nexport const createEditorContext: CreateEditorContext = (core, saveIndex) => {\n const { lifecycle, format, darkColorHandler, logicalRoot, cache, domHelper } = core;\n\n saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;\n\n const context: EditorContext = {\n isDarkMode: lifecycle.isDarkMode,\n defaultFormat: format.defaultFormat,\n pendingFormat: format.pendingFormat ?? undefined,\n darkColorHandler: darkColorHandler,\n addDelimiterForEntity: true,\n allowCacheElement: true,\n domIndexer: saveIndex ? cache.domIndexer : undefined,\n zoomScale: domHelper.calculateZoomScale(),\n experimentalFeatures: core.experimentalFeatures ?? [],\n ...getRootComputedStyleForContext(logicalRoot.ownerDocument),\n };\n\n if (core.domHelper.isRightToLeft()) {\n context.isRootRtl = true;\n }\n\n return context;\n};\n"]}
1
+ {"version":3,"file":"createEditorContext.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/createEditorContext/createEditorContext.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAGlF;;;GAGG;AACH,MAAM,CAAC,IAAM,mBAAmB,GAAwB,UAAC,IAAI,EAAE,SAAS;;IAC5D,IAAA,SAAS,GAA8D,IAAI,UAAlE,EAAE,MAAM,GAAsD,IAAI,OAA1D,EAAE,gBAAgB,GAAoC,IAAI,iBAAxC,EAAE,WAAW,GAAuB,IAAI,YAA3B,EAAE,KAAK,GAAgB,IAAI,MAApB,EAAE,SAAS,GAAK,IAAI,UAAT,CAAU;IAEpF,SAAS,GAAG,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC;IAE5D,IAAM,OAAO,cACT,UAAU,EAAE,SAAS,CAAC,UAAU,EAChC,aAAa,EAAE,MAAM,CAAC,aAAa,EACnC,aAAa,EAAE,MAAA,MAAM,CAAC,aAAa,mCAAI,SAAS,EAChD,gBAAgB,EAAE,gBAAgB,EAClC,qBAAqB,EAAE,IAAI,EAC3B,iBAAiB,EAAE,IAAI,EACvB,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EACpD,SAAS,EAAE,SAAS,CAAC,kBAAkB,EAAE,EACzC,oBAAoB,EAAE,MAAA,IAAI,CAAC,oBAAoB,mCAAI,EAAE,EACrD,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,IAClC,8BAA8B,CAAC,WAAW,CAAC,aAAa,CAAC,CAC/D,CAAC;IAEF,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE;QAChC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;KAC5B;IAED,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC","sourcesContent":["import { getRootComputedStyleForContext } from './getRootComputedStyleForContext';\nimport type { EditorContext, CreateEditorContext } from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * Create a EditorContext object used by ContentModel API\n */\nexport const createEditorContext: CreateEditorContext = (core, saveIndex) => {\n const { lifecycle, format, darkColorHandler, logicalRoot, cache, domHelper } = core;\n\n saveIndex = saveIndex && !core.lifecycle.shadowEditFragment;\n\n const context: EditorContext = {\n isDarkMode: lifecycle.isDarkMode,\n defaultFormat: format.defaultFormat,\n pendingFormat: format.pendingFormat ?? undefined,\n darkColorHandler: darkColorHandler,\n addDelimiterForEntity: true,\n allowCacheElement: true,\n domIndexer: saveIndex ? cache.domIndexer : undefined,\n zoomScale: domHelper.calculateZoomScale(),\n experimentalFeatures: core.experimentalFeatures ?? [],\n paragraphMap: core.cache.paragraphMap,\n ...getRootComputedStyleForContext(logicalRoot.ownerDocument),\n };\n\n if (core.domHelper.isRightToLeft()) {\n context.isRootRtl = true;\n }\n\n return context;\n};\n"]}
@@ -20,6 +20,7 @@ export var formatContentModel = function (core, formatter, options, domToModelOp
20
20
  deletedEntities: [],
21
21
  rawEvent: rawEvent,
22
22
  newImages: [],
23
+ paragraphIndexer: core.cache.paragraphMap,
23
24
  };
24
25
  var hasFocus = core.domHelper.hasFocus();
25
26
  var changed = formatter(model, context);
@@ -1 +1 @@
1
- {"version":3,"file":"formatContentModel.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeSource } from 'roosterjs-content-model-dom';\nimport { scrollCaretIntoView } from './scrollCaretIntoView';\nimport type {\n ChangedEntity,\n ContentChangedEvent,\n DOMSelection,\n FormatContentModel,\n FormatContentModelContext,\n EditorCore,\n} from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * The general API to do format change with Content Model\n * It will grab a Content Model for current editor content, and invoke a callback function\n * to do format change. Then according to the return value, write back the modified content model into editor.\n * If there is cached model, it will be used and updated.\n * @param core The EditorCore object\n * @param formatter Formatter function, see ContentModelFormatter\n * @param options More options, see FormatContentModelOptions\n */\nexport const formatContentModel: FormatContentModel = (\n core,\n formatter,\n options,\n domToModelOptions\n) => {\n const { onNodeCreated, rawEvent, selectionOverride, scrollCaretIntoView: scroll } =\n options || {};\n const model = core.api.createContentModel(core, domToModelOptions, selectionOverride);\n const context: FormatContentModelContext = {\n newEntities: [],\n deletedEntities: [],\n rawEvent,\n newImages: [],\n };\n\n const hasFocus = core.domHelper.hasFocus();\n\n const changed = formatter(model, context);\n const { skipUndoSnapshot, clearModelCache, entityStates, canUndoByBackspace } = context;\n\n if (changed) {\n const isNested = core.undo.isNested;\n const shouldAddSnapshot =\n (!skipUndoSnapshot || skipUndoSnapshot == 'DoNotSkip') && !isNested;\n const shouldMarkNewContent =\n (skipUndoSnapshot === true || skipUndoSnapshot == 'MarkNewContent') && !isNested;\n let selection: DOMSelection | undefined;\n\n if (shouldAddSnapshot) {\n core.undo.isNested = true;\n\n core.api.addUndoSnapshot(core, !!canUndoByBackspace, entityStates);\n }\n\n try {\n handleImages(core, context);\n\n selection =\n core.api.setContentModel(\n core,\n model,\n hasFocus ? undefined : { ignoreSelection: true }, // If editor did not have focus before format, do not set focus after format\n onNodeCreated\n ) ?? undefined;\n\n handlePendingFormat(core, context, selection);\n\n if (scroll && (selection?.type == 'range' || selection?.type == 'image')) {\n scrollCaretIntoView(core, selection);\n }\n\n const eventData: ContentChangedEvent = {\n eventType: 'contentChanged',\n contentModel: clearModelCache ? undefined : model,\n selection: clearModelCache ? undefined : selection,\n source: options?.changeSource || ChangeSource.Format,\n data: options?.getChangeData?.(),\n formatApiName: options?.apiName,\n changedEntities: getChangedEntities(context, rawEvent),\n };\n\n core.api.triggerEvent(core, eventData, true /*broadcast*/);\n\n if (canUndoByBackspace && selection?.type == 'range') {\n core.undo.autoCompleteInsertPoint = {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n };\n }\n\n if (shouldAddSnapshot) {\n core.api.addUndoSnapshot(core, false /*canUndoByBackspace*/, entityStates);\n }\n\n if (shouldMarkNewContent) {\n core.undo.snapshotsManager.hasNewContent = true;\n }\n } finally {\n if (!isNested) {\n core.undo.isNested = false;\n }\n }\n } else {\n if (clearModelCache) {\n core.cache.cachedModel = undefined;\n core.cache.cachedSelection = undefined;\n }\n\n handlePendingFormat(core, context, core.api.getDOMSelection(core));\n }\n\n if (context.announceData) {\n core.api.announce(core, context.announceData);\n }\n};\n\nfunction handleImages(core: EditorCore, context: FormatContentModelContext) {\n if (context.newImages.length > 0) {\n const width = core.domHelper.getClientWidth();\n const minMaxImageSize = 10;\n const maxWidth = Math.max(width, minMaxImageSize);\n context.newImages.forEach(image => {\n image.format.maxWidth = `${maxWidth}px`;\n });\n }\n}\n\nfunction handlePendingFormat(\n core: EditorCore,\n context: FormatContentModelContext,\n selection?: DOMSelection | null\n) {\n const pendingFormat =\n context.newPendingFormat == 'preserve'\n ? core.format.pendingFormat?.format\n : context.newPendingFormat;\n const pendingParagraphFormat =\n context.newPendingParagraphFormat == 'preserve'\n ? core.format.pendingFormat?.paragraphFormat\n : context.newPendingParagraphFormat;\n\n if (\n (pendingFormat || pendingParagraphFormat) &&\n selection?.type == 'range' &&\n selection.range.collapsed\n ) {\n core.format.pendingFormat = {\n format: pendingFormat ? { ...pendingFormat } : undefined,\n paragraphFormat: pendingParagraphFormat ? { ...pendingParagraphFormat } : undefined,\n insertPoint: {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n },\n };\n }\n}\n\nfunction getChangedEntities(\n context: FormatContentModelContext,\n rawEvent?: Event\n): ChangedEntity[] | undefined {\n return context.autoDetectChangedEntities\n ? undefined\n : context.newEntities\n .map(\n (entity): ChangedEntity => ({\n entity,\n operation: 'newEntity',\n rawEvent,\n })\n )\n .concat(\n context.deletedEntities.map(entry => ({\n entity: entry.entity,\n operation: entry.operation,\n rawEvent,\n }))\n );\n}\n"]}
1
+ {"version":3,"file":"formatContentModel.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeSource } from 'roosterjs-content-model-dom';\nimport { scrollCaretIntoView } from './scrollCaretIntoView';\nimport type {\n ChangedEntity,\n ContentChangedEvent,\n DOMSelection,\n FormatContentModel,\n FormatContentModelContext,\n EditorCore,\n} from 'roosterjs-content-model-types';\n\n/**\n * @internal\n * The general API to do format change with Content Model\n * It will grab a Content Model for current editor content, and invoke a callback function\n * to do format change. Then according to the return value, write back the modified content model into editor.\n * If there is cached model, it will be used and updated.\n * @param core The EditorCore object\n * @param formatter Formatter function, see ContentModelFormatter\n * @param options More options, see FormatContentModelOptions\n */\nexport const formatContentModel: FormatContentModel = (\n core,\n formatter,\n options,\n domToModelOptions\n) => {\n const { onNodeCreated, rawEvent, selectionOverride, scrollCaretIntoView: scroll } =\n options || {};\n const model = core.api.createContentModel(core, domToModelOptions, selectionOverride);\n const context: FormatContentModelContext = {\n newEntities: [],\n deletedEntities: [],\n rawEvent,\n newImages: [],\n paragraphIndexer: core.cache.paragraphMap,\n };\n\n const hasFocus = core.domHelper.hasFocus();\n\n const changed = formatter(model, context);\n const { skipUndoSnapshot, clearModelCache, entityStates, canUndoByBackspace } = context;\n\n if (changed) {\n const isNested = core.undo.isNested;\n const shouldAddSnapshot =\n (!skipUndoSnapshot || skipUndoSnapshot == 'DoNotSkip') && !isNested;\n const shouldMarkNewContent =\n (skipUndoSnapshot === true || skipUndoSnapshot == 'MarkNewContent') && !isNested;\n let selection: DOMSelection | undefined;\n\n if (shouldAddSnapshot) {\n core.undo.isNested = true;\n\n core.api.addUndoSnapshot(core, !!canUndoByBackspace, entityStates);\n }\n\n try {\n handleImages(core, context);\n\n selection =\n core.api.setContentModel(\n core,\n model,\n hasFocus ? undefined : { ignoreSelection: true }, // If editor did not have focus before format, do not set focus after format\n onNodeCreated\n ) ?? undefined;\n\n handlePendingFormat(core, context, selection);\n\n if (scroll && (selection?.type == 'range' || selection?.type == 'image')) {\n scrollCaretIntoView(core, selection);\n }\n\n const eventData: ContentChangedEvent = {\n eventType: 'contentChanged',\n contentModel: clearModelCache ? undefined : model,\n selection: clearModelCache ? undefined : selection,\n source: options?.changeSource || ChangeSource.Format,\n data: options?.getChangeData?.(),\n formatApiName: options?.apiName,\n changedEntities: getChangedEntities(context, rawEvent),\n };\n\n core.api.triggerEvent(core, eventData, true /*broadcast*/);\n\n if (canUndoByBackspace && selection?.type == 'range') {\n core.undo.autoCompleteInsertPoint = {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n };\n }\n\n if (shouldAddSnapshot) {\n core.api.addUndoSnapshot(core, false /*canUndoByBackspace*/, entityStates);\n }\n\n if (shouldMarkNewContent) {\n core.undo.snapshotsManager.hasNewContent = true;\n }\n } finally {\n if (!isNested) {\n core.undo.isNested = false;\n }\n }\n } else {\n if (clearModelCache) {\n core.cache.cachedModel = undefined;\n core.cache.cachedSelection = undefined;\n }\n\n handlePendingFormat(core, context, core.api.getDOMSelection(core));\n }\n\n if (context.announceData) {\n core.api.announce(core, context.announceData);\n }\n};\n\nfunction handleImages(core: EditorCore, context: FormatContentModelContext) {\n if (context.newImages.length > 0) {\n const width = core.domHelper.getClientWidth();\n const minMaxImageSize = 10;\n const maxWidth = Math.max(width, minMaxImageSize);\n context.newImages.forEach(image => {\n image.format.maxWidth = `${maxWidth}px`;\n });\n }\n}\n\nfunction handlePendingFormat(\n core: EditorCore,\n context: FormatContentModelContext,\n selection?: DOMSelection | null\n) {\n const pendingFormat =\n context.newPendingFormat == 'preserve'\n ? core.format.pendingFormat?.format\n : context.newPendingFormat;\n const pendingParagraphFormat =\n context.newPendingParagraphFormat == 'preserve'\n ? core.format.pendingFormat?.paragraphFormat\n : context.newPendingParagraphFormat;\n\n if (\n (pendingFormat || pendingParagraphFormat) &&\n selection?.type == 'range' &&\n selection.range.collapsed\n ) {\n core.format.pendingFormat = {\n format: pendingFormat ? { ...pendingFormat } : undefined,\n paragraphFormat: pendingParagraphFormat ? { ...pendingParagraphFormat } : undefined,\n insertPoint: {\n node: selection.range.startContainer,\n offset: selection.range.startOffset,\n },\n };\n }\n}\n\nfunction getChangedEntities(\n context: FormatContentModelContext,\n rawEvent?: Event\n): ChangedEntity[] | undefined {\n return context.autoDetectChangedEntities\n ? undefined\n : context.newEntities\n .map(\n (entity): ChangedEntity => ({\n entity,\n operation: 'newEntity',\n rawEvent,\n })\n )\n .concat(\n context.deletedEntities.map(entry => ({\n entity: entry.entity,\n operation: entry.operation,\n rawEvent,\n }))\n );\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { areSameSelections } from './areSameSelections';
2
+ import { createParagraphMap } from './ParagraphMapImpl';
2
3
  import { createTextMutationObserver } from './textMutationObserver';
3
4
  import { DomIndexerImpl } from './domIndexerImpl';
4
5
  import { updateCache } from './updateCache';
@@ -44,13 +45,15 @@ var CachePlugin = /** @class */ (function () {
44
45
  _this.updateCachedModel(_this.editor);
45
46
  }
46
47
  };
47
- this.state = option.disableCache
48
- ? {}
49
- : {
50
- domIndexer: new DomIndexerImpl(option.experimentalFeatures &&
51
- option.experimentalFeatures.indexOf('PersistCache') >= 0),
52
- textMutationObserver: createTextMutationObserver(contentDiv, this.onMutation),
53
- };
48
+ this.state = {};
49
+ if (!option.disableCache) {
50
+ this.state.domIndexer = new DomIndexerImpl(option.experimentalFeatures &&
51
+ option.experimentalFeatures.indexOf('PersistCache') >= 0);
52
+ this.state.textMutationObserver = createTextMutationObserver(contentDiv, this.onMutation);
53
+ }
54
+ if (option.enableParagraphMap) {
55
+ this.state.paragraphMap = createParagraphMap();
56
+ }
54
57
  }
55
58
  /**
56
59
  * Get name of this plugin
@@ -132,10 +135,13 @@ var CachePlugin = /** @class */ (function () {
132
135
  }
133
136
  };
134
137
  CachePlugin.prototype.invalidateCache = function () {
135
- var _a;
138
+ var _a, _b;
136
139
  if (!((_a = this.editor) === null || _a === void 0 ? void 0 : _a.isInShadowEdit())) {
137
140
  this.state.cachedModel = undefined;
138
141
  this.state.cachedSelection = undefined;
142
+ // Clear paragraph indexer to prevent stale references to old paragraphs
143
+ // It will be rebuild next time when we create a new Content Model
144
+ (_b = this.state.paragraphMap) === null || _b === void 0 ? void 0 : _b.clear();
139
145
  }
140
146
  };
141
147
  CachePlugin.prototype.updateCachedModel = function (editor, forceUpdate) {
@@ -1 +1 @@
1
- {"version":3,"file":"CachePlugin.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { areSameSelections } from './areSameSelections';\nimport { createTextMutationObserver } from './textMutationObserver';\nimport { DomIndexerImpl } from './domIndexerImpl';\nimport { updateCache } from './updateCache';\nimport type { Mutation } from './MutationType';\nimport type {\n CachePluginState,\n IEditor,\n PluginEvent,\n PluginWithState,\n EditorOptions,\n} from 'roosterjs-content-model-types';\n\n/**\n * ContentModel cache plugin manages cached Content Model, and refresh the cache when necessary\n */\nclass CachePlugin implements PluginWithState<CachePluginState> {\n private editor: IEditor | null = null;\n private state: CachePluginState;\n\n /**\n * Construct a new instance of CachePlugin class\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\n constructor(option: EditorOptions, contentDiv: HTMLDivElement) {\n this.state = option.disableCache\n ? {}\n : {\n domIndexer: new DomIndexerImpl(\n option.experimentalFeatures &&\n option.experimentalFeatures.indexOf('PersistCache') >= 0\n ),\n textMutationObserver: createTextMutationObserver(contentDiv, this.onMutation),\n };\n }\n\n /**\n * Get name of this plugin\n */\n getName() {\n return 'Cache';\n }\n\n /**\n * The first method that editor will call to a plugin when editor is initializing.\n * It will pass in the editor instance, plugin should take this chance to save the\n * editor reference so that it can call to any editor method or format API later.\n * @param editor The editor object\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.editor.getDocument().addEventListener('selectionchange', this.onNativeSelectionChange);\n\n this.state.textMutationObserver?.startObserving();\n }\n\n /**\n * The last method that editor will call to a plugin before it is disposed.\n * Plugin can take this chance to clear the reference to editor. After this method is\n * called, plugin should not call to any editor method since it will result in error.\n */\n dispose() {\n this.state.textMutationObserver?.stopObserving();\n\n if (this.editor) {\n this.editor\n .getDocument()\n .removeEventListener('selectionchange', this.onNativeSelectionChange);\n this.editor = null;\n }\n }\n\n /**\n * Get plugin state object\n */\n getState(): CachePluginState {\n return this.state;\n }\n\n /**\n * Core method for a plugin. Once an event happens in editor, editor will call this\n * method of each plugin to handle the event as long as the event is not handled\n * exclusively by another plugin.\n * @param event The event to handle:\n */\n onPluginEvent(event: PluginEvent) {\n if (!this.editor) {\n return;\n }\n\n switch (event.eventType) {\n case 'logicalRootChanged':\n this.invalidateCache();\n\n if (this.state.textMutationObserver) {\n this.state.textMutationObserver.stopObserving();\n this.state.textMutationObserver = createTextMutationObserver(\n event.logicalRoot,\n this.onMutation\n );\n this.state.textMutationObserver.startObserving();\n }\n break;\n\n case 'keyDown':\n case 'input':\n if (!this.state.textMutationObserver) {\n // When updating cache is not enabled, need to clear the cache to make sure other plugins can get an up-to-date content model\n this.invalidateCache();\n }\n break;\n\n case 'selectionChanged':\n this.updateCachedModel(this.editor);\n break;\n\n case 'contentChanged':\n const { contentModel, selection } = event;\n\n if (contentModel && this.state.domIndexer) {\n updateCache(this.state, contentModel, selection);\n } else {\n this.invalidateCache();\n }\n\n break;\n }\n }\n\n private onMutation = (mutation: Mutation) => {\n if (this.editor) {\n switch (mutation.type) {\n case 'childList':\n if (\n !this.state.domIndexer?.reconcileChildList(\n mutation.addedNodes,\n mutation.removedNodes\n )\n ) {\n this.invalidateCache();\n }\n break;\n\n case 'text':\n this.updateCachedModel(this.editor, true /*forceUpdate*/);\n break;\n\n case 'elementId':\n const element = mutation.element;\n\n if (!this.state.domIndexer?.reconcileElementId(element)) {\n this.invalidateCache();\n }\n\n break;\n\n case 'unknown':\n this.invalidateCache();\n break;\n }\n }\n };\n\n private onNativeSelectionChange = () => {\n if (this.editor?.hasFocus()) {\n this.updateCachedModel(this.editor);\n }\n };\n\n private invalidateCache() {\n if (!this.editor?.isInShadowEdit()) {\n this.state.cachedModel = undefined;\n this.state.cachedSelection = undefined;\n }\n }\n\n private updateCachedModel(editor: IEditor, forceUpdate?: boolean) {\n if (editor.isInShadowEdit()) {\n return;\n }\n\n const cachedSelection = this.state.cachedSelection;\n this.state.cachedSelection = undefined; // Clear it to force getDOMSelection() retrieve the latest selection range\n\n const newRangeEx = editor.getDOMSelection() || undefined;\n const model = this.state.cachedModel;\n const isSelectionChanged =\n forceUpdate ||\n !cachedSelection ||\n !newRangeEx ||\n !areSameSelections(newRangeEx, cachedSelection);\n\n if (isSelectionChanged) {\n if (\n !model ||\n !newRangeEx ||\n !this.state.domIndexer?.reconcileSelection(model, newRangeEx, cachedSelection)\n ) {\n this.invalidateCache();\n } else {\n updateCache(this.state, model, newRangeEx);\n }\n } else {\n this.state.cachedSelection = cachedSelection;\n }\n }\n}\n\n/**\n * @internal\n * Create a new instance of CachePlugin class.\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\nexport function createCachePlugin(\n option: EditorOptions,\n contentDiv: HTMLDivElement\n): PluginWithState<CachePluginState> {\n return new CachePlugin(option, contentDiv);\n}\n"]}
1
+ {"version":3,"file":"CachePlugin.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { areSameSelections } from './areSameSelections';\nimport { createParagraphMap } from './ParagraphMapImpl';\nimport { createTextMutationObserver } from './textMutationObserver';\nimport { DomIndexerImpl } from './domIndexerImpl';\nimport { updateCache } from './updateCache';\nimport type { Mutation } from './MutationType';\nimport type {\n CachePluginState,\n IEditor,\n PluginEvent,\n PluginWithState,\n EditorOptions,\n} from 'roosterjs-content-model-types';\n\n/**\n * ContentModel cache plugin manages cached Content Model, and refresh the cache when necessary\n */\nclass CachePlugin implements PluginWithState<CachePluginState> {\n private editor: IEditor | null = null;\n private state: CachePluginState;\n\n /**\n * Construct a new instance of CachePlugin class\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\n constructor(option: EditorOptions, contentDiv: HTMLDivElement) {\n this.state = {};\n\n if (!option.disableCache) {\n this.state.domIndexer = new DomIndexerImpl(\n option.experimentalFeatures &&\n option.experimentalFeatures.indexOf('PersistCache') >= 0\n );\n this.state.textMutationObserver = createTextMutationObserver(\n contentDiv,\n this.onMutation\n );\n }\n\n if (option.enableParagraphMap) {\n this.state.paragraphMap = createParagraphMap();\n }\n }\n\n /**\n * Get name of this plugin\n */\n getName() {\n return 'Cache';\n }\n\n /**\n * The first method that editor will call to a plugin when editor is initializing.\n * It will pass in the editor instance, plugin should take this chance to save the\n * editor reference so that it can call to any editor method or format API later.\n * @param editor The editor object\n */\n initialize(editor: IEditor) {\n this.editor = editor;\n this.editor.getDocument().addEventListener('selectionchange', this.onNativeSelectionChange);\n\n this.state.textMutationObserver?.startObserving();\n }\n\n /**\n * The last method that editor will call to a plugin before it is disposed.\n * Plugin can take this chance to clear the reference to editor. After this method is\n * called, plugin should not call to any editor method since it will result in error.\n */\n dispose() {\n this.state.textMutationObserver?.stopObserving();\n\n if (this.editor) {\n this.editor\n .getDocument()\n .removeEventListener('selectionchange', this.onNativeSelectionChange);\n this.editor = null;\n }\n }\n\n /**\n * Get plugin state object\n */\n getState(): CachePluginState {\n return this.state;\n }\n\n /**\n * Core method for a plugin. Once an event happens in editor, editor will call this\n * method of each plugin to handle the event as long as the event is not handled\n * exclusively by another plugin.\n * @param event The event to handle:\n */\n onPluginEvent(event: PluginEvent) {\n if (!this.editor) {\n return;\n }\n\n switch (event.eventType) {\n case 'logicalRootChanged':\n this.invalidateCache();\n\n if (this.state.textMutationObserver) {\n this.state.textMutationObserver.stopObserving();\n this.state.textMutationObserver = createTextMutationObserver(\n event.logicalRoot,\n this.onMutation\n );\n this.state.textMutationObserver.startObserving();\n }\n break;\n\n case 'keyDown':\n case 'input':\n if (!this.state.textMutationObserver) {\n // When updating cache is not enabled, need to clear the cache to make sure other plugins can get an up-to-date content model\n this.invalidateCache();\n }\n break;\n\n case 'selectionChanged':\n this.updateCachedModel(this.editor);\n break;\n\n case 'contentChanged':\n const { contentModel, selection } = event;\n\n if (contentModel && this.state.domIndexer) {\n updateCache(this.state, contentModel, selection);\n } else {\n this.invalidateCache();\n }\n\n break;\n }\n }\n\n private onMutation = (mutation: Mutation) => {\n if (this.editor) {\n switch (mutation.type) {\n case 'childList':\n if (\n !this.state.domIndexer?.reconcileChildList(\n mutation.addedNodes,\n mutation.removedNodes\n )\n ) {\n this.invalidateCache();\n }\n break;\n\n case 'text':\n this.updateCachedModel(this.editor, true /*forceUpdate*/);\n break;\n\n case 'elementId':\n const element = mutation.element;\n\n if (!this.state.domIndexer?.reconcileElementId(element)) {\n this.invalidateCache();\n }\n\n break;\n\n case 'unknown':\n this.invalidateCache();\n break;\n }\n }\n };\n\n private onNativeSelectionChange = () => {\n if (this.editor?.hasFocus()) {\n this.updateCachedModel(this.editor);\n }\n };\n\n private invalidateCache() {\n if (!this.editor?.isInShadowEdit()) {\n this.state.cachedModel = undefined;\n this.state.cachedSelection = undefined;\n\n // Clear paragraph indexer to prevent stale references to old paragraphs\n // It will be rebuild next time when we create a new Content Model\n this.state.paragraphMap?.clear();\n }\n }\n\n private updateCachedModel(editor: IEditor, forceUpdate?: boolean) {\n if (editor.isInShadowEdit()) {\n return;\n }\n\n const cachedSelection = this.state.cachedSelection;\n this.state.cachedSelection = undefined; // Clear it to force getDOMSelection() retrieve the latest selection range\n\n const newRangeEx = editor.getDOMSelection() || undefined;\n const model = this.state.cachedModel;\n const isSelectionChanged =\n forceUpdate ||\n !cachedSelection ||\n !newRangeEx ||\n !areSameSelections(newRangeEx, cachedSelection);\n\n if (isSelectionChanged) {\n if (\n !model ||\n !newRangeEx ||\n !this.state.domIndexer?.reconcileSelection(model, newRangeEx, cachedSelection)\n ) {\n this.invalidateCache();\n } else {\n updateCache(this.state, model, newRangeEx);\n }\n } else {\n this.state.cachedSelection = cachedSelection;\n }\n }\n}\n\n/**\n * @internal\n * Create a new instance of CachePlugin class.\n * @param option The editor option\n * @param contentDiv The editor content DIV\n */\nexport function createCachePlugin(\n option: EditorOptions,\n contentDiv: HTMLDivElement\n): PluginWithState<CachePluginState> {\n return new CachePlugin(option, contentDiv);\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { ParagraphIndexer, ParagraphMap, ReadonlyContentModelParagraph } from 'roosterjs-content-model-types';
2
+ /**
3
+ * @internal, used by test code only
4
+ */
5
+ export interface ParagraphMapReset {
6
+ _reset(): void;
7
+ _getMap(): {
8
+ [key: string]: ReadonlyContentModelParagraph;
9
+ };
10
+ }
11
+ /**
12
+ * @internal
13
+ */
14
+ export declare function createParagraphMap(): ParagraphMap & ParagraphIndexer;
@@ -0,0 +1,64 @@
1
+ import { getParagraphMarker, setParagraphMarker } from 'roosterjs-content-model-dom';
2
+ var idPrefix = 'paragraph';
3
+ var ParagraphMapImpl = /** @class */ (function () {
4
+ function ParagraphMapImpl() {
5
+ this.nextId = 0;
6
+ this.paragraphMap = {};
7
+ ParagraphMapImpl.prefixNum++;
8
+ }
9
+ ParagraphMapImpl.prototype.assignMarkerToModel = function (element, paragraph) {
10
+ var marker = getParagraphMarker(element);
11
+ var paragraphWithMarker = paragraph;
12
+ if (marker) {
13
+ paragraphWithMarker._marker = marker;
14
+ this.paragraphMap[marker] = paragraph;
15
+ }
16
+ else {
17
+ paragraphWithMarker._marker = this.generateId();
18
+ this.applyMarkerToDom(element, paragraph);
19
+ }
20
+ };
21
+ ParagraphMapImpl.prototype.applyMarkerToDom = function (element, paragraph) {
22
+ var paragraphWithMarker = paragraph;
23
+ if (!paragraphWithMarker._marker) {
24
+ paragraphWithMarker._marker = this.generateId();
25
+ }
26
+ var marker = paragraphWithMarker._marker;
27
+ if (marker) {
28
+ setParagraphMarker(element, marker);
29
+ this.paragraphMap[marker] = paragraph;
30
+ }
31
+ };
32
+ /**
33
+ * Get paragraph using a previously marked paragraph
34
+ * @param markedParagraph The previously marked paragraph to get
35
+ */
36
+ ParagraphMapImpl.prototype.getParagraphFromMarker = function (markerParagraph) {
37
+ var marker = markerParagraph._marker;
38
+ return marker ? this.paragraphMap[marker] || null : null;
39
+ };
40
+ ParagraphMapImpl.prototype.clear = function () {
41
+ this.paragraphMap = {};
42
+ };
43
+ //#region For test code only
44
+ ParagraphMapImpl.prototype._reset = function () {
45
+ ParagraphMapImpl.prefixNum = 0;
46
+ this.nextId = 0;
47
+ };
48
+ ParagraphMapImpl.prototype._getMap = function () {
49
+ return this.paragraphMap;
50
+ };
51
+ //#endregion
52
+ ParagraphMapImpl.prototype.generateId = function () {
53
+ return idPrefix + "_" + ParagraphMapImpl.prefixNum + "_" + this.nextId++;
54
+ };
55
+ ParagraphMapImpl.prefixNum = 0;
56
+ return ParagraphMapImpl;
57
+ }());
58
+ /**
59
+ * @internal
60
+ */
61
+ export function createParagraphMap() {
62
+ return new ParagraphMapImpl();
63
+ }
64
+ //# sourceMappingURL=ParagraphMapImpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParagraphMapImpl.js","sourceRoot":"","sources":["../../../../../packages/roosterjs-content-model-core/lib/corePlugin/cache/ParagraphMapImpl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAqBrF,IAAM,QAAQ,GAAG,WAAW,CAAC;AAE7B;IAKI;QAHQ,WAAM,GAAG,CAAC,CAAC;QACX,iBAAY,GAAqD,EAAE,CAAC;QAGxE,gBAAgB,CAAC,SAAS,EAAE,CAAC;IACjC,CAAC;IAED,8CAAmB,GAAnB,UAAoB,OAAoB,EAAE,SAAgC;QACtE,IAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAM,mBAAmB,GAAG,SAAgC,CAAC;QAE7D,IAAI,MAAM,EAAE;YACR,mBAAmB,CAAC,OAAO,GAAG,MAAM,CAAC;YAErC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;SACzC;aAAM;YACH,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAEhD,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;SAC7C;IACL,CAAC;IAED,2CAAgB,GAAhB,UAAiB,OAAoB,EAAE,SAAgC;QACnE,IAAM,mBAAmB,GAAG,SAAgC,CAAC;QAE7D,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE;YAC9B,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;SACnD;QAED,IAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC;QAE3C,IAAI,MAAM,EAAE;YACR,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAEpC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;SACzC;IACL,CAAC;IAED;;;OAGG;IACH,iDAAsB,GAAtB,UACI,eAA8C;QAE9C,IAAM,MAAM,GAAI,eAAuC,CAAC,OAAO,CAAC;QAEhE,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;IAED,gCAAK,GAAL;QACI,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IAC3B,CAAC;IAED,4BAA4B;IAC5B,iCAAM,GAAN;QACI,gBAAgB,CAAC,SAAS,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,CAAC;IAED,kCAAO,GAAP;QACI,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B,CAAC;IACD,YAAY;IAEJ,qCAAU,GAAlB;QACI,OAAU,QAAQ,SAAI,gBAAgB,CAAC,SAAS,SAAI,IAAI,CAAC,MAAM,EAAI,CAAC;IACxE,CAAC;IApEc,0BAAS,GAAG,CAAC,CAAC;IAqEjC,uBAAC;CAAA,AAtED,IAsEC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAC9B,OAAO,IAAI,gBAAgB,EAAE,CAAC;AAClC,CAAC","sourcesContent":["import { getParagraphMarker, setParagraphMarker } from 'roosterjs-content-model-dom';\nimport type {\n ContentModelParagraph,\n ContentModelParagraphCommon,\n ParagraphIndexer,\n ParagraphMap,\n ReadonlyContentModelParagraph,\n} from 'roosterjs-content-model-types';\n\ninterface ParagraphWithMarker extends ContentModelParagraphCommon {\n _marker?: string;\n}\n\n/**\n * @internal, used by test code only\n */\nexport interface ParagraphMapReset {\n _reset(): void;\n _getMap(): { [key: string]: ReadonlyContentModelParagraph };\n}\n\nconst idPrefix = 'paragraph';\n\nclass ParagraphMapImpl implements ParagraphMap, ParagraphIndexer, ParagraphMapReset {\n private static prefixNum = 0;\n private nextId = 0;\n private paragraphMap: { [key: string]: ReadonlyContentModelParagraph } = {};\n\n constructor() {\n ParagraphMapImpl.prefixNum++;\n }\n\n assignMarkerToModel(element: HTMLElement, paragraph: ContentModelParagraph): void {\n const marker = getParagraphMarker(element);\n const paragraphWithMarker = paragraph as ParagraphWithMarker;\n\n if (marker) {\n paragraphWithMarker._marker = marker;\n\n this.paragraphMap[marker] = paragraph;\n } else {\n paragraphWithMarker._marker = this.generateId();\n\n this.applyMarkerToDom(element, paragraph);\n }\n }\n\n applyMarkerToDom(element: HTMLElement, paragraph: ContentModelParagraph): void {\n const paragraphWithMarker = paragraph as ParagraphWithMarker;\n\n if (!paragraphWithMarker._marker) {\n paragraphWithMarker._marker = this.generateId();\n }\n\n const marker = paragraphWithMarker._marker;\n\n if (marker) {\n setParagraphMarker(element, marker);\n\n this.paragraphMap[marker] = paragraph;\n }\n }\n\n /**\n * Get paragraph using a previously marked paragraph\n * @param markedParagraph The previously marked paragraph to get\n */\n getParagraphFromMarker(\n markerParagraph: ReadonlyContentModelParagraph\n ): ReadonlyContentModelParagraph | null {\n const marker = (markerParagraph as ParagraphWithMarker)._marker;\n\n return marker ? this.paragraphMap[marker] || null : null;\n }\n\n clear() {\n this.paragraphMap = {};\n }\n\n //#region For test code only\n _reset() {\n ParagraphMapImpl.prefixNum = 0;\n this.nextId = 0;\n }\n\n _getMap() {\n return this.paragraphMap;\n }\n //#endregion\n\n private generateId() {\n return `${idPrefix}_${ParagraphMapImpl.prefixNum}_${this.nextId++}`;\n }\n}\n\n/**\n * @internal\n */\nexport function createParagraphMap(): ParagraphMap & ParagraphIndexer {\n return new ParagraphMapImpl();\n}\n"]}
package/package.json CHANGED
@@ -3,10 +3,10 @@
3
3
  "description": "Core editor for roosterjs",
4
4
  "dependencies": {
5
5
  "tslib": "^2.3.1",
6
- "roosterjs-content-model-dom": "^9.22.0",
7
- "roosterjs-content-model-types": "^9.22.0"
6
+ "roosterjs-content-model-dom": "^9.24.0",
7
+ "roosterjs-content-model-types": "^9.24.0"
8
8
  },
9
- "version": "9.22.0",
9
+ "version": "9.24.0",
10
10
  "main": "./lib/index.js",
11
11
  "typings": "./lib/index.d.ts",
12
12
  "module": "./lib-mjs/index.js",