roosterjs 9.53.0 → 9.55.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.
@@ -6751,7 +6751,7 @@ var formatSegmentWithContentModel_1 = __webpack_require__(/*! ../utils/formatSeg
6751
6751
  */
6752
6752
  function changeCapitalization(editor, capitalization, language) {
6753
6753
  editor.focus();
6754
- (0, formatSegmentWithContentModel_1.formatSegmentWithContentModel)(editor, 'changeCapitalization', function (_, __, segment) {
6754
+ (0, formatSegmentWithContentModel_1.formatSegmentWithContentModel)(editor, 'changeCapitalization', function (_, __, segment, paragraph) {
6755
6755
  if ((segment === null || segment === void 0 ? void 0 : segment.segmentType) == 'Text') {
6756
6756
  switch (capitalization) {
6757
6757
  case 'lowerCase':
@@ -6762,7 +6762,13 @@ function changeCapitalization(editor, capitalization, language) {
6762
6762
  break;
6763
6763
  case 'capitalize':
6764
6764
  var wordArray = segment.text.toLocaleLowerCase(language).split(' ');
6765
- for (var i = 0; i < wordArray.length; i++) {
6765
+ // When a collapsed selection is in the middle of a word, the word is split
6766
+ // into multiple text segments around the selection marker. In that case the
6767
+ // first segment of the word continues from a previous segment, so its first
6768
+ // word must not be capitalized to avoid results like "HeLlo" for "he|llo".
6769
+ var precedingChar = getPrecedingCharacter(paragraph, segment);
6770
+ var startIndex = precedingChar && precedingChar != ' ' ? 1 : 0;
6771
+ for (var i = startIndex; i < wordArray.length; i++) {
6766
6772
  wordArray[i] =
6767
6773
  wordArray[i].charAt(0).toLocaleUpperCase(language) +
6768
6774
  wordArray[i].slice(1);
@@ -6786,6 +6792,30 @@ function changeCapitalization(editor, capitalization, language) {
6786
6792
  });
6787
6793
  }
6788
6794
  exports.changeCapitalization = changeCapitalization;
6795
+ /**
6796
+ * Get the character immediately preceding the given segment within its paragraph, skipping
6797
+ * selection markers (which carry no text). Returns an empty string if there is no preceding
6798
+ * text character (e.g. the segment starts the paragraph or follows a non-text segment).
6799
+ */
6800
+ function getPrecedingCharacter(paragraph, segment) {
6801
+ if (!paragraph) {
6802
+ return '';
6803
+ }
6804
+ var index = paragraph.segments.indexOf(segment);
6805
+ for (var i = index - 1; i >= 0; i--) {
6806
+ var previous = paragraph.segments[i];
6807
+ if (previous.segmentType == 'SelectionMarker') {
6808
+ continue;
6809
+ }
6810
+ else if (previous.segmentType == 'Text' && previous.text.length > 0) {
6811
+ return previous.text[previous.text.length - 1];
6812
+ }
6813
+ else {
6814
+ break;
6815
+ }
6816
+ }
6817
+ return '';
6818
+ }
6789
6819
 
6790
6820
 
6791
6821
  /***/ },
@@ -8019,6 +8049,15 @@ function formatInsertPointWithContentModel(editor, insertPoint, callback, option
8019
8049
  textWithSelection: getShadowTextProcessor(bundle),
8020
8050
  },
8021
8051
  tryGetFromCache: false,
8052
+ // When an element carries "container level" styles such as margin or padding, we first
8053
+ // wrap it in a FormatContainer. After all its child nodes are processed, we decide whether
8054
+ // to keep the FormatContainer or fall back to a plain paragraph when it only wraps a single
8055
+ // paragraph. However, formatInsertPointWithContentModel persists the Content Model group path
8056
+ // during processing so the later formatting callback can still use it (see the
8057
+ // DomToModelContextWithPath interface below). If the FormatContainer falls back to a paragraph,
8058
+ // it is removed from the model and the persisted path becomes invalid. To keep the path valid,
8059
+ // we skip the fallback check here and always keep the FormatContainer when one is needed.
8060
+ skipFormatContainerFallbackCheck: true,
8022
8061
  });
8023
8062
  }
8024
8063
  exports.formatInsertPointWithContentModel = formatInsertPointWithContentModel;
@@ -9339,21 +9378,11 @@ exports.generatePasteOptionFromPlugins = generatePasteOptionFromPlugins;
9339
9378
  "use strict";
9340
9379
 
9341
9380
  Object.defineProperty(exports, "__esModule", ({ value: true }));
9342
- exports.mergePasteContent = exports.cloneModelForPaste = void 0;
9381
+ exports.mergePasteContent = void 0;
9343
9382
  var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
9344
9383
  var createDomToModelContextForSanitizing_1 = __webpack_require__(/*! ../createModelFromHtml/createDomToModelContextForSanitizing */ "./packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.ts");
9345
9384
  var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
9346
9385
  var BlackColor = 'rgb(0,0,0)';
9347
- var CloneOption = {
9348
- includeCachedElement: function (node, type) { return (type == 'cache' ? undefined : node); },
9349
- };
9350
- /**
9351
- * @internal
9352
- */
9353
- function cloneModelForPaste(model) {
9354
- return (0, roosterjs_content_model_dom_1.cloneModel)(model, CloneOption);
9355
- }
9356
- exports.cloneModelForPaste = cloneModelForPaste;
9357
9386
  /**
9358
9387
  * @internal
9359
9388
  */
@@ -9361,7 +9390,7 @@ function mergePasteContent(editor, eventResult, isFirstPaste) {
9361
9390
  var fragment = eventResult.fragment, domToModelOption = eventResult.domToModelOption, customizedMerge = eventResult.customizedMerge, pasteType = eventResult.pasteType, clipboardData = eventResult.clipboardData, containsBlockElements = eventResult.containsBlockElements;
9362
9391
  editor.formatContentModel(function (model, context) {
9363
9392
  if (!isFirstPaste && clipboardData.modelBeforePaste) {
9364
- var clonedModel = cloneModelForPaste(clipboardData.modelBeforePaste);
9393
+ var clonedModel = (0, roosterjs_content_model_dom_1.cloneModelForPaste)(clipboardData.modelBeforePaste);
9365
9394
  model.blocks = clonedModel.blocks;
9366
9395
  }
9367
9396
  var domToModelContext = (0, createDomToModelContextForSanitizing_1.createDomToModelContextForSanitizing)(editor.getDocument(), undefined /*defaultFormat*/, editor.getEnvironment().domToModelSettings.customized, domToModelOption, editor.getDOMHelper());
@@ -9440,10 +9469,11 @@ function getLastSegmentFormat(pasteModel) {
9440
9469
  Object.defineProperty(exports, "__esModule", ({ value: true }));
9441
9470
  exports.paste = void 0;
9442
9471
  var cleanHtmlComments_1 = __webpack_require__(/*! ./cleanHtmlComments */ "./packages/roosterjs-content-model-core/lib/command/paste/cleanHtmlComments.ts");
9443
- var mergePasteContent_1 = __webpack_require__(/*! ./mergePasteContent */ "./packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts");
9472
+ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
9444
9473
  var convertInlineCss_1 = __webpack_require__(/*! ../createModelFromHtml/convertInlineCss */ "./packages/roosterjs-content-model-core/lib/command/createModelFromHtml/convertInlineCss.ts");
9445
9474
  var createPasteFragment_1 = __webpack_require__(/*! ./createPasteFragment */ "./packages/roosterjs-content-model-core/lib/command/paste/createPasteFragment.ts");
9446
9475
  var generatePasteOptionFromPlugins_1 = __webpack_require__(/*! ./generatePasteOptionFromPlugins */ "./packages/roosterjs-content-model-core/lib/command/paste/generatePasteOptionFromPlugins.ts");
9476
+ var mergePasteContent_1 = __webpack_require__(/*! ./mergePasteContent */ "./packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts");
9447
9477
  var retrieveHtmlInfo_1 = __webpack_require__(/*! ./retrieveHtmlInfo */ "./packages/roosterjs-content-model-core/lib/command/paste/retrieveHtmlInfo.ts");
9448
9478
  /**
9449
9479
  * Paste into editor using a clipboardData object
@@ -9459,7 +9489,7 @@ function paste(editor, clipboardData, pasteTypeOrGetter) {
9459
9489
  if (!clipboardData.modelBeforePaste) {
9460
9490
  isFirstPaste = true;
9461
9491
  editor.formatContentModel(function (model) {
9462
- clipboardData.modelBeforePaste = (0, mergePasteContent_1.cloneModelForPaste)(model);
9492
+ clipboardData.modelBeforePaste = (0, roosterjs_content_model_dom_1.cloneModelForPaste)(model);
9463
9493
  return false;
9464
9494
  });
9465
9495
  }
@@ -10103,6 +10133,9 @@ var createContentModel = function (core, option, selectionOverride) {
10103
10133
  var domToModelContext = option
10104
10134
  ? (0, roosterjs_content_model_dom_1.createDomToModelContext)(editorContext, settings.builtIn, settings.customized, option)
10105
10135
  : (0, roosterjs_content_model_dom_1.createDomToModelContextWithConfig)(settings.calculated, editorContext);
10136
+ if (option === null || option === void 0 ? void 0 : option.skipFormatContainerFallbackCheck) {
10137
+ domToModelContext.skipFormatContainerFallbackCheck = true;
10138
+ }
10106
10139
  if (selection) {
10107
10140
  domToModelContext.selection = selection;
10108
10141
  }
@@ -11574,6 +11607,11 @@ var CachePlugin = /** @class */ (function () {
11574
11607
  _this.invalidateCache();
11575
11608
  }
11576
11609
  break;
11610
+ case 'attribute':
11611
+ if (!_this.state.domIndexer.reconcileImageAttribute(mutation.element, mutation.attributeName)) {
11612
+ _this.invalidateCache();
11613
+ }
11614
+ break;
11577
11615
  case 'unknown':
11578
11616
  _this.invalidateCache();
11579
11617
  break;
@@ -12105,6 +12143,33 @@ var DomIndexerImpl = /** @class */ (function () {
12105
12143
  return false;
12106
12144
  }
12107
12145
  };
12146
+ DomIndexerImpl.prototype.reconcileImageAttribute = function (element, attributeName) {
12147
+ var _a, _b;
12148
+ if ((0, roosterjs_content_model_dom_1.isElementOfType)(element, 'img')) {
12149
+ var image = (_a = getIndexedSegmentItem(element)) === null || _a === void 0 ? void 0 : _a.segments[0];
12150
+ if ((image === null || image === void 0 ? void 0 : image.segmentType) == 'Image') {
12151
+ if (attributeName == 'src') {
12152
+ // Use getAttribute('src') instead of retrieving src directly, in case the src
12153
+ // has port and may be stripped by browser. This matches imageProcessor.
12154
+ image.src = (_b = element.getAttribute('src')) !== null && _b !== void 0 ? _b : '';
12155
+ return true;
12156
+ }
12157
+ else if (attributeName.indexOf('data-') == 0) {
12158
+ // A data-* attribute may be added, modified or removed. Rebuild the whole
12159
+ // dataset from DOM to keep it in sync, the same way imageProcessor builds it.
12160
+ var dataset_1 = image.dataset;
12161
+ (0, roosterjs_content_model_dom_1.getObjectKeys)(dataset_1).forEach(function (key) {
12162
+ delete dataset_1[key];
12163
+ });
12164
+ (0, roosterjs_content_model_dom_1.getObjectKeys)(element.dataset).forEach(function (key) {
12165
+ dataset_1[key] = element.dataset[key] || '';
12166
+ });
12167
+ return true;
12168
+ }
12169
+ }
12170
+ }
12171
+ return false;
12172
+ };
12108
12173
  DomIndexerImpl.prototype.onBlockEntityDelimiter = function (node, entity, parent) {
12109
12174
  if ((0, roosterjs_content_model_dom_1.isNodeOfType)(node, 'ELEMENT_NODE') && (0, roosterjs_content_model_dom_1.isEntityDelimiter)(node) && node.firstChild) {
12110
12175
  var indexedDelimiter = node.firstChild;
@@ -12420,6 +12485,16 @@ var TextMutationObserverImpl = /** @class */ (function () {
12420
12485
  (0, roosterjs_content_model_dom_1.isNodeOfType)(target, 'ELEMENT_NODE')) {
12421
12486
  _this.onMutation({ type: 'elementId', element: target });
12422
12487
  }
12488
+ else if (mutation.attributeName &&
12489
+ (0, roosterjs_content_model_dom_1.isNodeOfType)(target, 'ELEMENT_NODE') &&
12490
+ (mutation.attributeName == 'src' ||
12491
+ mutation.attributeName.indexOf('data-') == 0)) {
12492
+ _this.onMutation({
12493
+ type: 'attribute',
12494
+ element: target,
12495
+ attributeName: mutation.attributeName,
12496
+ });
12497
+ }
12423
12498
  else {
12424
12499
  // We cannot handle attributes changes on editor content for now
12425
12500
  canHandle = false;
@@ -12699,7 +12774,7 @@ var CopyPastePlugin = /** @class */ (function () {
12699
12774
  var dataTransfer = event.clipboardData;
12700
12775
  if (shouldPreventDefaultPaste(dataTransfer, editor_1)) {
12701
12776
  event.preventDefault();
12702
- (0, roosterjs_content_model_dom_1.extractClipboardItems)((0, roosterjs_content_model_dom_1.toArray)(dataTransfer.items), _this.state.allowedCustomPasteType).then(function (clipboardData) {
12777
+ (0, roosterjs_content_model_dom_1.extractClipboardItems)((0, roosterjs_content_model_dom_1.toArray)(dataTransfer.items), _this.state.allowedCustomPasteType, true /* isPasteNative */).then(function (clipboardData) {
12703
12778
  if (!editor_1.isDisposed()) {
12704
12779
  (0, paste_1.paste)(editor_1, clipboardData, _this.state.defaultPasteType);
12705
12780
  }
@@ -18136,7 +18211,9 @@ var formatContainerProcessorInternal = function (group, element, context, forceF
18136
18211
  if (element.style.fontSize && parseInt(element.style.fontSize) == 0) {
18137
18212
  formatContainer.zeroFontSize = true;
18138
18213
  }
18139
- if (shouldFallbackToParagraph(formatContainer) && !forceFormatContainer) {
18214
+ if (!context.skipFormatContainerFallbackCheck &&
18215
+ shouldFallbackToParagraph(formatContainer) &&
18216
+ !forceFormatContainer) {
18140
18217
  // For DIV container that only has one paragraph child, container style can be merged into paragraph
18141
18218
  // and no need to have this container
18142
18219
  var paragraph = formatContainer.blocks[0];
@@ -19717,7 +19794,7 @@ var ContentHandlers = (_a = {},
19717
19794
  * @param allowedCustomPasteType Allowed custom content type when paste besides text/plain, text/html and images
19718
19795
  Only text types are supported, and do not add "text/" prefix to the type values
19719
19796
  */
19720
- function extractClipboardItems(items, allowedCustomPasteType) {
19797
+ function extractClipboardItems(items, allowedCustomPasteType, isPasteNative) {
19721
19798
  var data = {
19722
19799
  types: [],
19723
19800
  text: '',
@@ -19725,7 +19802,7 @@ function extractClipboardItems(items, allowedCustomPasteType) {
19725
19802
  files: [],
19726
19803
  rawHtml: null,
19727
19804
  customValues: {},
19728
- pasteNativeEvent: true,
19805
+ pasteNativeEvent: !!isPasteNative,
19729
19806
  };
19730
19807
  return Promise.all((items || []).map(function (item) {
19731
19808
  var type = item.type;
@@ -20666,6 +20743,7 @@ function pruneUnselectedModel(model) {
20666
20743
  exports.pruneUnselectedModel = pruneUnselectedModel;
20667
20744
  function pruneUnselectedModelInternal(model, isSelectionAfterElement) {
20668
20745
  var e_1, _a, _b;
20746
+ var _c;
20669
20747
  for (var index = model.blocks.length - 1; index >= 0; index--) {
20670
20748
  var block = model.blocks[index];
20671
20749
  switch (block.blockType) {
@@ -20689,15 +20767,17 @@ function pruneUnselectedModelInternal(model, isSelectionAfterElement) {
20689
20767
  case 'Paragraph':
20690
20768
  var newSegments = [];
20691
20769
  try {
20692
- for (var _c = (e_1 = void 0, (0, tslib_1.__values)(block.segments)), _d = _c.next(); !_d.done; _d = _c.next()) {
20693
- var segment = _d.value;
20770
+ for (var _d = (e_1 = void 0, (0, tslib_1.__values)(block.segments)), _e = _d.next(); !_e.done; _e = _d.next()) {
20771
+ var segment = _e.value;
20772
+ var isSelectedOrUndeletable = segment.isSelected || ((_c = segment.link) === null || _c === void 0 ? void 0 : _c.format.undeletable);
20694
20773
  if (segment.segmentType == 'General') {
20695
20774
  pruneUnselectedModel(segment);
20696
- if (segment.blocks.length > 0 || segment.isSelected) {
20775
+ if (segment.blocks.length > 0 || isSelectedOrUndeletable) {
20697
20776
  newSegments.push(segment);
20698
20777
  }
20699
20778
  }
20700
- else if (segment.isSelected && segment.segmentType != 'SelectionMarker') {
20779
+ else if (isSelectedOrUndeletable &&
20780
+ segment.segmentType != 'SelectionMarker') {
20701
20781
  newSegments.push(segment);
20702
20782
  }
20703
20783
  }
@@ -20705,7 +20785,7 @@ function pruneUnselectedModelInternal(model, isSelectionAfterElement) {
20705
20785
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
20706
20786
  finally {
20707
20787
  try {
20708
- if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
20788
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
20709
20789
  }
20710
20790
  finally { if (e_1) throw e_1.error; }
20711
20791
  }
@@ -23678,8 +23758,8 @@ exports.shouldSetValue = shouldSetValue;
23678
23758
  Object.defineProperty(exports, "__esModule", ({ value: true }));
23679
23759
  exports.createImage = exports.createText = exports.createTableCell = exports.createTable = exports.createSelectionMarker = exports.createParagraph = exports.createFormatContainer = exports.createListItem = exports.createBr = exports.isLinkUndeletable = exports.setLinkUndeletable = exports.scrollRectIntoView = exports.normalizeRect = exports.isWhiteSpacePreserved = exports.reuseCachedElement = exports.findClosestBlockEntityContainer = exports.isBlockEntityContainer = exports.isEntityDelimiter = exports.addDelimiters = exports.generateEntityClassNames = exports.parseEntityFormat = exports.getAllEntityWrappers = exports.findClosestEntityWrapper = exports.isEntityElement = exports.unwrap = exports.wrap = exports.wrapAllChildNodes = exports.moveChildNodes = exports.toArray = exports.getSafeIdSelector = exports.getObjectKeys = exports.isElementOfType = exports.isNodeOfType = exports.hasMetadata = exports.getMetadata = exports.updateMetadata = exports.buildSelectionMarker = exports.isBlockElement = exports.areSameFormats = exports.parseFormat = exports.getRegularSelectionOffsets = exports.formatContainerProcessor = exports.tableProcessor = exports.entityProcessor = exports.processChildNode = exports.handleRegularSelection = exports.childProcessor = exports.contentModelToText = exports.contentModelToDom = exports.domToContentModel = void 0;
23680
23760
  exports.trimModelForSelection = exports.getDOMInsertPointRect = exports.getSelectionRootNode = exports.isBold = exports.createModelToDomConfig = exports.createModelToDomContextWithConfig = exports.createModelToDomContext = exports.createDomToModelConfig = exports.createDomToModelContextWithConfig = exports.createDomToModelContext = exports.defaultGenerateColorKey = exports.parseColor = exports.setColor = exports.getColor = exports.DeprecatedColors = exports.BorderKeys = exports.parseValueWithUnit = exports.getAutoListStyleType = exports.getOrderedListNumberStr = exports.ParagraphFormats = exports.ListFormatsToMove = exports.ListFormatsToKeep = exports.ListFormats = exports.copyFormat = exports.setParagraphNotImplicit = exports.normalizeSegmentFormat = exports.mergeTextSegments = exports.normalizeSingleSegment = exports.isEmpty = exports.addSegment = exports.unwrapBlock = exports.isGeneralSegment = exports.normalizeContentModel = exports.normalizeParagraph = exports.addTextSegment = exports.addLink = exports.addCode = exports.addBlock = exports.mutateSegment = exports.mutateSegments = exports.mutateBlock = exports.createTableRow = exports.createEmptyModel = exports.createListLevel = exports.createDivider = exports.createEntity = exports.createGeneralBlock = exports.createGeneralSegment = exports.createParagraphDecorator = exports.createContentModelDocument = void 0;
23681
- exports.runEditSteps = exports.getClosestAncestorBlockGroupIndex = exports.getSegmentTextFormat = exports.getListStyleTypeFromString = exports.retrieveModelFormatState = exports.setTableCellBackgroundColor = exports.MIN_ALLOWED_TABLE_CELL_HEIGHT = exports.MIN_ALLOWED_TABLE_CELL_WIDTH = exports.normalizeTable = exports.setFirstColumnFormatBorders = exports.applyTableFormat = exports.deleteBlock = exports.deleteSegment = exports.deleteSelection = exports.mergeModel = exports.cloneModel = exports.setSelection = exports.hasSelectionInBlockGroup = exports.hasSelectionInSegment = exports.hasSelectionInBlock = exports.getSelectedCells = exports.getSelectedSegmentsAndParagraphs = exports.getSelectedSegments = exports.getSelectedParagraphs = exports.getOperationalBlocks = exports.getFirstSelectedTable = exports.getFirstSelectedListItem = exports.iterateSelections = exports.isBlockGroupOfType = exports.getRangesByText = exports.getImageState = exports.setImageState = exports.getParagraphMarker = exports.setParagraphMarker = exports.cacheGetEventData = exports.extractClipboardItems = exports.normalizeFontFamily = exports.transformColor = exports.retrieveDocumentMetadata = exports.readFile = exports.parseTableCells = exports.normalizeText = exports.isSpace = exports.isPunctuation = exports.extractBorderValues = exports.combineBorderValue = exports.getNodePositionFromEvent = exports.isCursorMovingKey = exports.isModifierKey = exports.isCharacterValue = void 0;
23682
- exports.EmptySegmentFormat = exports.UnorderedListStyleMap = exports.OrderedListStyleMap = exports.TableBorderFormat = exports.NumberingListType = exports.BulletListType = exports.ChangeSource = exports.ListMetadataDefinition = exports.getListMetadata = exports.updateListMetadata = exports.getTableMetadata = exports.updateTableMetadata = exports.getTableCellMetadata = exports.updateTableCellMetadata = exports.getImageMetadata = exports.updateImageMetadata = void 0;
23761
+ exports.getClosestAncestorBlockGroupIndex = exports.getSegmentTextFormat = exports.getListStyleTypeFromString = exports.retrieveModelFormatState = exports.setTableCellBackgroundColor = exports.MIN_ALLOWED_TABLE_CELL_HEIGHT = exports.MIN_ALLOWED_TABLE_CELL_WIDTH = exports.normalizeTable = exports.setFirstColumnFormatBorders = exports.applyTableFormat = exports.deleteBlock = exports.deleteSegment = exports.deleteSelection = exports.mergeModel = exports.cloneModelForPaste = exports.cloneModel = exports.setSelection = exports.hasSelectionInBlockGroup = exports.hasSelectionInSegment = exports.hasSelectionInBlock = exports.getSelectedCells = exports.getSelectedSegmentsAndParagraphs = exports.getSelectedSegments = exports.getSelectedParagraphs = exports.getOperationalBlocks = exports.getFirstSelectedTable = exports.getFirstSelectedListItem = exports.iterateSelections = exports.isBlockGroupOfType = exports.getRangesByText = exports.getImageState = exports.setImageState = exports.getParagraphMarker = exports.setParagraphMarker = exports.cacheGetEventData = exports.extractClipboardItems = exports.normalizeFontFamily = exports.transformColor = exports.retrieveDocumentMetadata = exports.readFile = exports.parseTableCells = exports.normalizeText = exports.isSpace = exports.isPunctuation = exports.extractBorderValues = exports.combineBorderValue = exports.getNodePositionFromEvent = exports.isCursorMovingKey = exports.isModifierKey = exports.isCharacterValue = void 0;
23762
+ exports.EmptySegmentFormat = exports.UnorderedListStyleMap = exports.OrderedListStyleMap = exports.TableBorderFormat = exports.NumberingListType = exports.BulletListType = exports.ChangeSource = exports.ListMetadataDefinition = exports.getListMetadata = exports.updateListMetadata = exports.getTableMetadata = exports.updateTableMetadata = exports.getTableCellMetadata = exports.updateTableCellMetadata = exports.getImageMetadata = exports.updateImageMetadata = exports.runEditSteps = void 0;
23683
23763
  var domToContentModel_1 = __webpack_require__(/*! ./domToModel/domToContentModel */ "./packages/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts");
23684
23764
  Object.defineProperty(exports, "domToContentModel", ({ enumerable: true, get: function () { return domToContentModel_1.domToContentModel; } }));
23685
23765
  var contentModelToDom_1 = __webpack_require__(/*! ./modelToDom/contentModelToDom */ "./packages/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts");
@@ -23910,6 +23990,7 @@ var setSelection_1 = __webpack_require__(/*! ./modelApi/selection/setSelection *
23910
23990
  Object.defineProperty(exports, "setSelection", ({ enumerable: true, get: function () { return setSelection_1.setSelection; } }));
23911
23991
  var cloneModel_1 = __webpack_require__(/*! ./modelApi/editing/cloneModel */ "./packages/roosterjs-content-model-dom/lib/modelApi/editing/cloneModel.ts");
23912
23992
  Object.defineProperty(exports, "cloneModel", ({ enumerable: true, get: function () { return cloneModel_1.cloneModel; } }));
23993
+ Object.defineProperty(exports, "cloneModelForPaste", ({ enumerable: true, get: function () { return cloneModel_1.cloneModelForPaste; } }));
23913
23994
  var mergeModel_1 = __webpack_require__(/*! ./modelApi/editing/mergeModel */ "./packages/roosterjs-content-model-dom/lib/modelApi/editing/mergeModel.ts");
23914
23995
  Object.defineProperty(exports, "mergeModel", ({ enumerable: true, get: function () { return mergeModel_1.mergeModel; } }));
23915
23996
  var deleteSelection_1 = __webpack_require__(/*! ./modelApi/editing/deleteSelection */ "./packages/roosterjs-content-model-dom/lib/modelApi/editing/deleteSelection.ts");
@@ -24739,7 +24820,34 @@ exports.normalizeAllSegments = normalizeAllSegments;
24739
24820
  function normalizeSingleSegment(paragraph, segment, ignoreTrailingSpaces) {
24740
24821
  if (ignoreTrailingSpaces === void 0) { ignoreTrailingSpaces = false; }
24741
24822
  var context = resetNormalizeSegmentContext();
24823
+ var index = paragraph.segments.indexOf(segment);
24742
24824
  context.ignoreTrailingSpaces = ignoreTrailingSpaces;
24825
+ // Search backward for the nearest non-empty text segment (skipping SelectionMarkers and empty text),
24826
+ // to determine whether leading spaces of the current segment should be preserved.
24827
+ // If the previous text doesn't end with a space, keep the leading space to maintain word separation.
24828
+ for (var i = index - 1; i >= 0; i--) {
24829
+ var s = paragraph.segments[i];
24830
+ if (s.segmentType == 'Text') {
24831
+ if (s.text.length > 0) {
24832
+ if (s.text.substr(-1) != SPACE) {
24833
+ context.ignoreLeadingSpaces = false;
24834
+ }
24835
+ break;
24836
+ }
24837
+ }
24838
+ else if (s.segmentType == 'Image') {
24839
+ // If the previous segment is an image, we should keep the leading space of the current segment to maintain word separation.
24840
+ context.ignoreLeadingSpaces = false;
24841
+ break;
24842
+ }
24843
+ else if (s.segmentType == 'Br') {
24844
+ // If the previous segment is a line break, we should ignore the leading space of the current segment.
24845
+ context.ignoreLeadingSpaces = true;
24846
+ }
24847
+ else if (s.segmentType != 'SelectionMarker') {
24848
+ break;
24849
+ }
24850
+ }
24743
24851
  normalizeSegment(paragraph, segment, context);
24744
24852
  }
24745
24853
  exports.normalizeSingleSegment = normalizeSingleSegment;
@@ -24780,6 +24888,10 @@ function normalizeSegment(paragraph, segment, context) {
24780
24888
  context.ignoreLeadingSpaces = false;
24781
24889
  break;
24782
24890
  case 'Text':
24891
+ // Skip empty text segments to avoid affecting normalization context
24892
+ if (segment.text.length == 0) {
24893
+ break;
24894
+ }
24783
24895
  context.textSegments.push(segment);
24784
24896
  context.lastInlineSegment = segment;
24785
24897
  context.lastTextSegment = segment;
@@ -24787,9 +24899,9 @@ function normalizeSegment(paragraph, segment, context) {
24787
24899
  var last = segment.text.substr(-1);
24788
24900
  if (!(0, hasSpacesOnly_1.hasSpacesOnly)(segment.text)) {
24789
24901
  if (first == SPACE) {
24790
- // 1. Multiple leading space => single &nbsp; or empty (depends on if previous segment ends with space)
24902
+ // 1. Multiple leading space => single space or empty (depends on if previous segment ends with space)
24791
24903
  (0, mutate_1.mutateSegment)(paragraph, segment, function (textSegment) {
24792
- textSegment.text = textSegment.text.replace(LEADING_SPACE_REGEX, context.ignoreLeadingSpaces ? '' : NONE_BREAK_SPACE);
24904
+ textSegment.text = textSegment.text.replace(LEADING_SPACE_REGEX, context.ignoreLeadingSpaces ? '' : SPACE);
24793
24905
  });
24794
24906
  }
24795
24907
  if (last == SPACE) {
@@ -25888,8 +26000,20 @@ function getBorderStyleFromColor(color) {
25888
26000
  "use strict";
25889
26001
 
25890
26002
  Object.defineProperty(exports, "__esModule", ({ value: true }));
25891
- exports.cloneModel = void 0;
26003
+ exports.cloneModel = exports.cloneModelForPaste = void 0;
25892
26004
  var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
26005
+ var CloneOption = {
26006
+ includeCachedElement: function (node, type) { return (type == 'cache' ? undefined : node); },
26007
+ };
26008
+ /**
26009
+ * Clone a content model for paste operations, ensuring that cached elements are handled appropriately.
26010
+ * @param model The content model to clone
26011
+ * @returns A cloned content model suitable for paste operations
26012
+ */
26013
+ function cloneModelForPaste(model) {
26014
+ return cloneModel(model, CloneOption);
26015
+ }
26016
+ exports.cloneModelForPaste = cloneModelForPaste;
25893
26017
  /**
25894
26018
  * Clone a content model
25895
26019
  * @param model The content model to clone
@@ -28673,6 +28797,7 @@ function internalIterateSelections(path, callback, option, table, treatAllAsSele
28673
28797
 
28674
28798
  Object.defineProperty(exports, "__esModule", ({ value: true }));
28675
28799
  exports.setSelection = void 0;
28800
+ var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
28676
28801
  var isGeneralSegment_1 = __webpack_require__(/*! ../typeCheck/isGeneralSegment */ "./packages/roosterjs-content-model-dom/lib/modelApi/typeCheck/isGeneralSegment.ts");
28677
28802
  var mutate_1 = __webpack_require__(/*! ../common/mutate */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/mutate.ts");
28678
28803
  /**
@@ -28710,6 +28835,7 @@ function setSelectionToBlockGroup(group, isInSelection, start, end) {
28710
28835
  });
28711
28836
  }
28712
28837
  function setSelectionToBlock(block, isInSelection, start, end) {
28838
+ var _a;
28713
28839
  switch (block.blockType) {
28714
28840
  case 'BlockGroup':
28715
28841
  return setSelectionToBlockGroup(block, isInSelection, start, end);
@@ -28730,16 +28856,31 @@ function setSelectionToBlock(block, isInSelection, start, end) {
28730
28856
  return isInSelection;
28731
28857
  });
28732
28858
  case 'Paragraph':
28733
- var segmentsToDelete_1 = [];
28859
+ var state_1 = {
28860
+ segmentsToDelete: [],
28861
+ boundaryMarkers: [],
28862
+ hasSelectedNonMarker: false,
28863
+ };
28734
28864
  block.segments.forEach(function (segment, i) {
28735
28865
  isInSelection = handleSelection(isInSelection, segment, start, end, function (isInSelection) {
28736
- return setSelectionToSegment(block, segment, isInSelection, segmentsToDelete_1, start, end, i);
28866
+ return setSelectionToSegment(block, segment, isInSelection, state_1, start, end, i);
28737
28867
  });
28738
28868
  });
28739
- if (segmentsToDelete_1.length > 0) {
28869
+ if (state_1.hasSelectedNonMarker) {
28870
+ // This paragraph contains a real (non-marker) selected segment, so any leading/trailing
28871
+ // selection marker of the range is redundant within this paragraph and can be removed.
28872
+ // We only do this within the same paragraph: a boundary marker at a paragraph edge must be
28873
+ // kept to distinguish "selection starts at the beginning of this line" from "selection
28874
+ // starts at the end of the previous line".
28875
+ (_a = state_1.segmentsToDelete).push.apply(_a, (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(state_1.boundaryMarkers), false));
28876
+ }
28877
+ if (state_1.segmentsToDelete.length > 0) {
28740
28878
  var mutablePara = (0, mutate_1.mutateBlock)(block);
28741
28879
  var index = void 0;
28742
- while ((index = segmentsToDelete_1.pop()) !== undefined) {
28880
+ // Sort ascending so the pop()-based splice below always removes the highest index first,
28881
+ // keeping the remaining indices valid (boundary markers may sit before queued deletions).
28882
+ state_1.segmentsToDelete.sort(function (a, b) { return a - b; });
28883
+ while ((index = state_1.segmentsToDelete.pop()) !== undefined) {
28743
28884
  if (index >= 0) {
28744
28885
  mutablePara.segments.splice(index, 1);
28745
28886
  }
@@ -28788,14 +28929,23 @@ function findCell(table, cell) {
28788
28929
  : -1;
28789
28930
  return { row: row, col: col };
28790
28931
  }
28791
- function setSelectionToSegment(paragraph, segment, isInSelection, segmentsToDelete, start, end, i) {
28932
+ function setSelectionToSegment(paragraph, segment, isInSelection, state, start, end, i) {
28933
+ if (segment.segmentType != 'SelectionMarker' && isInSelection) {
28934
+ state.hasSelectedNonMarker = true;
28935
+ }
28792
28936
  switch (segment.segmentType) {
28793
28937
  case 'SelectionMarker':
28794
28938
  if (!isInSelection || (segment != start && segment != end)) {
28795
28939
  // Delete the selection marker when
28796
28940
  // 1. It is not in selection any more. Or
28797
28941
  // 2. It is in middle of selection, so no need to have it
28798
- segmentsToDelete.push(i);
28942
+ state.segmentsToDelete.push(i);
28943
+ }
28944
+ else {
28945
+ // It is a leading/trailing selection marker of a range selection. Keep it for now, but
28946
+ // remember it so it can be removed later if this same paragraph also contains a real
28947
+ // (non-marker) selected segment, in which case the marker is redundant.
28948
+ state.boundaryMarkers.push(i);
28799
28949
  }
28800
28950
  return isInSelection;
28801
28951
  case 'General':
@@ -29236,7 +29386,7 @@ var handleBlockGroupChildren = function (doc, parent, group, context) {
29236
29386
  (_a = context.domIndexer) === null || _a === void 0 ? void 0 : _a.onBlockEntity(childBlock, group);
29237
29387
  }
29238
29388
  });
29239
- cleanUpNodeStack(listFormat.nodeStack, context);
29389
+ cleanUpNodeStack(listFormat.nodeStack, context, parent);
29240
29390
  // Remove all rest node if any since they don't appear in content model
29241
29391
  (0, cleanUpRestNodes_1.cleanUpRestNodes)(refNode, context.rewriteFromModel);
29242
29392
  }
@@ -29245,7 +29395,7 @@ var handleBlockGroupChildren = function (doc, parent, group, context) {
29245
29395
  }
29246
29396
  };
29247
29397
  exports.handleBlockGroupChildren = handleBlockGroupChildren;
29248
- function cleanUpNodeStack(nodeStack, context) {
29398
+ function cleanUpNodeStack(nodeStack, context, leavingParent) {
29249
29399
  var _a, _b;
29250
29400
  if (nodeStack.length > 0) {
29251
29401
  // Clear list stack, only run to nodeStack[1] because nodeStack[0] is the parent node
@@ -29253,6 +29403,13 @@ function cleanUpNodeStack(nodeStack, context) {
29253
29403
  var node = (_b = (_a = nodeStack.pop()) === null || _a === void 0 ? void 0 : _a.refNode) !== null && _b !== void 0 ? _b : null;
29254
29404
  (0, cleanUpRestNodes_1.cleanUpRestNodes)(node, context.rewriteFromModel);
29255
29405
  }
29406
+ if (leavingParent && nodeStack[0].node == leavingParent) {
29407
+ // When leaving a parent node that is the same with the root of node stack
29408
+ // It means the whole list node stack is being invalidated, so we clear it
29409
+ while (nodeStack.length > 0) {
29410
+ nodeStack.pop();
29411
+ }
29412
+ }
29256
29413
  }
29257
29414
  }
29258
29415
 
@@ -30641,11 +30798,17 @@ exports.MarkdownHeadings = {
30641
30798
  "use strict";
30642
30799
 
30643
30800
  Object.defineProperty(exports, "__esModule", ({ value: true }));
30644
- exports.convertContentModelToMarkdown = exports.convertMarkdownToContentModel = void 0;
30801
+ exports.MarkdownPastePlugin = exports.isPastedContentMarkdown = exports.isContentMarkdown = exports.convertContentModelToMarkdown = exports.convertMarkdownToContentModel = void 0;
30645
30802
  var convertMarkdownToContentModel_1 = __webpack_require__(/*! ./markdownToModel/convertMarkdownToContentModel */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts");
30646
30803
  Object.defineProperty(exports, "convertMarkdownToContentModel", ({ enumerable: true, get: function () { return convertMarkdownToContentModel_1.convertMarkdownToContentModel; } }));
30647
30804
  var convertContentModelToMarkdown_1 = __webpack_require__(/*! ./modelToMarkdown/convertContentModelToMarkdown */ "./packages/roosterjs-content-model-markdown/lib/modelToMarkdown/convertContentModelToMarkdown.ts");
30648
30805
  Object.defineProperty(exports, "convertContentModelToMarkdown", ({ enumerable: true, get: function () { return convertContentModelToMarkdown_1.convertContentModelToMarkdown; } }));
30806
+ var isContentMarkdown_1 = __webpack_require__(/*! ./publicApi/isContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts");
30807
+ Object.defineProperty(exports, "isContentMarkdown", ({ enumerable: true, get: function () { return isContentMarkdown_1.isContentMarkdown; } }));
30808
+ var isPastedContentMarkdown_1 = __webpack_require__(/*! ./publicApi/isPastedContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts");
30809
+ Object.defineProperty(exports, "isPastedContentMarkdown", ({ enumerable: true, get: function () { return isPastedContentMarkdown_1.isPastedContentMarkdown; } }));
30810
+ var MarkdownPastePlugin_1 = __webpack_require__(/*! ./plugins/MarkdownPastePlugin */ "./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts");
30811
+ Object.defineProperty(exports, "MarkdownPastePlugin", ({ enumerable: true, get: function () { return MarkdownPastePlugin_1.MarkdownPastePlugin; } }));
30649
30812
 
30650
30813
 
30651
30814
  /***/ },
@@ -31335,6 +31498,13 @@ function parseInlineSegments(text, segments, state, link) {
31335
31498
  };
31336
31499
  while (i < text.length) {
31337
31500
  var remaining = text.substring(i);
31501
+ // Escaped character: a backslash followed by an ASCII punctuation character emits
31502
+ // that character literally (e.g. "\*" -> "*") and is never treated as a marker.
31503
+ if (text[i] === '\\' && i + 1 < text.length && isEscapable(text[i + 1])) {
31504
+ buffer += text[i + 1];
31505
+ i += 2;
31506
+ continue;
31507
+ }
31338
31508
  // Image: ![alt](url)
31339
31509
  var imgMatch = imagePattern.exec(remaining);
31340
31510
  if (imgMatch && isValidUrl(imgMatch[2])) {
@@ -31398,6 +31568,10 @@ function shouldToggleFormatting(text, index, marker, currentState) {
31398
31568
  function isWhitespace(char) {
31399
31569
  return /\s/.test(char);
31400
31570
  }
31571
+ function isEscapable(char) {
31572
+ // Per CommonMark, any ASCII punctuation character may be backslash-escaped.
31573
+ return /[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/.test(char);
31574
+ }
31401
31575
  function toggleFormatting(state, type) {
31402
31576
  switch (type) {
31403
31577
  case 'bold':
@@ -31901,6 +32075,278 @@ function modelProcessor(model, newLine) {
31901
32075
  exports.modelProcessor = modelProcessor;
31902
32076
 
31903
32077
 
32078
+ /***/ },
32079
+
32080
+ /***/ "./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts"
32081
+ /*!**************************************************************************************!*\
32082
+ !*** ./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts ***!
32083
+ \**************************************************************************************/
32084
+ (__unused_webpack_module, exports, __webpack_require__) {
32085
+
32086
+ "use strict";
32087
+
32088
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
32089
+ exports.MarkdownPastePlugin = void 0;
32090
+ var convertMarkdownToContentModel_1 = __webpack_require__(/*! ../markdownToModel/convertMarkdownToContentModel */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts");
32091
+ var isPastedContentMarkdown_1 = __webpack_require__(/*! ../publicApi/isPastedContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts");
32092
+ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
32093
+ var DefaultOptions = {
32094
+ autoConversion: false,
32095
+ undoConversion: false,
32096
+ };
32097
+ /**
32098
+ * Markdown paste plugin. Handles the BeforePaste event and, when the pasted content
32099
+ * can be interpreted as markdown, converts the plain text into a Content Model and
32100
+ * pastes it as rich markdown content instead of the original clipboard HTML.
32101
+ */
32102
+ var MarkdownPastePlugin = /** @class */ (function () {
32103
+ /**
32104
+ * Construct a new instance of MarkdownPastePlugin
32105
+ * @param options Options to control the markdown paste behavior
32106
+ */
32107
+ function MarkdownPastePlugin(options) {
32108
+ this.editor = null;
32109
+ this.options = options !== null && options !== void 0 ? options : DefaultOptions;
32110
+ }
32111
+ /**
32112
+ * Get name of this plugin
32113
+ */
32114
+ MarkdownPastePlugin.prototype.getName = function () {
32115
+ return 'MarkdownPaste';
32116
+ };
32117
+ /**
32118
+ * The first method that editor will call to a plugin when editor is initializing.
32119
+ * It will pass in the editor instance, plugin should take this chance to save the
32120
+ * editor reference so that it can call to any editor method or format API later.
32121
+ * @param editor The editor object
32122
+ */
32123
+ MarkdownPastePlugin.prototype.initialize = function (editor) {
32124
+ this.editor = editor;
32125
+ };
32126
+ /**
32127
+ * The last method that editor will call to a plugin before it is disposed.
32128
+ * Plugin can take this chance to clear the reference to editor. After this method is
32129
+ * called, plugin should not call to any editor method since it will result in error.
32130
+ */
32131
+ MarkdownPastePlugin.prototype.dispose = function () {
32132
+ this.editor = null;
32133
+ };
32134
+ /**
32135
+ * Core method for a plugin. Once an event happens in editor, editor will call this
32136
+ * method of each plugin to handle the event as long as the event is not handled
32137
+ * exclusively by another plugin.
32138
+ * @param event The event to handle:
32139
+ */
32140
+ MarkdownPastePlugin.prototype.onPluginEvent = function (event) {
32141
+ if (!this.editor) {
32142
+ return;
32143
+ }
32144
+ if (event.eventType === 'contentChanged' &&
32145
+ event.source === roosterjs_content_model_dom_1.ChangeSource.Paste &&
32146
+ this.options.autoConversion &&
32147
+ event.data) {
32148
+ var clipboardData = event.data;
32149
+ var shouldConvert = clipboardData && clipboardData.pasteNativeEvent && this.options.autoConversion;
32150
+ if (shouldConvert &&
32151
+ (0, isPastedContentMarkdown_1.isPastedContentMarkdown)(this.editor, clipboardData) &&
32152
+ clipboardData.modelBeforePaste) {
32153
+ var modelBeforePaste_1 = (0, roosterjs_content_model_dom_1.cloneModelForPaste)(clipboardData.modelBeforePaste);
32154
+ (0, roosterjs_content_model_dom_1.mergeModel)(modelBeforePaste_1, (0, convertMarkdownToContentModel_1.convertMarkdownToContentModel)(clipboardData.text, { emptyLine: 'merge' }));
32155
+ if (this.options.undoConversion) {
32156
+ this.editor.takeSnapshot();
32157
+ }
32158
+ this.editor.formatContentModel(function (model) {
32159
+ model.blocks = modelBeforePaste_1.blocks;
32160
+ return true;
32161
+ }, {
32162
+ apiName: 'MarkdownConversion',
32163
+ scrollCaretIntoView: true,
32164
+ });
32165
+ }
32166
+ }
32167
+ else if (event.eventType === 'beforePaste' && !event.clipboardData.pasteNativeEvent) {
32168
+ var shouldConvert = event.pasteType === 'asMarkdown';
32169
+ if (shouldConvert && (0, isPastedContentMarkdown_1.isPastedContentMarkdown)(this.editor, event.clipboardData)) {
32170
+ convertPastedTextToMarkdown(this.editor, event.fragment, event.clipboardData.text);
32171
+ }
32172
+ }
32173
+ };
32174
+ return MarkdownPastePlugin;
32175
+ }());
32176
+ exports.MarkdownPastePlugin = MarkdownPastePlugin;
32177
+ function convertPastedTextToMarkdown(editor, fragment, text) {
32178
+ var model = (0, convertMarkdownToContentModel_1.convertMarkdownToContentModel)(text, {
32179
+ emptyLine: 'merge',
32180
+ });
32181
+ while (fragment.firstChild) {
32182
+ fragment.removeChild(fragment.firstChild);
32183
+ }
32184
+ (0, roosterjs_content_model_dom_1.contentModelToDom)(editor.getDocument(), fragment, model, (0, roosterjs_content_model_dom_1.createModelToDomContext)());
32185
+ }
32186
+
32187
+
32188
+ /***/ },
32189
+
32190
+ /***/ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts"
32191
+ /*!**************************************************************************************!*\
32192
+ !*** ./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts ***!
32193
+ \**************************************************************************************/
32194
+ (__unused_webpack_module, exports, __webpack_require__) {
32195
+
32196
+ "use strict";
32197
+
32198
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
32199
+ exports.isContentMarkdown = void 0;
32200
+ var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
32201
+ // Block-level markdown patterns. A line that matches any of these is considered markdown.
32202
+ var BlockPatterns = [
32203
+ /^#{1,6}\s.+/,
32204
+ /^\s*>\s.+/,
32205
+ /^\s*[\*\-\+]\s.+/,
32206
+ /^\s*\d+\.\s.+/,
32207
+ /^---+$/,
32208
+ /^\s*\|.*\|\s*$/, // table row: "| a | b |"
32209
+ ];
32210
+ // Inline markdown patterns. The text contains markdown if any of these match anywhere.
32211
+ var InlinePatterns = [
32212
+ /!\[[^\[\]]+\]\([^\)\s]+\)/,
32213
+ /\[[^\[\]]+\]\([^\)\s]+\)/,
32214
+ /\*\*[^\s*][^*]*\*\*/,
32215
+ /(^|[^*])\*[^\s*][^*]*\*([^*]|$)/,
32216
+ /~~[^\s~][^~]*~~/, // strikethrough: ~~text~~
32217
+ ];
32218
+ /**
32219
+ * Detect whether the given plain text contains any markdown markup.
32220
+ * Recognizes block-level patterns (headings, blockquotes, lists, horizontal rules, tables)
32221
+ * and inline patterns (bold, italic, strikethrough, links, images).
32222
+ * @param text The plain text to check.
32223
+ * @returns True if the text contains any markdown markup, false otherwise.
32224
+ */
32225
+ function isContentMarkdown(text) {
32226
+ var e_1, _a, e_2, _b, e_3, _c;
32227
+ if (!text || !text.trim()) {
32228
+ return false;
32229
+ }
32230
+ var lines = text.split(/\r\n|\r|\n/);
32231
+ try {
32232
+ 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()) {
32233
+ var line = lines_1_1.value;
32234
+ try {
32235
+ 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()) {
32236
+ var pattern = BlockPatterns_1_1.value;
32237
+ if (pattern.test(line)) {
32238
+ return true;
32239
+ }
32240
+ }
32241
+ }
32242
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
32243
+ finally {
32244
+ try {
32245
+ if (BlockPatterns_1_1 && !BlockPatterns_1_1.done && (_b = BlockPatterns_1.return)) _b.call(BlockPatterns_1);
32246
+ }
32247
+ finally { if (e_2) throw e_2.error; }
32248
+ }
32249
+ }
32250
+ }
32251
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
32252
+ finally {
32253
+ try {
32254
+ if (lines_1_1 && !lines_1_1.done && (_a = lines_1.return)) _a.call(lines_1);
32255
+ }
32256
+ finally { if (e_1) throw e_1.error; }
32257
+ }
32258
+ try {
32259
+ 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()) {
32260
+ var pattern = InlinePatterns_1_1.value;
32261
+ if (pattern.test(text)) {
32262
+ return true;
32263
+ }
32264
+ }
32265
+ }
32266
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
32267
+ finally {
32268
+ try {
32269
+ if (InlinePatterns_1_1 && !InlinePatterns_1_1.done && (_c = InlinePatterns_1.return)) _c.call(InlinePatterns_1);
32270
+ }
32271
+ finally { if (e_3) throw e_3.error; }
32272
+ }
32273
+ return false;
32274
+ }
32275
+ exports.isContentMarkdown = isContentMarkdown;
32276
+
32277
+
32278
+ /***/ },
32279
+
32280
+ /***/ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts"
32281
+ /*!********************************************************************************************!*\
32282
+ !*** ./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts ***!
32283
+ \********************************************************************************************/
32284
+ (__unused_webpack_module, exports, __webpack_require__) {
32285
+
32286
+ "use strict";
32287
+
32288
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
32289
+ exports.isPastedContentMarkdown = void 0;
32290
+ var isContentMarkdown_1 = __webpack_require__(/*! ./isContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts");
32291
+ // Tags that are considered "thin wrappers", which only add structure (such as line breaks)
32292
+ // around the plain text without applying any real formatting to its content.
32293
+ var ThinWrapperTags = new Set(['DIV', 'P', 'BR', 'SPAN']);
32294
+ var AllowedAttributes = new Set(['class', 'style']);
32295
+ /**
32296
+ * Detect whether the given clipboard content can be interpreted as markdown.
32297
+ * @param editor The editor instance.
32298
+ * @param clipboardData The clipboard data to check.
32299
+ * @returns True if the content can be interpreted as markdown, false otherwise.
32300
+ */
32301
+ function isPastedContentMarkdown(editor, clipboardData) {
32302
+ var text = clipboardData.text, rawHtml = clipboardData.rawHtml;
32303
+ if (!text || !text.trim()) {
32304
+ return false;
32305
+ }
32306
+ if ((0, isContentMarkdown_1.isContentMarkdown)(text)) {
32307
+ if (!rawHtml) {
32308
+ return true;
32309
+ }
32310
+ var doc = editor.getDocument();
32311
+ var trustedHTMLHandler = editor.getDOMCreator();
32312
+ var fragment = parseHtmlToFragment(rawHtml, doc, trustedHTMLHandler);
32313
+ return isThinWrapperOfPlainText(fragment, text);
32314
+ }
32315
+ return false;
32316
+ }
32317
+ exports.isPastedContentMarkdown = isPastedContentMarkdown;
32318
+ function isThinWrapperOfPlainText(fragment, text) {
32319
+ var elements = fragment.querySelectorAll('*');
32320
+ for (var i = 0; i < elements.length; i++) {
32321
+ var element = elements[i];
32322
+ if (!ThinWrapperTags.has(element.tagName)) {
32323
+ return false;
32324
+ }
32325
+ for (var j = 0; j < element.attributes.length; j++) {
32326
+ var attr = element.attributes[j];
32327
+ if (!AllowedAttributes.has(attr.name) && !attr.name.startsWith('data-')) {
32328
+ return false;
32329
+ }
32330
+ }
32331
+ }
32332
+ return removeWhitespace(fragment.textContent || '') === removeWhitespace(text);
32333
+ }
32334
+ function removeWhitespace(text) {
32335
+ return text.replace(/\s/g, '');
32336
+ }
32337
+ function parseHtmlToFragment(html, doc, trustedHTMLHandler) {
32338
+ var parsedDoc = trustedHTMLHandler.htmlToDOM(html);
32339
+ var fragment = doc.createDocumentFragment();
32340
+ var body = parsedDoc === null || parsedDoc === void 0 ? void 0 : parsedDoc.body;
32341
+ if (body) {
32342
+ while (body.firstChild) {
32343
+ fragment.appendChild(body.firstChild);
32344
+ }
32345
+ }
32346
+ return fragment;
32347
+ }
32348
+
32349
+
31904
32350
  /***/ },
31905
32351
 
31906
32352
  /***/ "./packages/roosterjs-content-model-plugins/lib/announce/AnnouncePlugin.ts"
@@ -37665,16 +38111,31 @@ function createImageWrapper(editor, image, options, editInfo, htmlOptions, opera
37665
38111
  croppers = (0, createImageCropper_1.createImageCropper)(doc);
37666
38112
  }
37667
38113
  var wrapper = createWrapper(editor, imageClone, options, editInfo, resizers, rotators, croppers);
38114
+ // Capture the image footprint before attaching the shadow root, since the light-DOM image
38115
+ // stops being rendered once the shadow root takes over.
38116
+ var imageWidth = image.offsetWidth;
38117
+ var imageHeight = image.offsetHeight;
37668
38118
  var imageSpan = (0, roosterjs_content_model_dom_1.wrap)(doc, image, 'span');
37669
- var shadowSpan = createShadowSpan(wrapper, imageSpan);
38119
+ var shadowSpan = createShadowSpan(wrapper, imageSpan, imageWidth, imageHeight);
37670
38120
  return { wrapper: wrapper, shadowSpan: shadowSpan, imageClone: imageClone, resizers: resizers, rotators: rotators, croppers: croppers };
37671
38121
  }
37672
38122
  exports.createImageWrapper = createImageWrapper;
37673
- var createShadowSpan = function (wrapper, imageSpan) {
38123
+ var createShadowSpan = function (wrapper, imageSpan, imageWidth, imageHeight) {
37674
38124
  var shadowRoot = imageSpan.attachShadow({
37675
38125
  mode: 'open',
37676
38126
  });
37677
38127
  imageSpan.id = IMAGE_EDIT_SHADOW_ROOT;
38128
+ // Pin the shadow host to the original image's box so that wrapping the image does not grow the
38129
+ // surrounding line box. Without this, the taller edit wrapper enlarges the line and the browser
38130
+ // scrolls the selection back into view, making the editor jump. The wrapper and handles still
38131
+ // overflow the host visually because overflow is left visible.
38132
+ if (imageWidth > 0 && imageHeight > 0) {
38133
+ imageSpan.style.display = 'inline-block';
38134
+ imageSpan.style.width = imageWidth + "px";
38135
+ imageSpan.style.height = imageHeight + "px";
38136
+ imageSpan.style.verticalAlign = 'bottom';
38137
+ imageSpan.style.overflow = 'visible';
38138
+ }
37678
38139
  shadowRoot.appendChild(wrapper);
37679
38140
  return imageSpan;
37680
38141
  };