roosterjs 9.53.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-adapter-amd-min.js +1 -1
- package/dist/rooster-adapter-amd-min.js.map +1 -1
- package/dist/rooster-adapter-amd.js +2 -0
- package/dist/rooster-adapter-amd.js.map +1 -1
- package/dist/rooster-adapter-min.js +1 -1
- package/dist/rooster-adapter-min.js.map +1 -1
- package/dist/rooster-adapter.js +2 -0
- package/dist/rooster-adapter.js.map +1 -1
- package/dist/rooster-amd-min.js +1 -1
- package/dist/rooster-amd-min.js.map +1 -1
- package/dist/rooster-amd.d.ts +99 -4
- package/dist/rooster-amd.js +362 -10
- package/dist/rooster-amd.js.map +1 -1
- package/dist/rooster-min.js +1 -1
- package/dist/rooster-min.js.map +1 -1
- package/dist/rooster-react.js +92 -92
- package/dist/rooster-react.js.map +1 -1
- package/dist/rooster.d.ts +99 -4
- package/dist/rooster.js +362 -10
- package/dist/rooster.js.map +1 -1
- package/package.json +7 -7
package/dist/rooster.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Type definitions for roosterjs (Version 9.
|
|
1
|
+
// Type definitions for roosterjs (Version 9.54.0)
|
|
2
2
|
// Generated by dts tool from roosterjs
|
|
3
3
|
// Project: https://github.com/Microsoft/roosterjs
|
|
4
4
|
|
|
@@ -1512,7 +1512,11 @@ type PasteType = /**
|
|
|
1512
1512
|
/**
|
|
1513
1513
|
* If there is a image uri in the clipboard, paste the content as image element
|
|
1514
1514
|
*/
|
|
1515
|
-
| 'asImage'
|
|
1515
|
+
| 'asImage'
|
|
1516
|
+
/**
|
|
1517
|
+
* If the editor includes a markdown plugin @see MarkdownPastePlugin, and there is markdown content in the clipboard, paste it as markdown
|
|
1518
|
+
*/
|
|
1519
|
+
| 'asMarkdown';
|
|
1516
1520
|
|
|
1517
1521
|
/**
|
|
1518
1522
|
* All Border operations
|
|
@@ -2884,6 +2888,13 @@ interface DomToModelSettings {
|
|
|
2884
2888
|
* If true elements that has display:none style will be processed
|
|
2885
2889
|
*/
|
|
2886
2890
|
processNonVisibleElements?: boolean;
|
|
2891
|
+
/**
|
|
2892
|
+
* When set to true, if a container element could be represented by a FormatContainer, always keep the
|
|
2893
|
+
* FormatContainer and never fall back to a paragraph, even when it only has a single child.
|
|
2894
|
+
* Set this when the intermediate FormatContainer is persisted during DOM to Content Model conversion
|
|
2895
|
+
* and is later used during formatting.
|
|
2896
|
+
*/
|
|
2897
|
+
skipFormatContainerFallbackCheck?: boolean;
|
|
2887
2898
|
}
|
|
2888
2899
|
|
|
2889
2900
|
/**
|
|
@@ -3304,6 +3315,13 @@ interface DomToModelOptionForCreateModel extends DomToModelOption {
|
|
|
3304
3315
|
* When this option is passed, "tryGetFromCache" will be ignored.
|
|
3305
3316
|
*/
|
|
3306
3317
|
recalculateTableSize?: boolean | 'all' | 'selected' | 'none';
|
|
3318
|
+
/**
|
|
3319
|
+
* When set to true, if a container element could be represented by a FormatContainer, always keep the
|
|
3320
|
+
* FormatContainer and never fall back to a paragraph, even when it only has a single child.
|
|
3321
|
+
* Set this when the intermediate FormatContainer is persisted during DOM to Content Model conversion
|
|
3322
|
+
* and is later used during formatting.
|
|
3323
|
+
*/
|
|
3324
|
+
skipFormatContainerFallbackCheck?: boolean;
|
|
3307
3325
|
}
|
|
3308
3326
|
|
|
3309
3327
|
/**
|
|
@@ -3413,6 +3431,14 @@ interface DomIndexer {
|
|
|
3413
3431
|
* @returns True if successfully updated, otherwise false
|
|
3414
3432
|
*/
|
|
3415
3433
|
reconcileElementId: (element: HTMLElement) => boolean;
|
|
3434
|
+
/**
|
|
3435
|
+
* When src or a data-* attribute is changed on an indexed IMG element, update the related
|
|
3436
|
+
* ContentModelImage (src property or dataset) so the cached model stays valid.
|
|
3437
|
+
* @param element The element that has an attribute changed
|
|
3438
|
+
* @param attributeName The name of the changed attribute
|
|
3439
|
+
* @returns True if successfully updated, otherwise false
|
|
3440
|
+
*/
|
|
3441
|
+
reconcileImageAttribute: (element: HTMLElement, attributeName: string) => boolean;
|
|
3416
3442
|
/**
|
|
3417
3443
|
* When child list of editor content is changed, we can use this method to do sync the change from editor into content model.
|
|
3418
3444
|
* This is mostly used when user start to type in an empty line. In that case browser will remove the existing BR node in the empty line if any,
|
|
@@ -6336,7 +6362,7 @@ interface BeforePasteEvent extends BasePluginEvent<'beforePaste'> {
|
|
|
6336
6362
|
*/
|
|
6337
6363
|
readonly htmlAttributes: Record<string, string>;
|
|
6338
6364
|
/**
|
|
6339
|
-
* Paste type option (as plain text, merge format, normal, as image)
|
|
6365
|
+
* Paste type option (as plain text, merge format, normal, as image, asMarkdown (@see MarkdownPastePlugin ))
|
|
6340
6366
|
*/
|
|
6341
6367
|
readonly pasteType: PasteType;
|
|
6342
6368
|
/**
|
|
@@ -7668,7 +7694,7 @@ function createDomToModelContext(editorContext?: EditorContext, ...options: (Dom
|
|
|
7668
7694
|
* @param config A full config object to define how to convert DOM tree to Content Model
|
|
7669
7695
|
* @param editorContext Context of editor
|
|
7670
7696
|
*/
|
|
7671
|
-
function createDomToModelContextWithConfig(config: DomToModelSettings, editorContext?: EditorContext):
|
|
7697
|
+
function createDomToModelContextWithConfig(config: DomToModelSettings, editorContext?: EditorContext): DomToModelContext;
|
|
7672
7698
|
|
|
7673
7699
|
/**
|
|
7674
7700
|
* Create Dom to Content Model Config object
|
|
@@ -10919,6 +10945,23 @@ function convertMarkdownToContentModel(text: string, options?: MarkdownToModelOp
|
|
|
10919
10945
|
*/
|
|
10920
10946
|
function convertContentModelToMarkdown(model: ContentModelDocument, newLine?: MarkdownLineBreaks): string;
|
|
10921
10947
|
|
|
10948
|
+
/**
|
|
10949
|
+
* Detect whether the given plain text contains any markdown markup.
|
|
10950
|
+
* Recognizes block-level patterns (headings, blockquotes, lists, horizontal rules, tables)
|
|
10951
|
+
* and inline patterns (bold, italic, strikethrough, links, images).
|
|
10952
|
+
* @param text The plain text to check.
|
|
10953
|
+
* @returns True if the text contains any markdown markup, false otherwise.
|
|
10954
|
+
*/
|
|
10955
|
+
function isContentMarkdown(text: string): boolean;
|
|
10956
|
+
|
|
10957
|
+
/**
|
|
10958
|
+
* Detect whether the given clipboard content can be interpreted as markdown.
|
|
10959
|
+
* @param editor The editor instance.
|
|
10960
|
+
* @param clipboardData The clipboard data to check.
|
|
10961
|
+
* @returns True if the content can be interpreted as markdown, false otherwise.
|
|
10962
|
+
*/
|
|
10963
|
+
function isPastedContentMarkdown(editor: IEditor, clipboardData: ClipboardData): boolean;
|
|
10964
|
+
|
|
10922
10965
|
/**
|
|
10923
10966
|
* The characters to add line breaks and new lines
|
|
10924
10967
|
*/
|
|
@@ -10950,4 +10993,56 @@ interface MarkdownToModelOptions {
|
|
|
10950
10993
|
direction?: 'ltr' | 'rtl' | undefined;
|
|
10951
10994
|
}
|
|
10952
10995
|
|
|
10996
|
+
/**
|
|
10997
|
+
* Markdown paste plugin. Handles the BeforePaste event and, when the pasted content
|
|
10998
|
+
* can be interpreted as markdown, converts the plain text into a Content Model and
|
|
10999
|
+
* pastes it as rich markdown content instead of the original clipboard HTML.
|
|
11000
|
+
*/
|
|
11001
|
+
class MarkdownPastePlugin implements EditorPlugin {
|
|
11002
|
+
private editor;
|
|
11003
|
+
private options;
|
|
11004
|
+
/**
|
|
11005
|
+
* Construct a new instance of MarkdownPastePlugin
|
|
11006
|
+
* @param options Options to control the markdown paste behavior
|
|
11007
|
+
*/
|
|
11008
|
+
constructor(options?: MarkdownPasteOptions);
|
|
11009
|
+
/**
|
|
11010
|
+
* Get name of this plugin
|
|
11011
|
+
*/
|
|
11012
|
+
getName(): string;
|
|
11013
|
+
/**
|
|
11014
|
+
* The first method that editor will call to a plugin when editor is initializing.
|
|
11015
|
+
* It will pass in the editor instance, plugin should take this chance to save the
|
|
11016
|
+
* editor reference so that it can call to any editor method or format API later.
|
|
11017
|
+
* @param editor The editor object
|
|
11018
|
+
*/
|
|
11019
|
+
initialize(editor: IEditor): void;
|
|
11020
|
+
/**
|
|
11021
|
+
* The last method that editor will call to a plugin before it is disposed.
|
|
11022
|
+
* Plugin can take this chance to clear the reference to editor. After this method is
|
|
11023
|
+
* called, plugin should not call to any editor method since it will result in error.
|
|
11024
|
+
*/
|
|
11025
|
+
dispose(): void;
|
|
11026
|
+
/**
|
|
11027
|
+
* Core method for a plugin. Once an event happens in editor, editor will call this
|
|
11028
|
+
* method of each plugin to handle the event as long as the event is not handled
|
|
11029
|
+
* exclusively by another plugin.
|
|
11030
|
+
* @param event The event to handle:
|
|
11031
|
+
*/
|
|
11032
|
+
onPluginEvent(event: PluginEvent): void;
|
|
11033
|
+
}
|
|
11034
|
+
|
|
11035
|
+
/**
|
|
11036
|
+
* Options for MarkdownPastePlugin
|
|
11037
|
+
*/
|
|
11038
|
+
interface MarkdownPasteOptions {
|
|
11039
|
+
/**
|
|
11040
|
+
* When true, content that can be interpreted as markdown is automatically converted
|
|
11041
|
+
* into rich content on every paste, without requiring an explicit "Paste as Markdown"
|
|
11042
|
+
* command.
|
|
11043
|
+
* @default false
|
|
11044
|
+
*/
|
|
11045
|
+
autoConversion: boolean;
|
|
11046
|
+
}
|
|
11047
|
+
|
|
10953
11048
|
}
|
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;
|
|
@@ -10104,6 +10113,9 @@ var createContentModel = function (core, option, selectionOverride) {
|
|
|
10104
10113
|
var domToModelContext = option
|
|
10105
10114
|
? (0, roosterjs_content_model_dom_1.createDomToModelContext)(editorContext, settings.builtIn, settings.customized, option)
|
|
10106
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
|
+
}
|
|
10107
10119
|
if (selection) {
|
|
10108
10120
|
domToModelContext.selection = selection;
|
|
10109
10121
|
}
|
|
@@ -11575,6 +11587,11 @@ var CachePlugin = /** @class */ (function () {
|
|
|
11575
11587
|
_this.invalidateCache();
|
|
11576
11588
|
}
|
|
11577
11589
|
break;
|
|
11590
|
+
case 'attribute':
|
|
11591
|
+
if (!_this.state.domIndexer.reconcileImageAttribute(mutation.element, mutation.attributeName)) {
|
|
11592
|
+
_this.invalidateCache();
|
|
11593
|
+
}
|
|
11594
|
+
break;
|
|
11578
11595
|
case 'unknown':
|
|
11579
11596
|
_this.invalidateCache();
|
|
11580
11597
|
break;
|
|
@@ -12106,6 +12123,33 @@ var DomIndexerImpl = /** @class */ (function () {
|
|
|
12106
12123
|
return false;
|
|
12107
12124
|
}
|
|
12108
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
|
+
};
|
|
12109
12153
|
DomIndexerImpl.prototype.onBlockEntityDelimiter = function (node, entity, parent) {
|
|
12110
12154
|
if ((0, roosterjs_content_model_dom_1.isNodeOfType)(node, 'ELEMENT_NODE') && (0, roosterjs_content_model_dom_1.isEntityDelimiter)(node) && node.firstChild) {
|
|
12111
12155
|
var indexedDelimiter = node.firstChild;
|
|
@@ -12421,6 +12465,16 @@ var TextMutationObserverImpl = /** @class */ (function () {
|
|
|
12421
12465
|
(0, roosterjs_content_model_dom_1.isNodeOfType)(target, 'ELEMENT_NODE')) {
|
|
12422
12466
|
_this.onMutation({ type: 'elementId', element: target });
|
|
12423
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
|
+
}
|
|
12424
12478
|
else {
|
|
12425
12479
|
// We cannot handle attributes changes on editor content for now
|
|
12426
12480
|
canHandle = false;
|
|
@@ -18137,7 +18191,9 @@ var formatContainerProcessorInternal = function (group, element, context, forceF
|
|
|
18137
18191
|
if (element.style.fontSize && parseInt(element.style.fontSize) == 0) {
|
|
18138
18192
|
formatContainer.zeroFontSize = true;
|
|
18139
18193
|
}
|
|
18140
|
-
if (
|
|
18194
|
+
if (!context.skipFormatContainerFallbackCheck &&
|
|
18195
|
+
shouldFallbackToParagraph(formatContainer) &&
|
|
18196
|
+
!forceFormatContainer) {
|
|
18141
18197
|
// For DIV container that only has one paragraph child, container style can be merged into paragraph
|
|
18142
18198
|
// and no need to have this container
|
|
18143
18199
|
var paragraph = formatContainer.blocks[0];
|
|
@@ -28674,6 +28730,7 @@ function internalIterateSelections(path, callback, option, table, treatAllAsSele
|
|
|
28674
28730
|
|
|
28675
28731
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
28676
28732
|
exports.setSelection = void 0;
|
|
28733
|
+
var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
|
|
28677
28734
|
var isGeneralSegment_1 = __webpack_require__(/*! ../typeCheck/isGeneralSegment */ "./packages/roosterjs-content-model-dom/lib/modelApi/typeCheck/isGeneralSegment.ts");
|
|
28678
28735
|
var mutate_1 = __webpack_require__(/*! ../common/mutate */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/mutate.ts");
|
|
28679
28736
|
/**
|
|
@@ -28711,6 +28768,7 @@ function setSelectionToBlockGroup(group, isInSelection, start, end) {
|
|
|
28711
28768
|
});
|
|
28712
28769
|
}
|
|
28713
28770
|
function setSelectionToBlock(block, isInSelection, start, end) {
|
|
28771
|
+
var _a;
|
|
28714
28772
|
switch (block.blockType) {
|
|
28715
28773
|
case 'BlockGroup':
|
|
28716
28774
|
return setSelectionToBlockGroup(block, isInSelection, start, end);
|
|
@@ -28731,16 +28789,31 @@ function setSelectionToBlock(block, isInSelection, start, end) {
|
|
|
28731
28789
|
return isInSelection;
|
|
28732
28790
|
});
|
|
28733
28791
|
case 'Paragraph':
|
|
28734
|
-
var
|
|
28792
|
+
var state_1 = {
|
|
28793
|
+
segmentsToDelete: [],
|
|
28794
|
+
boundaryMarkers: [],
|
|
28795
|
+
hasSelectedNonMarker: false,
|
|
28796
|
+
};
|
|
28735
28797
|
block.segments.forEach(function (segment, i) {
|
|
28736
28798
|
isInSelection = handleSelection(isInSelection, segment, start, end, function (isInSelection) {
|
|
28737
|
-
return setSelectionToSegment(block, segment, isInSelection,
|
|
28799
|
+
return setSelectionToSegment(block, segment, isInSelection, state_1, start, end, i);
|
|
28738
28800
|
});
|
|
28739
28801
|
});
|
|
28740
|
-
if (
|
|
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) {
|
|
28741
28811
|
var mutablePara = (0, mutate_1.mutateBlock)(block);
|
|
28742
28812
|
var index = void 0;
|
|
28743
|
-
|
|
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) {
|
|
28744
28817
|
if (index >= 0) {
|
|
28745
28818
|
mutablePara.segments.splice(index, 1);
|
|
28746
28819
|
}
|
|
@@ -28789,14 +28862,23 @@ function findCell(table, cell) {
|
|
|
28789
28862
|
: -1;
|
|
28790
28863
|
return { row: row, col: col };
|
|
28791
28864
|
}
|
|
28792
|
-
function setSelectionToSegment(paragraph, segment, isInSelection,
|
|
28865
|
+
function setSelectionToSegment(paragraph, segment, isInSelection, state, start, end, i) {
|
|
28866
|
+
if (segment.segmentType != 'SelectionMarker' && isInSelection) {
|
|
28867
|
+
state.hasSelectedNonMarker = true;
|
|
28868
|
+
}
|
|
28793
28869
|
switch (segment.segmentType) {
|
|
28794
28870
|
case 'SelectionMarker':
|
|
28795
28871
|
if (!isInSelection || (segment != start && segment != end)) {
|
|
28796
28872
|
// Delete the selection marker when
|
|
28797
28873
|
// 1. It is not in selection any more. Or
|
|
28798
28874
|
// 2. It is in middle of selection, so no need to have it
|
|
28799
|
-
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);
|
|
28800
28882
|
}
|
|
28801
28883
|
return isInSelection;
|
|
28802
28884
|
case 'General':
|
|
@@ -29237,7 +29319,7 @@ var handleBlockGroupChildren = function (doc, parent, group, context) {
|
|
|
29237
29319
|
(_a = context.domIndexer) === null || _a === void 0 ? void 0 : _a.onBlockEntity(childBlock, group);
|
|
29238
29320
|
}
|
|
29239
29321
|
});
|
|
29240
|
-
cleanUpNodeStack(listFormat.nodeStack, context);
|
|
29322
|
+
cleanUpNodeStack(listFormat.nodeStack, context, parent);
|
|
29241
29323
|
// Remove all rest node if any since they don't appear in content model
|
|
29242
29324
|
(0, cleanUpRestNodes_1.cleanUpRestNodes)(refNode, context.rewriteFromModel);
|
|
29243
29325
|
}
|
|
@@ -29246,7 +29328,7 @@ var handleBlockGroupChildren = function (doc, parent, group, context) {
|
|
|
29246
29328
|
}
|
|
29247
29329
|
};
|
|
29248
29330
|
exports.handleBlockGroupChildren = handleBlockGroupChildren;
|
|
29249
|
-
function cleanUpNodeStack(nodeStack, context) {
|
|
29331
|
+
function cleanUpNodeStack(nodeStack, context, leavingParent) {
|
|
29250
29332
|
var _a, _b;
|
|
29251
29333
|
if (nodeStack.length > 0) {
|
|
29252
29334
|
// Clear list stack, only run to nodeStack[1] because nodeStack[0] is the parent node
|
|
@@ -29254,6 +29336,13 @@ function cleanUpNodeStack(nodeStack, context) {
|
|
|
29254
29336
|
var node = (_b = (_a = nodeStack.pop()) === null || _a === void 0 ? void 0 : _a.refNode) !== null && _b !== void 0 ? _b : null;
|
|
29255
29337
|
(0, cleanUpRestNodes_1.cleanUpRestNodes)(node, context.rewriteFromModel);
|
|
29256
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
|
+
}
|
|
29257
29346
|
}
|
|
29258
29347
|
}
|
|
29259
29348
|
|
|
@@ -30642,11 +30731,17 @@ exports.MarkdownHeadings = {
|
|
|
30642
30731
|
"use strict";
|
|
30643
30732
|
|
|
30644
30733
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
30645
|
-
exports.convertContentModelToMarkdown = exports.convertMarkdownToContentModel = void 0;
|
|
30734
|
+
exports.MarkdownPastePlugin = exports.isPastedContentMarkdown = exports.isContentMarkdown = exports.convertContentModelToMarkdown = exports.convertMarkdownToContentModel = void 0;
|
|
30646
30735
|
var convertMarkdownToContentModel_1 = __webpack_require__(/*! ./markdownToModel/convertMarkdownToContentModel */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts");
|
|
30647
30736
|
Object.defineProperty(exports, "convertMarkdownToContentModel", ({ enumerable: true, get: function () { return convertMarkdownToContentModel_1.convertMarkdownToContentModel; } }));
|
|
30648
30737
|
var convertContentModelToMarkdown_1 = __webpack_require__(/*! ./modelToMarkdown/convertContentModelToMarkdown */ "./packages/roosterjs-content-model-markdown/lib/modelToMarkdown/convertContentModelToMarkdown.ts");
|
|
30649
30738
|
Object.defineProperty(exports, "convertContentModelToMarkdown", ({ enumerable: true, get: function () { return convertContentModelToMarkdown_1.convertContentModelToMarkdown; } }));
|
|
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; } }));
|
|
30650
30745
|
|
|
30651
30746
|
|
|
30652
30747
|
/***/ },
|
|
@@ -31336,6 +31431,13 @@ function parseInlineSegments(text, segments, state, link) {
|
|
|
31336
31431
|
};
|
|
31337
31432
|
while (i < text.length) {
|
|
31338
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
|
+
}
|
|
31339
31441
|
// Image: 
|
|
31340
31442
|
var imgMatch = imagePattern.exec(remaining);
|
|
31341
31443
|
if (imgMatch && isValidUrl(imgMatch[2])) {
|
|
@@ -31399,6 +31501,10 @@ function shouldToggleFormatting(text, index, marker, currentState) {
|
|
|
31399
31501
|
function isWhitespace(char) {
|
|
31400
31502
|
return /\s/.test(char);
|
|
31401
31503
|
}
|
|
31504
|
+
function isEscapable(char) {
|
|
31505
|
+
// Per CommonMark, any ASCII punctuation character may be backslash-escaped.
|
|
31506
|
+
return /[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/.test(char);
|
|
31507
|
+
}
|
|
31402
31508
|
function toggleFormatting(state, type) {
|
|
31403
31509
|
switch (type) {
|
|
31404
31510
|
case 'bold':
|
|
@@ -31902,6 +32008,252 @@ function modelProcessor(model, newLine) {
|
|
|
31902
32008
|
exports.modelProcessor = modelProcessor;
|
|
31903
32009
|
|
|
31904
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
|
+
|
|
31905
32257
|
/***/ },
|
|
31906
32258
|
|
|
31907
32259
|
/***/ "./packages/roosterjs-content-model-plugins/lib/announce/AnnouncePlugin.ts"
|