roosterjs 9.52.0 → 9.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/rooster.js CHANGED
@@ -8020,6 +8020,15 @@ function formatInsertPointWithContentModel(editor, insertPoint, callback, option
8020
8020
  textWithSelection: getShadowTextProcessor(bundle),
8021
8021
  },
8022
8022
  tryGetFromCache: false,
8023
+ // When an element carries "container level" styles such as margin or padding, we first
8024
+ // wrap it in a FormatContainer. After all its child nodes are processed, we decide whether
8025
+ // to keep the FormatContainer or fall back to a plain paragraph when it only wraps a single
8026
+ // paragraph. However, formatInsertPointWithContentModel persists the Content Model group path
8027
+ // during processing so the later formatting callback can still use it (see the
8028
+ // DomToModelContextWithPath interface below). If the FormatContainer falls back to a paragraph,
8029
+ // it is removed from the model and the persisted path becomes invalid. To keep the path valid,
8030
+ // we skip the fallback check here and always keep the FormatContainer when one is needed.
8031
+ skipFormatContainerFallbackCheck: true,
8023
8032
  });
8024
8033
  }
8025
8034
  exports.formatInsertPointWithContentModel = formatInsertPointWithContentModel;
@@ -9057,9 +9066,12 @@ function getContentForCopy(editor, isCut, event) {
9057
9066
  rawEvent: event,
9058
9067
  isCut: isCut,
9059
9068
  }).clonedRoot;
9069
+ // Build the text content from the (possibly modified) cloned root DOM tree so that any
9070
+ // changes made by beforeCutCopy event handlers are reflected in the plain text result as well
9071
+ var textModel = (0, roosterjs_content_model_dom_1.domToContentModel)(clonedRoot, (0, roosterjs_content_model_dom_1.createDomToModelContext)());
9060
9072
  return {
9061
9073
  htmlContent: clonedRoot,
9062
- textContent: (0, roosterjs_content_model_dom_1.contentModelToText)(pasteModel),
9074
+ textContent: (0, roosterjs_content_model_dom_1.contentModelToText)(textModel),
9063
9075
  };
9064
9076
  }
9065
9077
  }
@@ -9927,6 +9939,7 @@ var announce = function (core, announceData) {
9927
9939
  var textToAnnounce = formatString(template || text, formatStrings);
9928
9940
  if (!core.lifecycle.announceContainer) {
9929
9941
  core.lifecycle.announceContainer = (0, createAriaLiveElement_1.createAriaLiveElement)(core.physicalRoot.ownerDocument);
9942
+ core.domHelper.appendToRoot(core.lifecycle.announceContainer);
9930
9943
  }
9931
9944
  if (textToAnnounce && core.lifecycle.announceContainer) {
9932
9945
  var announceContainer = core.lifecycle.announceContainer;
@@ -10100,6 +10113,9 @@ var createContentModel = function (core, option, selectionOverride) {
10100
10113
  var domToModelContext = option
10101
10114
  ? (0, roosterjs_content_model_dom_1.createDomToModelContext)(editorContext, settings.builtIn, settings.customized, option)
10102
10115
  : (0, roosterjs_content_model_dom_1.createDomToModelContextWithConfig)(settings.calculated, editorContext);
10116
+ if (option === null || option === void 0 ? void 0 : option.skipFormatContainerFallbackCheck) {
10117
+ domToModelContext.skipFormatContainerFallbackCheck = true;
10118
+ }
10103
10119
  if (selection) {
10104
10120
  domToModelContext.selection = selection;
10105
10121
  }
@@ -10418,16 +10434,19 @@ var getDOMSelection = function (core) {
10418
10434
  exports.getDOMSelection = getDOMSelection;
10419
10435
  function getNewSelection(core) {
10420
10436
  var _a;
10437
+ var range = core.domHelper.getSelectionRange();
10438
+ if (!range || !core.logicalRoot.contains(range.commonAncestorContainer)) {
10439
+ return null;
10440
+ }
10421
10441
  var selection = (_a = core.logicalRoot.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
10422
- var range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
10423
- return selection && range && core.logicalRoot.contains(range.commonAncestorContainer)
10424
- ? {
10425
- type: 'range',
10426
- range: range,
10427
- isReverted: selection.focusNode != range.endContainer ||
10428
- selection.focusOffset != range.endOffset,
10429
- }
10430
- : null;
10442
+ var isReverted = selection
10443
+ ? selection.focusNode != range.endContainer || selection.focusOffset != range.endOffset
10444
+ : false;
10445
+ return {
10446
+ type: 'range',
10447
+ range: range,
10448
+ isReverted: isReverted,
10449
+ };
10431
10450
  }
10432
10451
 
10433
10452
 
@@ -10871,43 +10890,6 @@ var setContentModel = function (core, model, option, onNodeCreated, isInitializi
10871
10890
  exports.setContentModel = setContentModel;
10872
10891
 
10873
10892
 
10874
- /***/ },
10875
-
10876
- /***/ "./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/addRangeToSelection.ts"
10877
- /*!**************************************************************************************************!*\
10878
- !*** ./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/addRangeToSelection.ts ***!
10879
- \**************************************************************************************************/
10880
- (__unused_webpack_module, exports, __webpack_require__) {
10881
-
10882
- "use strict";
10883
-
10884
- Object.defineProperty(exports, "__esModule", ({ value: true }));
10885
- exports.addRangeToSelection = void 0;
10886
- var areSameSelections_1 = __webpack_require__(/*! ../../corePlugin/cache/areSameSelections */ "./packages/roosterjs-content-model-core/lib/corePlugin/cache/areSameSelections.ts");
10887
- /**
10888
- * @internal
10889
- */
10890
- function addRangeToSelection(doc, range, isReverted) {
10891
- var _a;
10892
- if (isReverted === void 0) { isReverted = false; }
10893
- var selection = (_a = doc.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
10894
- if (selection) {
10895
- var currentRange = selection.rangeCount > 0 && selection.getRangeAt(0);
10896
- if (currentRange && (0, areSameSelections_1.areSameRanges)(currentRange, range)) {
10897
- return;
10898
- }
10899
- selection.removeAllRanges();
10900
- if (!isReverted) {
10901
- selection.addRange(range);
10902
- }
10903
- else {
10904
- selection.setBaseAndExtent(range.endContainer, range.endOffset, range.startContainer, range.startOffset);
10905
- }
10906
- }
10907
- }
10908
- exports.addRangeToSelection = addRangeToSelection;
10909
-
10910
-
10911
10893
  /***/ },
10912
10894
 
10913
10895
  /***/ "./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/findLastedCoInMergedCell.ts"
@@ -11009,7 +10991,6 @@ exports.findTableCellElement = findTableCellElement;
11009
10991
 
11010
10992
  Object.defineProperty(exports, "__esModule", ({ value: true }));
11011
10993
  exports.setDOMSelection = void 0;
11012
- var addRangeToSelection_1 = __webpack_require__(/*! ./addRangeToSelection */ "./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/addRangeToSelection.ts");
11013
10994
  var areSameSelections_1 = __webpack_require__(/*! ../../corePlugin/cache/areSameSelections */ "./packages/roosterjs-content-model-core/lib/corePlugin/cache/areSameSelections.ts");
11014
10995
  var ensureUniqueId_1 = __webpack_require__(/*! ../setEditorStyle/ensureUniqueId */ "./packages/roosterjs-content-model-core/lib/coreApi/setEditorStyle/ensureUniqueId.ts");
11015
10996
  var findLastedCoInMergedCell_1 = __webpack_require__(/*! ./findLastedCoInMergedCell */ "./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/findLastedCoInMergedCell.ts");
@@ -11035,7 +11016,6 @@ var setDOMSelection = function (core, selection, skipSelectionChangedEvent) {
11035
11016
  // We are applying a new selection, so we don't need to apply cached selection in DOMEventPlugin.
11036
11017
  // Set skipReselectOnFocus to skip this behavior
11037
11018
  var skipReselectOnFocus = core.selection.skipReselectOnFocus;
11038
- var doc = core.physicalRoot.ownerDocument;
11039
11019
  var isDarkMode = core.lifecycle.isDarkMode;
11040
11020
  core.selection.skipReselectOnFocus = true;
11041
11021
  core.api.setEditorStyle(core, DOM_SELECTION_CSS_KEY, null /*cssRule*/);
@@ -11051,7 +11031,7 @@ var setDOMSelection = function (core, selection, skipSelectionChangedEvent) {
11051
11031
  : core.selection.imageSelectionBorderColor;
11052
11032
  core.api.setEditorStyle(core, DOM_SELECTION_CSS_KEY, "outline-style:solid!important; outline-color:" + (imageSelectionColor || DEFAULT_SELECTION_BORDER_COLOR) + "!important;", [(0, roosterjs_content_model_dom_1.getSafeIdSelector)((0, ensureUniqueId_1.ensureUniqueId)(image, IMAGE_ID))]);
11053
11033
  core.api.setEditorStyle(core, HIDE_SELECTION_CSS_KEY, TRANSPARENT_SELECTION_CSS_RULE, [SELECTION_SELECTOR]);
11054
- setRangeSelection(doc, image, false /* collapse */);
11034
+ setRangeSelection(core, image, false /* collapse */);
11055
11035
  break;
11056
11036
  case 'table':
11057
11037
  var table = selection.table, firstColumn = selection.firstColumn, firstRow = selection.firstRow, lastColumn = selection.lastColumn, lastRow = selection.lastRow;
@@ -11088,11 +11068,11 @@ var setDOMSelection = function (core, selection, skipSelectionChangedEvent) {
11088
11068
  (0, toggleCaret_1.toggleCaret)(core, true /* hide */);
11089
11069
  var nodeToSelect = ((_a = firstCell.cell) === null || _a === void 0 ? void 0 : _a.firstElementChild) || firstCell.cell;
11090
11070
  if (nodeToSelect) {
11091
- setRangeSelection(doc, nodeToSelect || undefined, true /* collapse */);
11071
+ setRangeSelection(core, nodeToSelect || undefined, true /* collapse */);
11092
11072
  }
11093
11073
  break;
11094
11074
  case 'range':
11095
- (0, addRangeToSelection_1.addRangeToSelection)(doc, selection.range, selection.isReverted);
11075
+ core.domHelper.setSelectionRange(selection.range, selection.isReverted);
11096
11076
  core.selection.selection = core.domHelper.hasFocus() ? null : selection;
11097
11077
  break;
11098
11078
  default:
@@ -11112,9 +11092,10 @@ var setDOMSelection = function (core, selection, skipSelectionChangedEvent) {
11112
11092
  }
11113
11093
  };
11114
11094
  exports.setDOMSelection = setDOMSelection;
11115
- function setRangeSelection(doc, element, collapse) {
11095
+ function setRangeSelection(core, element, collapse) {
11116
11096
  var _a;
11117
- if (element && doc.contains(element)) {
11097
+ if (element && core.domHelper.isNodeInEditor(element)) {
11098
+ var doc = core.physicalRoot.ownerDocument;
11118
11099
  var range = doc.createRange();
11119
11100
  var isReverted = undefined;
11120
11101
  range.selectNode(element);
@@ -11130,7 +11111,7 @@ function setRangeSelection(doc, element, collapse) {
11130
11111
  selection.focusOffset != range_1.endOffset;
11131
11112
  }
11132
11113
  }
11133
- (0, addRangeToSelection_1.addRangeToSelection)(doc, range, isReverted);
11114
+ core.domHelper.setSelectionRange(range, isReverted);
11134
11115
  }
11135
11116
  }
11136
11117
 
@@ -11317,9 +11298,9 @@ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-mo
11317
11298
  */
11318
11299
  function ensureUniqueId(element, idPrefix) {
11319
11300
  idPrefix = element.id || idPrefix;
11320
- var doc = element.ownerDocument;
11301
+ var root = element.getRootNode();
11321
11302
  var i = 0;
11322
- while (!element.id || doc.querySelectorAll((0, roosterjs_content_model_dom_1.getSafeIdSelector)(element.id)).length > 1) {
11303
+ while (!element.id || root.querySelectorAll((0, roosterjs_content_model_dom_1.getSafeIdSelector)(element.id)).length > 1) {
11323
11304
  element.id = idPrefix + '_' + i++;
11324
11305
  }
11325
11306
  return element.id;
@@ -11352,7 +11333,7 @@ var setEditorStyle = function (core, key, cssRule, subSelectors, maxRuleLength)
11352
11333
  if (!styleElement && cssRule) {
11353
11334
  var doc = core.physicalRoot.ownerDocument;
11354
11335
  styleElement = doc.createElement('style');
11355
- doc.head.appendChild(styleElement);
11336
+ core.domHelper.appendToRoot(styleElement);
11356
11337
  styleElement.dataset.roosterjsStyleKey = key;
11357
11338
  core.lifecycle.styleElements[key] = styleElement;
11358
11339
  }
@@ -11606,6 +11587,11 @@ var CachePlugin = /** @class */ (function () {
11606
11587
  _this.invalidateCache();
11607
11588
  }
11608
11589
  break;
11590
+ case 'attribute':
11591
+ if (!_this.state.domIndexer.reconcileImageAttribute(mutation.element, mutation.attributeName)) {
11592
+ _this.invalidateCache();
11593
+ }
11594
+ break;
11609
11595
  case 'unknown':
11610
11596
  _this.invalidateCache();
11611
11597
  break;
@@ -11830,12 +11816,13 @@ exports.createParagraphMap = createParagraphMap;
11830
11816
  /*!*****************************************************************************************!*\
11831
11817
  !*** ./packages/roosterjs-content-model-core/lib/corePlugin/cache/areSameSelections.ts ***!
11832
11818
  \*****************************************************************************************/
11833
- (__unused_webpack_module, exports) {
11819
+ (__unused_webpack_module, exports, __webpack_require__) {
11834
11820
 
11835
11821
  "use strict";
11836
11822
 
11837
11823
  Object.defineProperty(exports, "__esModule", ({ value: true }));
11838
- exports.areSameRanges = exports.areSameTableSelections = exports.areSameSelections = void 0;
11824
+ exports.areSameTableSelections = exports.areSameSelections = void 0;
11825
+ var areSameRanges_1 = __webpack_require__(/*! ../../utils/areSameRanges */ "./packages/roosterjs-content-model-core/lib/utils/areSameRanges.ts");
11839
11826
  /**
11840
11827
  * @internal
11841
11828
  * Check if the given selections are the same
@@ -11861,7 +11848,7 @@ function areSameSelections(sel1, sel2) {
11861
11848
  range1.endOffset == end.offset);
11862
11849
  }
11863
11850
  else {
11864
- return areSameRanges(range1, sel2.range);
11851
+ return (0, areSameRanges_1.areSameRanges)(range1, sel2.range);
11865
11852
  }
11866
11853
  }
11867
11854
  else {
@@ -11880,7 +11867,6 @@ var TableSelectionKeys = [
11880
11867
  'firstRow',
11881
11868
  'lastRow',
11882
11869
  ];
11883
- var RangeKeys = ['startContainer', 'endContainer', 'startOffset', 'endOffset'];
11884
11870
  /**
11885
11871
  * @internal
11886
11872
  */
@@ -11888,13 +11874,6 @@ function areSameTableSelections(t1, t2) {
11888
11874
  return areSame(t1, t2, TableSelectionKeys);
11889
11875
  }
11890
11876
  exports.areSameTableSelections = areSameTableSelections;
11891
- /**
11892
- * @internal
11893
- */
11894
- function areSameRanges(r1, r2) {
11895
- return areSame(r1, r2, RangeKeys);
11896
- }
11897
- exports.areSameRanges = areSameRanges;
11898
11877
  function isCacheSelection(sel) {
11899
11878
  return !!sel.start;
11900
11879
  }
@@ -12072,7 +12051,13 @@ var DomIndexerImpl = /** @class */ (function () {
12072
12051
  }
12073
12052
  else {
12074
12053
  var marker1 = this.reconcileNodeSelection(startContainer, startOffset);
12075
- var marker2 = this.reconcileNodeSelection(endContainer, endOffset);
12054
+ // Pass marker1 to the second call so its adjacent-marker cleanup
12055
+ // does not consume the SelectionMarker we just inserted. Without
12056
+ // this guard, when marker1 lands directly next to endContainer's
12057
+ // segment in paragraph.segments (e.g. startOffset == startContainer
12058
+ // text length), the second splice would absorb marker1 and leave
12059
+ // setSelection with a dangling reference. See issue #3341.
12060
+ var marker2 = this.reconcileNodeSelection(endContainer, endOffset, undefined, undefined, marker1);
12076
12061
  if (marker1 && marker2) {
12077
12062
  if (newSelection.isReverted) {
12078
12063
  model.hasRevertedRangeSelection = true;
@@ -12138,6 +12123,33 @@ var DomIndexerImpl = /** @class */ (function () {
12138
12123
  return false;
12139
12124
  }
12140
12125
  };
12126
+ DomIndexerImpl.prototype.reconcileImageAttribute = function (element, attributeName) {
12127
+ var _a, _b;
12128
+ if ((0, roosterjs_content_model_dom_1.isElementOfType)(element, 'img')) {
12129
+ var image = (_a = getIndexedSegmentItem(element)) === null || _a === void 0 ? void 0 : _a.segments[0];
12130
+ if ((image === null || image === void 0 ? void 0 : image.segmentType) == 'Image') {
12131
+ if (attributeName == 'src') {
12132
+ // Use getAttribute('src') instead of retrieving src directly, in case the src
12133
+ // has port and may be stripped by browser. This matches imageProcessor.
12134
+ image.src = (_b = element.getAttribute('src')) !== null && _b !== void 0 ? _b : '';
12135
+ return true;
12136
+ }
12137
+ else if (attributeName.indexOf('data-') == 0) {
12138
+ // A data-* attribute may be added, modified or removed. Rebuild the whole
12139
+ // dataset from DOM to keep it in sync, the same way imageProcessor builds it.
12140
+ var dataset_1 = image.dataset;
12141
+ (0, roosterjs_content_model_dom_1.getObjectKeys)(dataset_1).forEach(function (key) {
12142
+ delete dataset_1[key];
12143
+ });
12144
+ (0, roosterjs_content_model_dom_1.getObjectKeys)(element.dataset).forEach(function (key) {
12145
+ dataset_1[key] = element.dataset[key] || '';
12146
+ });
12147
+ return true;
12148
+ }
12149
+ }
12150
+ }
12151
+ return false;
12152
+ };
12141
12153
  DomIndexerImpl.prototype.onBlockEntityDelimiter = function (node, entity, parent) {
12142
12154
  if ((0, roosterjs_content_model_dom_1.isNodeOfType)(node, 'ELEMENT_NODE') && (0, roosterjs_content_model_dom_1.isEntityDelimiter)(node) && node.firstChild) {
12143
12155
  var indexedDelimiter = node.firstChild;
@@ -12148,10 +12160,10 @@ var DomIndexerImpl = /** @class */ (function () {
12148
12160
  var start = selection.start, end = selection.end;
12149
12161
  return start.node == end.node && start.offset == end.offset;
12150
12162
  };
12151
- DomIndexerImpl.prototype.reconcileNodeSelection = function (node, offset, defaultFormat, selectionMarker) {
12163
+ DomIndexerImpl.prototype.reconcileNodeSelection = function (node, offset, defaultFormat, selectionMarker, preserveMarker) {
12152
12164
  if ((0, roosterjs_content_model_dom_1.isNodeOfType)(node, 'TEXT_NODE')) {
12153
12165
  if (isIndexedSegment(node)) {
12154
- return this.reconcileTextSelection(node, offset, undefined, selectionMarker);
12166
+ return this.reconcileTextSelection(node, offset, undefined, selectionMarker, preserveMarker);
12155
12167
  }
12156
12168
  else if (isIndexedDelimiter(node)) {
12157
12169
  return this.reconcileDelimiterSelection(node, defaultFormat);
@@ -12181,7 +12193,7 @@ var DomIndexerImpl = /** @class */ (function () {
12181
12193
  }
12182
12194
  return marker;
12183
12195
  };
12184
- DomIndexerImpl.prototype.reconcileTextSelection = function (textNode, startOffset, endOffset, selectionMarker) {
12196
+ DomIndexerImpl.prototype.reconcileTextSelection = function (textNode, startOffset, endOffset, selectionMarker, preserveMarker) {
12185
12197
  var _a;
12186
12198
  var _b, _c, _d, _e, _f, _g;
12187
12199
  var _h = textNode.__roosterjsContentModel, paragraph = _h.paragraph, segments = _h.segments;
@@ -12234,11 +12246,13 @@ var DomIndexerImpl = /** @class */ (function () {
12234
12246
  var lastIndex = paragraph.segments.indexOf(last);
12235
12247
  if (firstIndex >= 0 && lastIndex >= 0) {
12236
12248
  while (firstIndex > 0 &&
12237
- paragraph.segments[firstIndex - 1].segmentType == 'SelectionMarker') {
12249
+ paragraph.segments[firstIndex - 1].segmentType == 'SelectionMarker' &&
12250
+ paragraph.segments[firstIndex - 1] !== preserveMarker) {
12238
12251
  firstIndex--;
12239
12252
  }
12240
12253
  while (lastIndex < paragraph.segments.length - 1 &&
12241
- paragraph.segments[lastIndex + 1].segmentType == 'SelectionMarker') {
12254
+ paragraph.segments[lastIndex + 1].segmentType == 'SelectionMarker' &&
12255
+ paragraph.segments[lastIndex + 1] !== preserveMarker) {
12242
12256
  lastIndex++;
12243
12257
  }
12244
12258
  (_a = paragraph.segments).splice.apply(_a, (0, tslib_1.__spreadArray)([firstIndex, lastIndex - firstIndex + 1], (0, tslib_1.__read)(newSegments), false));
@@ -12451,6 +12465,16 @@ var TextMutationObserverImpl = /** @class */ (function () {
12451
12465
  (0, roosterjs_content_model_dom_1.isNodeOfType)(target, 'ELEMENT_NODE')) {
12452
12466
  _this.onMutation({ type: 'elementId', element: target });
12453
12467
  }
12468
+ else if (mutation.attributeName &&
12469
+ (0, roosterjs_content_model_dom_1.isNodeOfType)(target, 'ELEMENT_NODE') &&
12470
+ (mutation.attributeName == 'src' ||
12471
+ mutation.attributeName.indexOf('data-') == 0)) {
12472
+ _this.onMutation({
12473
+ type: 'attribute',
12474
+ element: target,
12475
+ attributeName: mutation.attributeName,
12476
+ });
12477
+ }
12454
12478
  else {
12455
12479
  // We cannot handle attributes changes on editor content for now
12456
12480
  canHandle = false;
@@ -14356,6 +14380,7 @@ var LifecyclePlugin = /** @class */ (function () {
14356
14380
  delete this.state.rewriteFromModel;
14357
14381
  // Initialize the Announce container.
14358
14382
  this.state.announceContainer = (0, createAriaLiveElement_1.createAriaLiveElement)(editor.getDocument());
14383
+ editor.getDOMHelper().appendToRoot(this.state.announceContainer);
14359
14384
  };
14360
14385
  /**
14361
14386
  * Dispose this plugin
@@ -14549,20 +14574,23 @@ var SelectionPlugin = /** @class */ (function () {
14549
14574
  }
14550
14575
  };
14551
14576
  this.onSelectionChange = function () {
14552
- var _a;
14577
+ var _a, _b;
14553
14578
  if (((_a = _this.editor) === null || _a === void 0 ? void 0 : _a.hasFocus()) && !_this.editor.isInShadowEdit()) {
14554
14579
  var newSelection = _this.editor.getDOMSelection();
14580
+ var domHelper = _this.editor.getDOMHelper();
14555
14581
  //If am image selection changed to a wider range due a keyboard event, we should update the selection
14556
- var selection = _this.editor.getDocument().getSelection();
14557
- if (selection && selection.focusNode) {
14558
- var image = (0, isSingleImageInSelection_1.isSingleImageInSelection)(selection);
14582
+ var range = domHelper.getSelectionRange();
14583
+ if (range) {
14584
+ var image = (0, isSingleImageInSelection_1.isSingleImageInSelection)(range);
14559
14585
  if ((newSelection === null || newSelection === void 0 ? void 0 : newSelection.type) == 'image' && !image) {
14560
- var range = selection.getRangeAt(0);
14586
+ var sel = (_b = _this.editor.getDocument().defaultView) === null || _b === void 0 ? void 0 : _b.getSelection();
14587
+ var isReverted = sel
14588
+ ? sel.focusNode != range.endContainer || sel.focusOffset != range.endOffset
14589
+ : false;
14561
14590
  _this.editor.setDOMSelection({
14562
14591
  type: 'range',
14563
14592
  range: range,
14564
- isReverted: selection.focusNode != range.endContainer ||
14565
- selection.focusOffset != range.endOffset,
14593
+ isReverted: isReverted,
14566
14594
  });
14567
14595
  }
14568
14596
  else if ((newSelection === null || newSelection === void 0 ? void 0 : newSelection.type) !== 'image' && image) {
@@ -15951,10 +15979,23 @@ exports.Editor = Editor;
15951
15979
 
15952
15980
  Object.defineProperty(exports, "__esModule", ({ value: true }));
15953
15981
  exports.createDOMHelper = void 0;
15982
+ var areSameRanges_1 = __webpack_require__(/*! ../../utils/areSameRanges */ "./packages/roosterjs-content-model-core/lib/utils/areSameRanges.ts");
15954
15983
  var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
15984
+ function isSelectionWithComposedRanges(sel) {
15985
+ return 'getComposedRanges' in sel;
15986
+ }
15987
+ function isShadowRoot(node) {
15988
+ return 'host' in node;
15989
+ }
15955
15990
  var DOMHelperImpl = /** @class */ (function () {
15956
15991
  function DOMHelperImpl(contentDiv, options) {
15992
+ var _a;
15957
15993
  this.contentDiv = contentDiv;
15994
+ var rootNode = contentDiv.getRootNode();
15995
+ this.shadowRoot = (options === null || options === void 0 ? void 0 : options.useShadowDom) && isShadowRoot(rootNode) ? rootNode : null;
15996
+ this.doc = contentDiv.ownerDocument;
15997
+ var sel = (_a = this.doc.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
15998
+ this.useComposedRanges = !!(this.shadowRoot && sel && 'getComposedRanges' in sel);
15958
15999
  }
15959
16000
  DOMHelperImpl.prototype.queryElements = function (selector) {
15960
16001
  return (0, roosterjs_content_model_dom_1.toArray)(this.contentDiv.querySelectorAll(selector));
@@ -16016,7 +16057,9 @@ var DOMHelperImpl = /** @class */ (function () {
16016
16057
  return this.contentDiv;
16017
16058
  };
16018
16059
  DOMHelperImpl.prototype.hasFocus = function () {
16019
- var activeElement = this.contentDiv.ownerDocument.activeElement;
16060
+ var activeElement = this.shadowRoot
16061
+ ? this.shadowRoot.activeElement
16062
+ : this.doc.activeElement;
16020
16063
  return !!(activeElement && this.contentDiv.contains(activeElement));
16021
16064
  };
16022
16065
  /**
@@ -16083,6 +16126,51 @@ var DOMHelperImpl = /** @class */ (function () {
16083
16126
  DOMHelperImpl.prototype.getRangesByText = function (text, matchCase, wholeWord) {
16084
16127
  return (0, roosterjs_content_model_dom_1.getRangesByText)(this.contentDiv, text, matchCase, wholeWord, true /*editableOnly*/);
16085
16128
  };
16129
+ DOMHelperImpl.prototype.getSelectionRange = function () {
16130
+ var _a;
16131
+ var sel = (_a = this.doc.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
16132
+ if (!sel) {
16133
+ return null;
16134
+ }
16135
+ if (this.useComposedRanges && this.shadowRoot && isSelectionWithComposedRanges(sel)) {
16136
+ var staticRanges = sel.getComposedRanges({
16137
+ shadowRoots: [this.shadowRoot],
16138
+ });
16139
+ if ((staticRanges === null || staticRanges === void 0 ? void 0 : staticRanges.length) > 0) {
16140
+ var sr = staticRanges[0];
16141
+ var range = this.doc.createRange();
16142
+ range.setStart(sr.startContainer, sr.startOffset);
16143
+ range.setEnd(sr.endContainer, sr.endOffset);
16144
+ return range;
16145
+ }
16146
+ return null;
16147
+ }
16148
+ return sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
16149
+ };
16150
+ DOMHelperImpl.prototype.setSelectionRange = function (range, isReverted) {
16151
+ var _a;
16152
+ if (isReverted === void 0) { isReverted = false; }
16153
+ var sel = (_a = this.doc.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
16154
+ var currentRange = this.getSelectionRange();
16155
+ if (!sel || (currentRange && (0, areSameRanges_1.areSameRanges)(range, currentRange))) {
16156
+ return;
16157
+ }
16158
+ var startContainer = range.startContainer, startOffset = range.startOffset, endContainer = range.endContainer, endOffset = range.endOffset;
16159
+ if (!isReverted) {
16160
+ sel.setBaseAndExtent(startContainer, startOffset, endContainer, endOffset);
16161
+ }
16162
+ else {
16163
+ sel.setBaseAndExtent(endContainer, endOffset, startContainer, startOffset);
16164
+ }
16165
+ };
16166
+ DOMHelperImpl.prototype.appendToRoot = function (element) {
16167
+ if (this.shadowRoot) {
16168
+ this.shadowRoot.appendChild(element);
16169
+ }
16170
+ else {
16171
+ this.doc.body.appendChild(element);
16172
+ }
16173
+ };
16086
16174
  return DOMHelperImpl;
16087
16175
  }());
16088
16176
  /**
@@ -16199,7 +16287,10 @@ function createEditorCore(contentDiv, options) {
16199
16287
  corePlugins.lifecycle,
16200
16288
  ], false), environment: createEditorEnvironment(contentDiv, options), darkColorHandler: (0, DarkColorHandlerImpl_1.createDarkColorHandler)(contentDiv, (_b = options.getDarkColor) !== null && _b !== void 0 ? _b : getDarkColorFallback, options.knownColors, options.generateColorKey), trustedHTMLHandler: options.trustedHTMLHandler && !(0, domCreator_1.isDOMCreator)(options.trustedHTMLHandler)
16201
16289
  ? options.trustedHTMLHandler
16202
- : (0, domCreator_1.createTrustedHTMLHandler)(domCreator), domCreator: domCreator, domHelper: (0, DOMHelperImpl_1.createDOMHelper)(contentDiv) }, getPluginState(corePlugins)), { disposeErrorHandler: options.disposeErrorHandler, onFixUpModel: options.onFixUpModel, experimentalFeatures: options.experimentalFeatures ? (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(options.experimentalFeatures), false) : [] });
16290
+ : (0, domCreator_1.createTrustedHTMLHandler)(domCreator), domCreator: domCreator, domHelper: (0, DOMHelperImpl_1.createDOMHelper)(contentDiv, {
16291
+ useShadowDom: !!options.experimentalFeatures &&
16292
+ options.experimentalFeatures.indexOf('ShadowDom') >= 0,
16293
+ }) }, getPluginState(corePlugins)), { disposeErrorHandler: options.disposeErrorHandler, onFixUpModel: options.onFixUpModel, experimentalFeatures: options.experimentalFeatures ? (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(options.experimentalFeatures), false) : [] });
16203
16294
  }
16204
16295
  exports.createEditorCore = createEditorCore;
16205
16296
  function createEditorEnvironment(contentDiv, options) {
@@ -16357,6 +16448,8 @@ var containerSizeFormatParser = function (format, element) {
16357
16448
  if (element.tagName == 'DIV' || element.tagName == 'P') {
16358
16449
  delete format.width;
16359
16450
  delete format.height;
16451
+ delete format.maxHeight;
16452
+ delete format.maxWidth;
16360
16453
  }
16361
16454
  };
16362
16455
  exports.containerSizeFormatParser = containerSizeFormatParser;
@@ -16653,6 +16746,29 @@ var pasteWhiteSpaceFormatParser = function (format, element, context, defaultSty
16653
16746
  exports.pasteWhiteSpaceFormatParser = pasteWhiteSpaceFormatParser;
16654
16747
 
16655
16748
 
16749
+ /***/ },
16750
+
16751
+ /***/ "./packages/roosterjs-content-model-core/lib/utils/areSameRanges.ts"
16752
+ /*!**************************************************************************!*\
16753
+ !*** ./packages/roosterjs-content-model-core/lib/utils/areSameRanges.ts ***!
16754
+ \**************************************************************************/
16755
+ (__unused_webpack_module, exports) {
16756
+
16757
+ "use strict";
16758
+
16759
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
16760
+ exports.areSameRanges = void 0;
16761
+ var RangeKeys = ['startContainer', 'endContainer', 'startOffset', 'endOffset'];
16762
+ /**
16763
+ * @internal
16764
+ * Check if two ranges have the same start and end positions.
16765
+ */
16766
+ function areSameRanges(r1, r2) {
16767
+ return RangeKeys.every(function (k) { return r1[k] == r2[k]; });
16768
+ }
16769
+ exports.areSameRanges = areSameRanges;
16770
+
16771
+
16656
16772
  /***/ },
16657
16773
 
16658
16774
  /***/ "./packages/roosterjs-content-model-core/lib/utils/createAriaLiveElement.ts"
@@ -16678,7 +16794,6 @@ function createAriaLiveElement(document) {
16678
16794
  div.style.whiteSpace = 'nowrap';
16679
16795
  div.style.width = '1px';
16680
16796
  div.ariaLive = 'assertive';
16681
- document.body.appendChild(div);
16682
16797
  return div;
16683
16798
  }
16684
16799
  exports.createAriaLiveElement = createAriaLiveElement;
@@ -18076,7 +18191,9 @@ var formatContainerProcessorInternal = function (group, element, context, forceF
18076
18191
  if (element.style.fontSize && parseInt(element.style.fontSize) == 0) {
18077
18192
  formatContainer.zeroFontSize = true;
18078
18193
  }
18079
- if (shouldFallbackToParagraph(formatContainer) && !forceFormatContainer) {
18194
+ if (!context.skipFormatContainerFallbackCheck &&
18195
+ shouldFallbackToParagraph(formatContainer) &&
18196
+ !forceFormatContainer) {
18080
18197
  // For DIV container that only has one paragraph child, container style can be merged into paragraph
18081
18198
  // and no need to have this container
18082
18199
  var paragraph = formatContainer.blocks[0];
@@ -21169,11 +21286,13 @@ exports.directionFormatHandler = {
21169
21286
  format.direction = dir == 'rtl' ? 'rtl' : 'ltr';
21170
21287
  }
21171
21288
  },
21172
- apply: function (format, element) {
21289
+ apply: function (format, element, context) {
21173
21290
  if (format.direction) {
21174
21291
  element.style.direction = format.direction;
21175
21292
  }
21176
- if (format.direction == 'rtl' && (0, isElementOfType_1.isElementOfType)(element, 'table')) {
21293
+ if (format.direction == 'rtl' &&
21294
+ (0, isElementOfType_1.isElementOfType)(element, 'table') &&
21295
+ context.implicitFormat.direction != 'rtl') {
21177
21296
  element.style.justifySelf = 'flex-end';
21178
21297
  }
21179
21298
  },
@@ -21710,6 +21829,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
21710
21829
  exports.borderFormatHandler = void 0;
21711
21830
  var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
21712
21831
  var borderKeys_1 = __webpack_require__(/*! ../utils/borderKeys */ "./packages/roosterjs-content-model-dom/lib/formatHandlers/utils/borderKeys.ts");
21832
+ var borderValues_1 = __webpack_require__(/*! ../../domUtils/style/borderValues */ "./packages/roosterjs-content-model-dom/lib/domUtils/style/borderValues.ts");
21713
21833
  // This array needs to match BorderKeys array
21714
21834
  var BorderWidthKeys = [
21715
21835
  'borderTopWidth',
@@ -21738,7 +21858,17 @@ exports.borderFormatHandler = {
21738
21858
  width = '0px';
21739
21859
  }
21740
21860
  if (value && width != defaultWidth) {
21741
- format[key] = value == 'none' ? '' : value;
21861
+ var result = value;
21862
+ if (result.includes('initial')) {
21863
+ // Remove 'initial' from the last part (color) of the border value
21864
+ // since browsers ignore it when setting the inline style property
21865
+ var border = (0, borderValues_1.extractBorderValues)(value);
21866
+ if (border.color === 'initial') {
21867
+ border.color = '';
21868
+ }
21869
+ result = (0, borderValues_1.combineBorderValue)(border);
21870
+ }
21871
+ format[key] = result == 'none' ? '' : result;
21742
21872
  }
21743
21873
  });
21744
21874
  var borderRadius = element.style.borderRadius;
@@ -24140,6 +24270,7 @@ var createText_1 = __webpack_require__(/*! ../creators/createText */ "./packages
24140
24270
  var ensureParagraph_1 = __webpack_require__(/*! ./ensureParagraph */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/ensureParagraph.ts");
24141
24271
  var hasSpacesOnly_1 = __webpack_require__(/*! ./hasSpacesOnly */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/hasSpacesOnly.ts");
24142
24272
  var isWhiteSpacePreserved_1 = __webpack_require__(/*! ../../domUtils/isWhiteSpacePreserved */ "./packages/roosterjs-content-model-dom/lib/domUtils/isWhiteSpacePreserved.ts");
24273
+ var stripInvisibleUnicode_1 = __webpack_require__(/*! ./stripInvisibleUnicode */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/stripInvisibleUnicode.ts");
24143
24274
  /**
24144
24275
  * Add a new text segment to current paragraph
24145
24276
  * @param group Current BlockGroup that the paragraph belong to
@@ -24155,7 +24286,11 @@ function addTextSegment(group, text, context) {
24155
24286
  if (!(0, hasSpacesOnly_1.hasSpacesOnly)(text) ||
24156
24287
  ((_a = paragraph === null || paragraph === void 0 ? void 0 : paragraph.segments.length) !== null && _a !== void 0 ? _a : 0) > 0 ||
24157
24288
  (0, isWhiteSpacePreserved_1.isWhiteSpacePreserved)(paragraph === null || paragraph === void 0 ? void 0 : paragraph.format.whiteSpace)) {
24158
- textModel = (0, createText_1.createText)(text, context.segmentFormat);
24289
+ var filteredText = context.experimentalFeatures &&
24290
+ context.experimentalFeatures.indexOf('FilterInvisibleUnicode') > -1
24291
+ ? (0, stripInvisibleUnicode_1.stripInvisibleUnicode)(text)
24292
+ : text;
24293
+ textModel = (0, createText_1.createText)(filteredText, context.segmentFormat);
24159
24294
  if (context.isInSelection) {
24160
24295
  textModel.isSelected = true;
24161
24296
  }
@@ -24790,6 +24925,34 @@ function normalizeSegmentFormat(format, environment) {
24790
24925
  exports.normalizeSegmentFormat = normalizeSegmentFormat;
24791
24926
 
24792
24927
 
24928
+ /***/ },
24929
+
24930
+ /***/ "./packages/roosterjs-content-model-dom/lib/modelApi/common/stripInvisibleUnicode.ts"
24931
+ /*!*******************************************************************************************!*\
24932
+ !*** ./packages/roosterjs-content-model-dom/lib/modelApi/common/stripInvisibleUnicode.ts ***!
24933
+ \*******************************************************************************************/
24934
+ (__unused_webpack_module, exports) {
24935
+
24936
+ "use strict";
24937
+
24938
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
24939
+ exports.stripInvisibleUnicode = void 0;
24940
+ // According to https://embracethered.com/blog/posts/2024/hiding-and-finding-text-with-unicode-tags/
24941
+ // there are some invisible unicode characters in the range of U+E0000 to U+EFFFF, which are used for hiding text in HTML.
24942
+ // We need to strip them out before processing the pasted content, otherwise they will be treated as normal text and cause unexpected behavior.
24943
+ var INVISIBLE_UNICODE_REGEX = /[\u{E0000}-\u{EFFFF}]/gu;
24944
+ /**
24945
+ * @internal
24946
+ * Strip invisible unicode characters from the given string
24947
+ * @param value The string to be processed
24948
+ * @returns The string with invisible unicode characters removed
24949
+ */
24950
+ function stripInvisibleUnicode(value) {
24951
+ return value.replace(INVISIBLE_UNICODE_REGEX, '');
24952
+ }
24953
+ exports.stripInvisibleUnicode = stripInvisibleUnicode;
24954
+
24955
+
24793
24956
  /***/ },
24794
24957
 
24795
24958
  /***/ "./packages/roosterjs-content-model-dom/lib/modelApi/common/unwrapBlock.ts"
@@ -25248,16 +25411,25 @@ exports.createParagraphDecorator = createParagraphDecorator;
25248
25411
 
25249
25412
  Object.defineProperty(exports, "__esModule", ({ value: true }));
25250
25413
  exports.createSelectionMarker = void 0;
25251
- var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
25414
+ var EmptySegmentFormat_1 = __webpack_require__(/*! ../../constants/EmptySegmentFormat */ "./packages/roosterjs-content-model-dom/lib/constants/EmptySegmentFormat.ts");
25415
+ var getObjectKeys_1 = __webpack_require__(/*! ../../domUtils/getObjectKeys */ "./packages/roosterjs-content-model-dom/lib/domUtils/getObjectKeys.ts");
25252
25416
  /**
25253
25417
  * Create a ContentModelSelectionMarker model
25254
25418
  * @param format @optional The format of this model
25255
25419
  */
25256
25420
  function createSelectionMarker(format) {
25421
+ var filteredFormat = {};
25422
+ if (format) {
25423
+ (0, getObjectKeys_1.getObjectKeys)(EmptySegmentFormat_1.EmptySegmentFormat).forEach(function (key) {
25424
+ if (key in format) {
25425
+ filteredFormat[key] = format[key];
25426
+ }
25427
+ });
25428
+ }
25257
25429
  return {
25258
25430
  segmentType: 'SelectionMarker',
25259
25431
  isSelected: true,
25260
- format: (0, tslib_1.__assign)({}, format),
25432
+ format: filteredFormat,
25261
25433
  };
25262
25434
  }
25263
25435
  exports.createSelectionMarker = createSelectionMarker;
@@ -28558,6 +28730,7 @@ function internalIterateSelections(path, callback, option, table, treatAllAsSele
28558
28730
 
28559
28731
  Object.defineProperty(exports, "__esModule", ({ value: true }));
28560
28732
  exports.setSelection = void 0;
28733
+ var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
28561
28734
  var isGeneralSegment_1 = __webpack_require__(/*! ../typeCheck/isGeneralSegment */ "./packages/roosterjs-content-model-dom/lib/modelApi/typeCheck/isGeneralSegment.ts");
28562
28735
  var mutate_1 = __webpack_require__(/*! ../common/mutate */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/mutate.ts");
28563
28736
  /**
@@ -28595,6 +28768,7 @@ function setSelectionToBlockGroup(group, isInSelection, start, end) {
28595
28768
  });
28596
28769
  }
28597
28770
  function setSelectionToBlock(block, isInSelection, start, end) {
28771
+ var _a;
28598
28772
  switch (block.blockType) {
28599
28773
  case 'BlockGroup':
28600
28774
  return setSelectionToBlockGroup(block, isInSelection, start, end);
@@ -28615,16 +28789,31 @@ function setSelectionToBlock(block, isInSelection, start, end) {
28615
28789
  return isInSelection;
28616
28790
  });
28617
28791
  case 'Paragraph':
28618
- var segmentsToDelete_1 = [];
28792
+ var state_1 = {
28793
+ segmentsToDelete: [],
28794
+ boundaryMarkers: [],
28795
+ hasSelectedNonMarker: false,
28796
+ };
28619
28797
  block.segments.forEach(function (segment, i) {
28620
28798
  isInSelection = handleSelection(isInSelection, segment, start, end, function (isInSelection) {
28621
- return setSelectionToSegment(block, segment, isInSelection, segmentsToDelete_1, start, end, i);
28799
+ return setSelectionToSegment(block, segment, isInSelection, state_1, start, end, i);
28622
28800
  });
28623
28801
  });
28624
- if (segmentsToDelete_1.length > 0) {
28802
+ if (state_1.hasSelectedNonMarker) {
28803
+ // This paragraph contains a real (non-marker) selected segment, so any leading/trailing
28804
+ // selection marker of the range is redundant within this paragraph and can be removed.
28805
+ // We only do this within the same paragraph: a boundary marker at a paragraph edge must be
28806
+ // kept to distinguish "selection starts at the beginning of this line" from "selection
28807
+ // starts at the end of the previous line".
28808
+ (_a = state_1.segmentsToDelete).push.apply(_a, (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(state_1.boundaryMarkers), false));
28809
+ }
28810
+ if (state_1.segmentsToDelete.length > 0) {
28625
28811
  var mutablePara = (0, mutate_1.mutateBlock)(block);
28626
28812
  var index = void 0;
28627
- while ((index = segmentsToDelete_1.pop()) !== undefined) {
28813
+ // Sort ascending so the pop()-based splice below always removes the highest index first,
28814
+ // keeping the remaining indices valid (boundary markers may sit before queued deletions).
28815
+ state_1.segmentsToDelete.sort(function (a, b) { return a - b; });
28816
+ while ((index = state_1.segmentsToDelete.pop()) !== undefined) {
28628
28817
  if (index >= 0) {
28629
28818
  mutablePara.segments.splice(index, 1);
28630
28819
  }
@@ -28673,14 +28862,23 @@ function findCell(table, cell) {
28673
28862
  : -1;
28674
28863
  return { row: row, col: col };
28675
28864
  }
28676
- function setSelectionToSegment(paragraph, segment, isInSelection, segmentsToDelete, start, end, i) {
28865
+ function setSelectionToSegment(paragraph, segment, isInSelection, state, start, end, i) {
28866
+ if (segment.segmentType != 'SelectionMarker' && isInSelection) {
28867
+ state.hasSelectedNonMarker = true;
28868
+ }
28677
28869
  switch (segment.segmentType) {
28678
28870
  case 'SelectionMarker':
28679
28871
  if (!isInSelection || (segment != start && segment != end)) {
28680
28872
  // Delete the selection marker when
28681
28873
  // 1. It is not in selection any more. Or
28682
28874
  // 2. It is in middle of selection, so no need to have it
28683
- segmentsToDelete.push(i);
28875
+ state.segmentsToDelete.push(i);
28876
+ }
28877
+ else {
28878
+ // It is a leading/trailing selection marker of a range selection. Keep it for now, but
28879
+ // remember it so it can be removed later if this same paragraph also contains a real
28880
+ // (non-marker) selected segment, in which case the marker is redundant.
28881
+ state.boundaryMarkers.push(i);
28684
28882
  }
28685
28883
  return isInSelection;
28686
28884
  case 'General':
@@ -29121,7 +29319,7 @@ var handleBlockGroupChildren = function (doc, parent, group, context) {
29121
29319
  (_a = context.domIndexer) === null || _a === void 0 ? void 0 : _a.onBlockEntity(childBlock, group);
29122
29320
  }
29123
29321
  });
29124
- cleanUpNodeStack(listFormat.nodeStack, context);
29322
+ cleanUpNodeStack(listFormat.nodeStack, context, parent);
29125
29323
  // Remove all rest node if any since they don't appear in content model
29126
29324
  (0, cleanUpRestNodes_1.cleanUpRestNodes)(refNode, context.rewriteFromModel);
29127
29325
  }
@@ -29130,7 +29328,7 @@ var handleBlockGroupChildren = function (doc, parent, group, context) {
29130
29328
  }
29131
29329
  };
29132
29330
  exports.handleBlockGroupChildren = handleBlockGroupChildren;
29133
- function cleanUpNodeStack(nodeStack, context) {
29331
+ function cleanUpNodeStack(nodeStack, context, leavingParent) {
29134
29332
  var _a, _b;
29135
29333
  if (nodeStack.length > 0) {
29136
29334
  // Clear list stack, only run to nodeStack[1] because nodeStack[0] is the parent node
@@ -29138,6 +29336,13 @@ function cleanUpNodeStack(nodeStack, context) {
29138
29336
  var node = (_b = (_a = nodeStack.pop()) === null || _a === void 0 ? void 0 : _a.refNode) !== null && _b !== void 0 ? _b : null;
29139
29337
  (0, cleanUpRestNodes_1.cleanUpRestNodes)(node, context.rewriteFromModel);
29140
29338
  }
29339
+ if (leavingParent && nodeStack[0].node == leavingParent) {
29340
+ // When leaving a parent node that is the same with the root of node stack
29341
+ // It means the whole list node stack is being invalidated, so we clear it
29342
+ while (nodeStack.length > 0) {
29343
+ nodeStack.pop();
29344
+ }
29345
+ }
29141
29346
  }
29142
29347
  }
29143
29348
 
@@ -29332,14 +29537,16 @@ var handleFormatContainer = function (doc, parent, container, context, refNode)
29332
29537
  (0, applyFormat_1.applyFormat)(containerNode_1, context.formatAppliers.segmentOnBlock, container.format, context);
29333
29538
  (0, applyFormat_1.applyFormat)(containerNode_1, context.formatAppliers.container, container.format, context);
29334
29539
  });
29335
- if (container.tagName == 'pre') {
29336
- (0, stackFormat_1.stackFormat)(context, PreChildFormat, function () {
29540
+ (0, stackFormat_1.stackFormat)(context, container.format.direction ? { direction: container.format.direction } : null, function () {
29541
+ if (container.tagName == 'pre') {
29542
+ (0, stackFormat_1.stackFormat)(context, PreChildFormat, function () {
29543
+ context.modelHandlers.blockGroupChildren(doc, containerNode_1, container, context);
29544
+ });
29545
+ }
29546
+ else {
29337
29547
  context.modelHandlers.blockGroupChildren(doc, containerNode_1, container, context);
29338
- });
29339
- }
29340
- else {
29341
- context.modelHandlers.blockGroupChildren(doc, containerNode_1, container, context);
29342
- }
29548
+ }
29549
+ });
29343
29550
  element = containerNode_1;
29344
29551
  }
29345
29552
  if (element) {
@@ -29629,7 +29836,9 @@ var handleListItem = function (doc, parent, listItem, context, refNode) {
29629
29836
  // Need to apply listItemElement formats after applying metadata since the list numbers value relies on the result of metadata handling
29630
29837
  (0, applyFormat_1.applyFormat)(li, context.formatAppliers.listItemElement, listItem.format, context);
29631
29838
  (0, stackFormat_1.stackFormat)(context, listItem.formatHolder.format, function () {
29632
- context.modelHandlers.blockGroupChildren(doc, li, listItem, context);
29839
+ (0, stackFormat_1.stackFormat)(context, listItem.format.direction ? { direction: listItem.format.direction } : null, function () {
29840
+ context.modelHandlers.blockGroupChildren(doc, li, listItem, context);
29841
+ });
29633
29842
  });
29634
29843
  }
29635
29844
  else {
@@ -30001,7 +30210,9 @@ var handleTable = function (doc, parent, table, context, refNode) {
30001
30210
  (0, applyFormat_1.applyFormat)(td_1, context.formatAppliers.tableCellBorder, cell.format, context);
30002
30211
  (0, applyFormat_1.applyFormat)(td_1, context.formatAppliers.dataset, cell.dataset, context);
30003
30212
  }
30004
- context.modelHandlers.blockGroupChildren(doc, td_1, cell, context);
30213
+ (0, stackFormat_1.stackFormat)(context, cell.format.direction ? { direction: cell.format.direction } : null, function () {
30214
+ context.modelHandlers.blockGroupChildren(doc, td_1, cell, context);
30215
+ });
30005
30216
  });
30006
30217
  (_f = context.onNodeCreated) === null || _f === void 0 ? void 0 : _f.call(context, cell, td_1);
30007
30218
  }
@@ -30520,40 +30731,17 @@ exports.MarkdownHeadings = {
30520
30731
  "use strict";
30521
30732
 
30522
30733
  Object.defineProperty(exports, "__esModule", ({ value: true }));
30523
- exports.convertContentModelToMarkdown = exports.convertMarkdownToContentModel = void 0;
30734
+ exports.MarkdownPastePlugin = exports.isPastedContentMarkdown = exports.isContentMarkdown = exports.convertContentModelToMarkdown = exports.convertMarkdownToContentModel = void 0;
30524
30735
  var convertMarkdownToContentModel_1 = __webpack_require__(/*! ./markdownToModel/convertMarkdownToContentModel */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts");
30525
30736
  Object.defineProperty(exports, "convertMarkdownToContentModel", ({ enumerable: true, get: function () { return convertMarkdownToContentModel_1.convertMarkdownToContentModel; } }));
30526
30737
  var convertContentModelToMarkdown_1 = __webpack_require__(/*! ./modelToMarkdown/convertContentModelToMarkdown */ "./packages/roosterjs-content-model-markdown/lib/modelToMarkdown/convertContentModelToMarkdown.ts");
30527
30738
  Object.defineProperty(exports, "convertContentModelToMarkdown", ({ enumerable: true, get: function () { return convertContentModelToMarkdown_1.convertContentModelToMarkdown; } }));
30528
-
30529
-
30530
- /***/ },
30531
-
30532
- /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyLink.ts"
30533
- /*!*********************************************************************************************!*\
30534
- !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyLink.ts ***!
30535
- \*********************************************************************************************/
30536
- (__unused_webpack_module, exports) {
30537
-
30538
- "use strict";
30539
-
30540
- Object.defineProperty(exports, "__esModule", ({ value: true }));
30541
- exports.applyLink = void 0;
30542
- /**
30543
- * @internal
30544
- */
30545
- function applyLink(textSegment, text, url) {
30546
- textSegment.text = text;
30547
- textSegment.link = {
30548
- dataset: {},
30549
- format: {
30550
- href: url,
30551
- underline: true,
30552
- },
30553
- };
30554
- return textSegment;
30555
- }
30556
- exports.applyLink = applyLink;
30739
+ var isContentMarkdown_1 = __webpack_require__(/*! ./publicApi/isContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts");
30740
+ Object.defineProperty(exports, "isContentMarkdown", ({ enumerable: true, get: function () { return isContentMarkdown_1.isContentMarkdown; } }));
30741
+ var isPastedContentMarkdown_1 = __webpack_require__(/*! ./publicApi/isPastedContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts");
30742
+ Object.defineProperty(exports, "isPastedContentMarkdown", ({ enumerable: true, get: function () { return isPastedContentMarkdown_1.isPastedContentMarkdown; } }));
30743
+ var MarkdownPastePlugin_1 = __webpack_require__(/*! ./plugins/MarkdownPastePlugin */ "./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts");
30744
+ Object.defineProperty(exports, "MarkdownPastePlugin", ({ enumerable: true, get: function () { return MarkdownPastePlugin_1.MarkdownPastePlugin; } }));
30557
30745
 
30558
30746
 
30559
30747
  /***/ },
@@ -30570,46 +30758,39 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
30570
30758
  exports.applySegmentFormatting = void 0;
30571
30759
  var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
30572
30760
  var adjustHeading_1 = __webpack_require__(/*! ../utils/adjustHeading */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/adjustHeading.ts");
30573
- var applyLink_1 = __webpack_require__(/*! ./applyLink */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyLink.ts");
30574
- var applyTextFormatting_1 = __webpack_require__(/*! ./applyTextFormatting */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyTextFormatting.ts");
30575
30761
  var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
30576
- var createImageSegment_1 = __webpack_require__(/*! ../creators/createImageSegment */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createImageSegment.ts");
30577
- var splitParagraphSegments_1 = __webpack_require__(/*! ../utils/splitParagraphSegments */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/splitParagraphSegments.ts");
30762
+ var parseInlineSegments_1 = __webpack_require__(/*! ../utils/parseInlineSegments */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/parseInlineSegments.ts");
30578
30763
  /**
30579
30764
  * @internal
30580
30765
  */
30581
30766
  function applySegmentFormatting(text, paragraph, decorator) {
30582
- var e_1, _a, _b;
30767
+ var e_1, _a;
30583
30768
  if (text.length === 0) {
30584
30769
  var br = (0, roosterjs_content_model_dom_1.createBr)();
30585
30770
  paragraph.segments.push(br);
30586
30771
  }
30587
30772
  else {
30588
- var textSegments = (0, splitParagraphSegments_1.splitParagraphSegments)(text);
30773
+ var segments = [];
30774
+ (0, parseInlineSegments_1.parseInlineSegments)(text, segments);
30775
+ // Apply heading adjustment to the first text-bearing segment, if any.
30776
+ var headingAdjusted = false;
30589
30777
  try {
30590
- for (var textSegments_1 = (0, tslib_1.__values)(textSegments), textSegments_1_1 = textSegments_1.next(); !textSegments_1_1.done; textSegments_1_1 = textSegments_1.next()) {
30591
- var segment = textSegments_1_1.value;
30592
- var formattedSegment = (0, roosterjs_content_model_dom_1.createText)(segment.text);
30593
- if (segment.type === 'image') {
30594
- var image = (0, createImageSegment_1.createImageSegment)(segment.text, segment.url);
30595
- paragraph.segments.push(image);
30596
- }
30597
- else {
30598
- if (segment.type === 'link') {
30599
- (0, applyLink_1.applyLink)(formattedSegment, segment.text, segment.url);
30600
- }
30601
- var segmentWithAdjustedHeading = (0, adjustHeading_1.adjustHeading)(formattedSegment, decorator);
30602
- if (segmentWithAdjustedHeading) {
30603
- var formattedSegments = (0, applyTextFormatting_1.applyTextFormatting)(formattedSegment);
30604
- (_b = paragraph.segments).push.apply(_b, (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(formattedSegments), false));
30778
+ for (var segments_1 = (0, tslib_1.__values)(segments), segments_1_1 = segments_1.next(); !segments_1_1.done; segments_1_1 = segments_1.next()) {
30779
+ var segment = segments_1_1.value;
30780
+ if (!headingAdjusted && segment.segmentType === 'Text') {
30781
+ var adjusted = (0, adjustHeading_1.adjustHeading)(segment, decorator);
30782
+ headingAdjusted = true;
30783
+ if (!adjusted) {
30784
+ continue;
30605
30785
  }
30606
30786
  }
30787
+ paragraph.segments.push(segment);
30607
30788
  }
30608
30789
  }
30609
30790
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
30610
30791
  finally {
30611
30792
  try {
30612
- if (textSegments_1_1 && !textSegments_1_1.done && (_a = textSegments_1.return)) _a.call(textSegments_1);
30793
+ if (segments_1_1 && !segments_1_1.done && (_a = segments_1.return)) _a.call(segments_1);
30613
30794
  }
30614
30795
  finally { if (e_1) throw e_1.error; }
30615
30796
  }
@@ -30621,199 +30802,57 @@ exports.applySegmentFormatting = applySegmentFormatting;
30621
30802
 
30622
30803
  /***/ },
30623
30804
 
30624
- /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyTextFormatting.ts"
30625
- /*!*******************************************************************************************************!*\
30626
- !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyTextFormatting.ts ***!
30627
- \*******************************************************************************************************/
30805
+ /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts"
30806
+ /*!********************************************************************************************************!*\
30807
+ !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts ***!
30808
+ \********************************************************************************************************/
30628
30809
  (__unused_webpack_module, exports, __webpack_require__) {
30629
30810
 
30630
30811
  "use strict";
30631
30812
 
30632
30813
  Object.defineProperty(exports, "__esModule", ({ value: true }));
30633
- exports.applyTextFormatting = void 0;
30634
- var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
30635
- var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
30814
+ exports.convertMarkdownToContentModel = void 0;
30815
+ var markdownProcessor_1 = __webpack_require__(/*! ./processor/markdownProcessor */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/processor/markdownProcessor.ts");
30816
+ function convertMarkdownToContentModel(text, splitLinesPatternOrOptions) {
30817
+ var _a;
30818
+ var options = (_a = (typeof splitLinesPatternOrOptions === 'string'
30819
+ ? {
30820
+ splitLinesPattern: splitLinesPatternOrOptions,
30821
+ }
30822
+ : splitLinesPatternOrOptions)) !== null && _a !== void 0 ? _a : {};
30823
+ return (0, markdownProcessor_1.markdownProcessor)(text, options);
30824
+ }
30825
+ exports.convertMarkdownToContentModel = convertMarkdownToContentModel;
30826
+
30827
+
30828
+ /***/ },
30829
+
30830
+ /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createBlockGroupFromMarkdown.ts"
30831
+ /*!****************************************************************************************************************!*\
30832
+ !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createBlockGroupFromMarkdown.ts ***!
30833
+ \****************************************************************************************************************/
30834
+ (__unused_webpack_module, exports, __webpack_require__) {
30835
+
30836
+ "use strict";
30837
+
30838
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
30839
+ exports.createBlockGroupFromMarkdown = void 0;
30840
+ var createBlockQuoteFromMarkdown_1 = __webpack_require__(/*! ./createBlockQuoteFromMarkdown */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createBlockQuoteFromMarkdown.ts");
30841
+ var createListFromMarkdown_1 = __webpack_require__(/*! ./createListFromMarkdown */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createListFromMarkdown.ts");
30842
+ var MarkdownBlockGroupType = {
30843
+ unordered_list: 'ListItem',
30844
+ ordered_list: 'ListItem',
30845
+ blockquote: 'FormatContainer',
30846
+ };
30636
30847
  /**
30637
30848
  * @internal
30638
30849
  */
30639
- function applyTextFormatting(textSegment) {
30640
- var text = textSegment.text;
30641
- // Quick check: if the text contains only formatting markers, return original
30642
- if (isOnlyFormattingMarkers(text)) {
30643
- return [textSegment];
30644
- }
30645
- var textSegments = [];
30646
- var currentState = { bold: false, italic: false, strikethrough: false };
30647
- var currentText = '';
30648
- var i = 0;
30649
- while (i < text.length) {
30650
- var marker = parseMarkerAt(text, i);
30651
- if (marker) {
30652
- // Check if this marker should be treated as formatting or as literal text
30653
- if (shouldToggleFormatting(text, i, marker, currentState)) {
30654
- // If we have accumulated text, create a segment for it
30655
- if (currentText.length > 0) {
30656
- textSegments.push(createFormattedSegment(currentText, textSegment.format, currentState, textSegment.link));
30657
- currentText = '';
30658
- }
30659
- // Toggle the formatting state
30660
- toggleFormatting(currentState, marker.type);
30661
- // Skip the marker characters
30662
- i += marker.length;
30663
- }
30664
- else {
30665
- // Treat as regular text if marker is not valid in this context
30666
- currentText += text[i];
30667
- i++;
30668
- }
30669
- }
30670
- else {
30671
- // Regular character, add to current text
30672
- currentText += text[i];
30673
- i++;
30674
- }
30675
- }
30676
- // Add any remaining text as a final segment
30677
- if (currentText.length > 0) {
30678
- textSegments.push(createFormattedSegment(currentText, textSegment.format, currentState, textSegment.link));
30679
- }
30680
- // If no meaningful formatting was applied, return the original segment
30681
- if (textSegments.length === 0 ||
30682
- (textSegments.length === 1 && textSegments[0].text === textSegment.text)) {
30683
- return [textSegment];
30684
- }
30685
- return textSegments;
30686
- }
30687
- exports.applyTextFormatting = applyTextFormatting;
30688
- function isOnlyFormattingMarkers(text) {
30689
- // Remove all potential formatting markers and see if anything remains
30690
- var remaining = text;
30691
- remaining = remaining.replace(/\*\*/g, ''); // Remove **
30692
- remaining = remaining.replace(/~~/g, ''); // Remove ~~
30693
- remaining = remaining.replace(/\*/g, ''); // Remove *
30694
- // If nothing remains after removing all markers, it was only markers
30695
- return remaining.length === 0;
30696
- }
30697
- function parseMarkerAt(text, index) {
30698
- var remaining = text.substring(index);
30699
- if (remaining.startsWith('~~')) {
30700
- return { type: 'strikethrough', length: 2 };
30701
- }
30702
- if (remaining.startsWith('**')) {
30703
- return { type: 'bold', length: 2 };
30850
+ function createBlockGroupFromMarkdown(text, patternName, options, group) {
30851
+ if (MarkdownBlockGroupType[patternName] === 'ListItem') {
30852
+ return (0, createListFromMarkdown_1.createListFromMarkdown)(text, patternName === 'ordered_list' ? 'OL' : 'UL', options);
30704
30853
  }
30705
- if (remaining.startsWith('*')) {
30706
- return { type: 'italic', length: 1 };
30707
- }
30708
- return null;
30709
- }
30710
- function shouldToggleFormatting(text, index, marker, currentState) {
30711
- var nextChar = index + marker.length < text.length ? text.charAt(index + marker.length) : '';
30712
- var isCurrentlyActive = getCurrentFormatState(currentState, marker.type);
30713
- if (isCurrentlyActive) {
30714
- // We're currently in this format, so any marker can close it
30715
- return true;
30716
- }
30717
- else {
30718
- // We're not in this format, so this marker would open it
30719
- // Opening markers must be followed by non-whitespace
30720
- return nextChar.length > 0 && !isWhitespace(nextChar);
30721
- }
30722
- }
30723
- function isWhitespace(char) {
30724
- return /\s/.test(char);
30725
- }
30726
- function toggleFormatting(state, type) {
30727
- switch (type) {
30728
- case 'bold':
30729
- state.bold = !state.bold;
30730
- break;
30731
- case 'italic':
30732
- state.italic = !state.italic;
30733
- break;
30734
- case 'strikethrough':
30735
- state.strikethrough = !state.strikethrough;
30736
- break;
30737
- }
30738
- }
30739
- function getCurrentFormatState(state, type) {
30740
- switch (type) {
30741
- case 'bold':
30742
- return state.bold;
30743
- case 'italic':
30744
- return state.italic;
30745
- case 'strikethrough':
30746
- return state.strikethrough;
30747
- }
30748
- }
30749
- function createFormattedSegment(text, baseFormat, state, link) {
30750
- var format = (0, tslib_1.__assign)({}, baseFormat);
30751
- if (state.bold) {
30752
- format.fontWeight = 'bold';
30753
- }
30754
- if (state.italic) {
30755
- format.italic = true;
30756
- }
30757
- if (state.strikethrough) {
30758
- format.strikethrough = true;
30759
- }
30760
- return (0, roosterjs_content_model_dom_1.createText)(text, format, link);
30761
- }
30762
-
30763
-
30764
- /***/ },
30765
-
30766
- /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts"
30767
- /*!********************************************************************************************************!*\
30768
- !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts ***!
30769
- \********************************************************************************************************/
30770
- (__unused_webpack_module, exports, __webpack_require__) {
30771
-
30772
- "use strict";
30773
-
30774
- Object.defineProperty(exports, "__esModule", ({ value: true }));
30775
- exports.convertMarkdownToContentModel = void 0;
30776
- var markdownProcessor_1 = __webpack_require__(/*! ./processor/markdownProcessor */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/processor/markdownProcessor.ts");
30777
- function convertMarkdownToContentModel(text, splitLinesPatternOrOptions) {
30778
- var _a;
30779
- var options = (_a = (typeof splitLinesPatternOrOptions === 'string'
30780
- ? {
30781
- splitLinesPattern: splitLinesPatternOrOptions,
30782
- }
30783
- : splitLinesPatternOrOptions)) !== null && _a !== void 0 ? _a : {};
30784
- return (0, markdownProcessor_1.markdownProcessor)(text, options);
30785
- }
30786
- exports.convertMarkdownToContentModel = convertMarkdownToContentModel;
30787
-
30788
-
30789
- /***/ },
30790
-
30791
- /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createBlockGroupFromMarkdown.ts"
30792
- /*!****************************************************************************************************************!*\
30793
- !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createBlockGroupFromMarkdown.ts ***!
30794
- \****************************************************************************************************************/
30795
- (__unused_webpack_module, exports, __webpack_require__) {
30796
-
30797
- "use strict";
30798
-
30799
- Object.defineProperty(exports, "__esModule", ({ value: true }));
30800
- exports.createBlockGroupFromMarkdown = void 0;
30801
- var createBlockQuoteFromMarkdown_1 = __webpack_require__(/*! ./createBlockQuoteFromMarkdown */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createBlockQuoteFromMarkdown.ts");
30802
- var createListFromMarkdown_1 = __webpack_require__(/*! ./createListFromMarkdown */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createListFromMarkdown.ts");
30803
- var MarkdownBlockGroupType = {
30804
- unordered_list: 'ListItem',
30805
- ordered_list: 'ListItem',
30806
- blockquote: 'FormatContainer',
30807
- };
30808
- /**
30809
- * @internal
30810
- */
30811
- function createBlockGroupFromMarkdown(text, patternName, options, group) {
30812
- if (MarkdownBlockGroupType[patternName] === 'ListItem') {
30813
- return (0, createListFromMarkdown_1.createListFromMarkdown)(text, patternName === 'ordered_list' ? 'OL' : 'UL', options);
30814
- }
30815
- else {
30816
- return (0, createBlockQuoteFromMarkdown_1.createBlockQuoteFromMarkdown)(text, options, group);
30854
+ else {
30855
+ return (0, createBlockQuoteFromMarkdown_1.createBlockQuoteFromMarkdown)(text, options, group);
30817
30856
  }
30818
30857
  }
30819
30858
  exports.createBlockGroupFromMarkdown = createBlockGroupFromMarkdown;
@@ -31358,29 +31397,154 @@ exports.isMarkdownTable = isMarkdownTable;
31358
31397
 
31359
31398
  /***/ },
31360
31399
 
31361
- /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/splitParagraphSegments.ts"
31362
- /*!*******************************************************************************************************!*\
31363
- !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/splitParagraphSegments.ts ***!
31364
- \*******************************************************************************************************/
31365
- (__unused_webpack_module, exports) {
31400
+ /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/parseInlineSegments.ts"
31401
+ /*!****************************************************************************************************!*\
31402
+ !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/parseInlineSegments.ts ***!
31403
+ \****************************************************************************************************/
31404
+ (__unused_webpack_module, exports, __webpack_require__) {
31366
31405
 
31367
31406
  "use strict";
31368
31407
 
31369
31408
  Object.defineProperty(exports, "__esModule", ({ value: true }));
31370
- exports.splitParagraphSegments = void 0;
31371
- // Matches markdown links and images in a string.
31372
- // Group 1 (full link): [text](url) e.g. [Click here](https://example.com)
31373
- // Group 2: link text e.g. "Click here"
31374
- // Group 3: link url e.g. "https://example.com"
31375
- // Group 4 (full image): ![alt](url) e.g. ![Logo](https://example.com/logo.png)
31376
- // Group 5: alt text e.g. "Logo"
31377
- // Group 6: image url e.g. "https://example.com/logo.png"
31378
- var linkRegex = /(\[([^\[]+)\]\(([^\)]+)\))|(\!\[([^\[]+)\]\(([^\)]+)\))/g;
31379
- var isValidUrl = function (url) {
31409
+ exports.parseInlineSegments = void 0;
31410
+ var createImageSegment_1 = __webpack_require__(/*! ../creators/createImageSegment */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createImageSegment.ts");
31411
+ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
31412
+ // Matches a markdown link [text](url) anchored at the start of the input.
31413
+ var linkPattern = /^\[([^\[\]]+)\]\(([^\)]+)\)/;
31414
+ // Matches a markdown image ![alt](url) anchored at the start of the input.
31415
+ var imagePattern = /^!\[([^\[\]]+)\]\(([^\)]+)\)/;
31416
+ /**
31417
+ * @internal
31418
+ * Parse a markdown inline string into Content Model segments. Supports bold/italic/
31419
+ * strikethrough markers, links, and images, and keeps formatting state active across
31420
+ * link boundaries (e.g. **[link](url)**).
31421
+ */
31422
+ function parseInlineSegments(text, segments, state, link) {
31423
+ if (state === void 0) { state = { bold: false, italic: false, strikethrough: false }; }
31424
+ var buffer = '';
31425
+ var i = 0;
31426
+ var flushBuffer = function () {
31427
+ if (buffer.length > 0) {
31428
+ segments.push(createFormattedSegment(buffer, state, link));
31429
+ buffer = '';
31430
+ }
31431
+ };
31432
+ while (i < text.length) {
31433
+ var remaining = text.substring(i);
31434
+ // Escaped character: a backslash followed by an ASCII punctuation character emits
31435
+ // that character literally (e.g. "\*" -> "*") and is never treated as a marker.
31436
+ if (text[i] === '\\' && i + 1 < text.length && isEscapable(text[i + 1])) {
31437
+ buffer += text[i + 1];
31438
+ i += 2;
31439
+ continue;
31440
+ }
31441
+ // Image: ![alt](url)
31442
+ var imgMatch = imagePattern.exec(remaining);
31443
+ if (imgMatch && isValidUrl(imgMatch[2])) {
31444
+ flushBuffer();
31445
+ segments.push((0, createImageSegment_1.createImageSegment)(imgMatch[1], imgMatch[2]));
31446
+ i += imgMatch[0].length;
31447
+ continue;
31448
+ }
31449
+ // Link: [text](url) — keep outer formatting state active inside the link
31450
+ var linkMatch = linkPattern.exec(remaining);
31451
+ if (linkMatch && isValidUrl(linkMatch[2])) {
31452
+ flushBuffer();
31453
+ var innerLink = {
31454
+ dataset: {},
31455
+ format: { href: linkMatch[2], underline: true },
31456
+ };
31457
+ parseInlineSegments(linkMatch[1], segments, state, innerLink);
31458
+ i += linkMatch[0].length;
31459
+ continue;
31460
+ }
31461
+ // Formatting marker
31462
+ var marker = parseMarkerAt(text, i);
31463
+ if (marker && shouldToggleFormatting(text, i, marker, state)) {
31464
+ flushBuffer();
31465
+ toggleFormatting(state, marker.type);
31466
+ i += marker.length;
31467
+ continue;
31468
+ }
31469
+ buffer += text[i];
31470
+ i++;
31471
+ }
31472
+ flushBuffer();
31473
+ }
31474
+ exports.parseInlineSegments = parseInlineSegments;
31475
+ function parseMarkerAt(text, index) {
31476
+ var remaining = text.substring(index);
31477
+ if (remaining.startsWith('~~')) {
31478
+ return { type: 'strikethrough', length: 2 };
31479
+ }
31480
+ if (remaining.startsWith('**')) {
31481
+ return { type: 'bold', length: 2 };
31482
+ }
31483
+ if (remaining.startsWith('*')) {
31484
+ return { type: 'italic', length: 1 };
31485
+ }
31486
+ return null;
31487
+ }
31488
+ function shouldToggleFormatting(text, index, marker, currentState) {
31489
+ var isCurrentlyActive = getCurrentFormatState(currentState, marker.type);
31490
+ if (isCurrentlyActive) {
31491
+ return true;
31492
+ }
31493
+ // Opening marker must be followed by a non-whitespace character.
31494
+ var nextIndex = index + marker.length;
31495
+ var nextChar = nextIndex < text.length ? text.charAt(nextIndex) : '';
31496
+ if (nextChar.length === 0 || isWhitespace(nextChar)) {
31497
+ return false;
31498
+ }
31499
+ return true;
31500
+ }
31501
+ function isWhitespace(char) {
31502
+ return /\s/.test(char);
31503
+ }
31504
+ function isEscapable(char) {
31505
+ // Per CommonMark, any ASCII punctuation character may be backslash-escaped.
31506
+ return /[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/.test(char);
31507
+ }
31508
+ function toggleFormatting(state, type) {
31509
+ switch (type) {
31510
+ case 'bold':
31511
+ state.bold = !state.bold;
31512
+ break;
31513
+ case 'italic':
31514
+ state.italic = !state.italic;
31515
+ break;
31516
+ case 'strikethrough':
31517
+ state.strikethrough = !state.strikethrough;
31518
+ break;
31519
+ }
31520
+ }
31521
+ function getCurrentFormatState(state, type) {
31522
+ switch (type) {
31523
+ case 'bold':
31524
+ return state.bold;
31525
+ case 'italic':
31526
+ return state.italic;
31527
+ case 'strikethrough':
31528
+ return state.strikethrough;
31529
+ }
31530
+ }
31531
+ function createFormattedSegment(text, state, link) {
31532
+ var format = {};
31533
+ if (state.bold) {
31534
+ format.fontWeight = 'bold';
31535
+ }
31536
+ if (state.italic) {
31537
+ format.italic = true;
31538
+ }
31539
+ if (state.strikethrough) {
31540
+ format.strikethrough = true;
31541
+ }
31542
+ return (0, roosterjs_content_model_dom_1.createText)(text, format, link);
31543
+ }
31544
+ function isValidUrl(url) {
31380
31545
  if (!url) {
31381
31546
  return false;
31382
31547
  }
31383
- // Accept common non-http schemes and relative paths
31384
31548
  if (url.startsWith('data:') ||
31385
31549
  url.startsWith('blob:') ||
31386
31550
  url.startsWith('/') ||
@@ -31395,51 +31559,7 @@ var isValidUrl = function (url) {
31395
31559
  catch (_) {
31396
31560
  return false;
31397
31561
  }
31398
- };
31399
- function pushText(result, text) {
31400
- var last = result[result.length - 1];
31401
- if (last && last.type === 'text') {
31402
- last.text += text;
31403
- }
31404
- else {
31405
- result.push({ type: 'text', text: text, url: '' });
31406
- }
31407
31562
  }
31408
- /**
31409
- * @internal
31410
- */
31411
- function splitParagraphSegments(text) {
31412
- var result = [];
31413
- var lastIndex = 0;
31414
- var match = null;
31415
- while ((match = linkRegex.exec(text)) !== null) {
31416
- if (match.index > lastIndex) {
31417
- pushText(result, text.slice(lastIndex, match.index));
31418
- }
31419
- if (match[2] && match[3]) {
31420
- if (isValidUrl(match[3])) {
31421
- result.push({ type: 'link', text: match[2], url: match[3] });
31422
- }
31423
- else {
31424
- pushText(result, match[0]);
31425
- }
31426
- }
31427
- else if (match[5] && match[6]) {
31428
- if (isValidUrl(match[6])) {
31429
- result.push({ type: 'image', text: match[5], url: match[6] });
31430
- }
31431
- else {
31432
- pushText(result, match[0]);
31433
- }
31434
- }
31435
- lastIndex = linkRegex.lastIndex;
31436
- }
31437
- if (lastIndex < text.length) {
31438
- pushText(result, text.slice(lastIndex));
31439
- }
31440
- return result;
31441
- }
31442
- exports.splitParagraphSegments = splitParagraphSegments;
31443
31563
 
31444
31564
 
31445
31565
  /***/ },
@@ -31700,20 +31820,30 @@ function createMarkdownParagraph(paragraph, context) {
31700
31820
  }
31701
31821
  exports.createMarkdownParagraph = createMarkdownParagraph;
31702
31822
  function textProcessor(text) {
31703
- var markdownString = text.text;
31704
- if (text.link) {
31705
- markdownString = "[" + text.text + "](" + text.link.format.href + ")";
31706
- }
31707
- if (text.format.fontWeight == 'bold') {
31708
- markdownString = "**" + markdownString + "**";
31823
+ var _a = text.format, fontWeight = _a.fontWeight, italic = _a.italic, strikethrough = _a.strikethrough;
31824
+ var hasInlineFormat = fontWeight == 'bold' || italic || strikethrough;
31825
+ if (!hasInlineFormat) {
31826
+ return text.link ? "[" + text.text + "](" + text.link.format.href + ")" : text.text;
31827
+ }
31828
+ // Move leading/trailing whitespace outside the markers so the emitted
31829
+ // markdown is valid (CommonMark requires emphasis markers to hug
31830
+ // non-whitespace), e.g. "world " with <b> => " " + "**world**".
31831
+ var match = /^(\s*)([\s\S]*?)(\s*)$/.exec(text.text);
31832
+ var _b = (0, tslib_1.__read)(match ? match : ['', '', text.text, ''], 4), leading = _b[1], core = _b[2], trailing = _b[3];
31833
+ if (!core) {
31834
+ return text.text;
31835
+ }
31836
+ var inner = text.link ? "[" + core + "](" + text.link.format.href + ")" : core;
31837
+ if (fontWeight == 'bold') {
31838
+ inner = "**" + inner + "**";
31839
+ }
31840
+ if (strikethrough) {
31841
+ inner = "~~" + inner + "~~";
31709
31842
  }
31710
- if (text.format.strikethrough) {
31711
- markdownString = "~~" + markdownString + "~~";
31712
- }
31713
- if (text.format.italic) {
31714
- markdownString = "*" + markdownString + "*";
31843
+ if (italic) {
31844
+ inner = "*" + inner + "*";
31715
31845
  }
31716
- return markdownString;
31846
+ return "" + leading + inner + trailing;
31717
31847
  }
31718
31848
 
31719
31849
 
@@ -31878,6 +32008,252 @@ function modelProcessor(model, newLine) {
31878
32008
  exports.modelProcessor = modelProcessor;
31879
32009
 
31880
32010
 
32011
+ /***/ },
32012
+
32013
+ /***/ "./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts"
32014
+ /*!**************************************************************************************!*\
32015
+ !*** ./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts ***!
32016
+ \**************************************************************************************/
32017
+ (__unused_webpack_module, exports, __webpack_require__) {
32018
+
32019
+ "use strict";
32020
+
32021
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
32022
+ exports.MarkdownPastePlugin = void 0;
32023
+ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
32024
+ var convertMarkdownToContentModel_1 = __webpack_require__(/*! ../markdownToModel/convertMarkdownToContentModel */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts");
32025
+ var isPastedContentMarkdown_1 = __webpack_require__(/*! ../publicApi/isPastedContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts");
32026
+ var DefaultOptions = {
32027
+ autoConversion: false,
32028
+ };
32029
+ /**
32030
+ * Markdown paste plugin. Handles the BeforePaste event and, when the pasted content
32031
+ * can be interpreted as markdown, converts the plain text into a Content Model and
32032
+ * pastes it as rich markdown content instead of the original clipboard HTML.
32033
+ */
32034
+ var MarkdownPastePlugin = /** @class */ (function () {
32035
+ /**
32036
+ * Construct a new instance of MarkdownPastePlugin
32037
+ * @param options Options to control the markdown paste behavior
32038
+ */
32039
+ function MarkdownPastePlugin(options) {
32040
+ this.editor = null;
32041
+ this.options = options !== null && options !== void 0 ? options : DefaultOptions;
32042
+ }
32043
+ /**
32044
+ * Get name of this plugin
32045
+ */
32046
+ MarkdownPastePlugin.prototype.getName = function () {
32047
+ return 'MarkdownPaste';
32048
+ };
32049
+ /**
32050
+ * The first method that editor will call to a plugin when editor is initializing.
32051
+ * It will pass in the editor instance, plugin should take this chance to save the
32052
+ * editor reference so that it can call to any editor method or format API later.
32053
+ * @param editor The editor object
32054
+ */
32055
+ MarkdownPastePlugin.prototype.initialize = function (editor) {
32056
+ this.editor = editor;
32057
+ };
32058
+ /**
32059
+ * The last method that editor will call to a plugin before it is disposed.
32060
+ * Plugin can take this chance to clear the reference to editor. After this method is
32061
+ * called, plugin should not call to any editor method since it will result in error.
32062
+ */
32063
+ MarkdownPastePlugin.prototype.dispose = function () {
32064
+ this.editor = null;
32065
+ };
32066
+ /**
32067
+ * Core method for a plugin. Once an event happens in editor, editor will call this
32068
+ * method of each plugin to handle the event as long as the event is not handled
32069
+ * exclusively by another plugin.
32070
+ * @param event The event to handle:
32071
+ */
32072
+ MarkdownPastePlugin.prototype.onPluginEvent = function (event) {
32073
+ if (!this.editor || event.eventType != 'beforePaste') {
32074
+ return;
32075
+ }
32076
+ var shouldConvert = event.pasteType === 'asMarkdown' || this.options.autoConversion;
32077
+ if (shouldConvert && (0, isPastedContentMarkdown_1.isPastedContentMarkdown)(this.editor, event.clipboardData)) {
32078
+ convertPastedTextToMarkdown(this.editor, event.fragment, event.clipboardData.text);
32079
+ }
32080
+ };
32081
+ return MarkdownPastePlugin;
32082
+ }());
32083
+ exports.MarkdownPastePlugin = MarkdownPastePlugin;
32084
+ function convertPastedTextToMarkdown(editor, fragment, text) {
32085
+ var model = (0, convertMarkdownToContentModel_1.convertMarkdownToContentModel)(text, {
32086
+ emptyLine: 'merge',
32087
+ });
32088
+ while (fragment.firstChild) {
32089
+ fragment.removeChild(fragment.firstChild);
32090
+ }
32091
+ (0, roosterjs_content_model_dom_1.contentModelToDom)(editor.getDocument(), fragment, model, (0, roosterjs_content_model_dom_1.createModelToDomContext)());
32092
+ }
32093
+
32094
+
32095
+ /***/ },
32096
+
32097
+ /***/ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts"
32098
+ /*!**************************************************************************************!*\
32099
+ !*** ./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts ***!
32100
+ \**************************************************************************************/
32101
+ (__unused_webpack_module, exports, __webpack_require__) {
32102
+
32103
+ "use strict";
32104
+
32105
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
32106
+ exports.isContentMarkdown = void 0;
32107
+ var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
32108
+ // Block-level markdown patterns. A line that matches any of these is considered markdown.
32109
+ var BlockPatterns = [
32110
+ /^#{1,6}\s.+/,
32111
+ /^\s*>\s.+/,
32112
+ /^\s*[\*\-\+]\s.+/,
32113
+ /^\s*\d+\.\s.+/,
32114
+ /^---+$/,
32115
+ /^\s*\|.*\|\s*$/, // table row: "| a | b |"
32116
+ ];
32117
+ // Inline markdown patterns. The text contains markdown if any of these match anywhere.
32118
+ var InlinePatterns = [
32119
+ /!\[[^\[\]]+\]\([^\)\s]+\)/,
32120
+ /\[[^\[\]]+\]\([^\)\s]+\)/,
32121
+ /\*\*[^\s*][^*]*\*\*/,
32122
+ /(^|[^*])\*[^\s*][^*]*\*([^*]|$)/,
32123
+ /~~[^\s~][^~]*~~/, // strikethrough: ~~text~~
32124
+ ];
32125
+ /**
32126
+ * Detect whether the given plain text contains any markdown markup.
32127
+ * Recognizes block-level patterns (headings, blockquotes, lists, horizontal rules, tables)
32128
+ * and inline patterns (bold, italic, strikethrough, links, images).
32129
+ * @param text The plain text to check.
32130
+ * @returns True if the text contains any markdown markup, false otherwise.
32131
+ */
32132
+ function isContentMarkdown(text) {
32133
+ var e_1, _a, e_2, _b, e_3, _c;
32134
+ if (!text || !text.trim()) {
32135
+ return false;
32136
+ }
32137
+ var lines = text.split(/\r\n|\r|\n/);
32138
+ try {
32139
+ for (var lines_1 = (0, tslib_1.__values)(lines), lines_1_1 = lines_1.next(); !lines_1_1.done; lines_1_1 = lines_1.next()) {
32140
+ var line = lines_1_1.value;
32141
+ try {
32142
+ for (var BlockPatterns_1 = (e_2 = void 0, (0, tslib_1.__values)(BlockPatterns)), BlockPatterns_1_1 = BlockPatterns_1.next(); !BlockPatterns_1_1.done; BlockPatterns_1_1 = BlockPatterns_1.next()) {
32143
+ var pattern = BlockPatterns_1_1.value;
32144
+ if (pattern.test(line)) {
32145
+ return true;
32146
+ }
32147
+ }
32148
+ }
32149
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
32150
+ finally {
32151
+ try {
32152
+ if (BlockPatterns_1_1 && !BlockPatterns_1_1.done && (_b = BlockPatterns_1.return)) _b.call(BlockPatterns_1);
32153
+ }
32154
+ finally { if (e_2) throw e_2.error; }
32155
+ }
32156
+ }
32157
+ }
32158
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
32159
+ finally {
32160
+ try {
32161
+ if (lines_1_1 && !lines_1_1.done && (_a = lines_1.return)) _a.call(lines_1);
32162
+ }
32163
+ finally { if (e_1) throw e_1.error; }
32164
+ }
32165
+ try {
32166
+ for (var InlinePatterns_1 = (0, tslib_1.__values)(InlinePatterns), InlinePatterns_1_1 = InlinePatterns_1.next(); !InlinePatterns_1_1.done; InlinePatterns_1_1 = InlinePatterns_1.next()) {
32167
+ var pattern = InlinePatterns_1_1.value;
32168
+ if (pattern.test(text)) {
32169
+ return true;
32170
+ }
32171
+ }
32172
+ }
32173
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
32174
+ finally {
32175
+ try {
32176
+ if (InlinePatterns_1_1 && !InlinePatterns_1_1.done && (_c = InlinePatterns_1.return)) _c.call(InlinePatterns_1);
32177
+ }
32178
+ finally { if (e_3) throw e_3.error; }
32179
+ }
32180
+ return false;
32181
+ }
32182
+ exports.isContentMarkdown = isContentMarkdown;
32183
+
32184
+
32185
+ /***/ },
32186
+
32187
+ /***/ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts"
32188
+ /*!********************************************************************************************!*\
32189
+ !*** ./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts ***!
32190
+ \********************************************************************************************/
32191
+ (__unused_webpack_module, exports, __webpack_require__) {
32192
+
32193
+ "use strict";
32194
+
32195
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
32196
+ exports.isPastedContentMarkdown = void 0;
32197
+ var isContentMarkdown_1 = __webpack_require__(/*! ./isContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts");
32198
+ // Tags that are considered "thin wrappers", which only add structure (such as line breaks)
32199
+ // around the plain text without applying any real formatting to its content.
32200
+ var ThinWrapperTags = new Set(['DIV', 'P', 'BR', 'SPAN']);
32201
+ var AllowedAttributes = new Set(['class', 'style']);
32202
+ /**
32203
+ * Detect whether the given clipboard content can be interpreted as markdown.
32204
+ * @param editor The editor instance.
32205
+ * @param clipboardData The clipboard data to check.
32206
+ * @returns True if the content can be interpreted as markdown, false otherwise.
32207
+ */
32208
+ function isPastedContentMarkdown(editor, clipboardData) {
32209
+ var text = clipboardData.text, rawHtml = clipboardData.rawHtml;
32210
+ if (!text || !text.trim()) {
32211
+ return false;
32212
+ }
32213
+ if ((0, isContentMarkdown_1.isContentMarkdown)(text)) {
32214
+ if (!rawHtml) {
32215
+ return true;
32216
+ }
32217
+ var doc = editor.getDocument();
32218
+ var trustedHTMLHandler = editor.getDOMCreator();
32219
+ var fragment = parseHtmlToFragment(rawHtml, doc, trustedHTMLHandler);
32220
+ return isThinWrapperOfPlainText(fragment, text);
32221
+ }
32222
+ return false;
32223
+ }
32224
+ exports.isPastedContentMarkdown = isPastedContentMarkdown;
32225
+ function isThinWrapperOfPlainText(fragment, text) {
32226
+ var elements = fragment.querySelectorAll('*');
32227
+ for (var i = 0; i < elements.length; i++) {
32228
+ var element = elements[i];
32229
+ if (!ThinWrapperTags.has(element.tagName)) {
32230
+ return false;
32231
+ }
32232
+ for (var j = 0; j < element.attributes.length; j++) {
32233
+ var attr = element.attributes[j];
32234
+ if (!AllowedAttributes.has(attr.name) && !attr.name.startsWith('data-')) {
32235
+ return false;
32236
+ }
32237
+ }
32238
+ }
32239
+ return removeWhitespace(fragment.textContent || '') === removeWhitespace(text);
32240
+ }
32241
+ function removeWhitespace(text) {
32242
+ return text.replace(/\s/g, '');
32243
+ }
32244
+ function parseHtmlToFragment(html, doc, trustedHTMLHandler) {
32245
+ var parsedDoc = trustedHTMLHandler.htmlToDOM(html);
32246
+ var fragment = doc.createDocumentFragment();
32247
+ var body = parsedDoc === null || parsedDoc === void 0 ? void 0 : parsedDoc.body;
32248
+ if (body) {
32249
+ while (body.firstChild) {
32250
+ fragment.appendChild(body.firstChild);
32251
+ }
32252
+ }
32253
+ return fragment;
32254
+ }
32255
+
32256
+
31881
32257
  /***/ },
31882
32258
 
31883
32259
  /***/ "./packages/roosterjs-content-model-plugins/lib/announce/AnnouncePlugin.ts"