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-amd.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
|
|
|
@@ -1511,7 +1511,11 @@ export type PasteType = /**
|
|
|
1511
1511
|
/**
|
|
1512
1512
|
* If there is a image uri in the clipboard, paste the content as image element
|
|
1513
1513
|
*/
|
|
1514
|
-
| 'asImage'
|
|
1514
|
+
| 'asImage'
|
|
1515
|
+
/**
|
|
1516
|
+
* If the editor includes a markdown plugin @see MarkdownPastePlugin, and there is markdown content in the clipboard, paste it as markdown
|
|
1517
|
+
*/
|
|
1518
|
+
| 'asMarkdown';
|
|
1515
1519
|
|
|
1516
1520
|
/**
|
|
1517
1521
|
* All Border operations
|
|
@@ -2883,6 +2887,13 @@ export interface DomToModelSettings {
|
|
|
2883
2887
|
* If true elements that has display:none style will be processed
|
|
2884
2888
|
*/
|
|
2885
2889
|
processNonVisibleElements?: boolean;
|
|
2890
|
+
/**
|
|
2891
|
+
* When set to true, if a container element could be represented by a FormatContainer, always keep the
|
|
2892
|
+
* FormatContainer and never fall back to a paragraph, even when it only has a single child.
|
|
2893
|
+
* Set this when the intermediate FormatContainer is persisted during DOM to Content Model conversion
|
|
2894
|
+
* and is later used during formatting.
|
|
2895
|
+
*/
|
|
2896
|
+
skipFormatContainerFallbackCheck?: boolean;
|
|
2886
2897
|
}
|
|
2887
2898
|
|
|
2888
2899
|
/**
|
|
@@ -3303,6 +3314,13 @@ export interface DomToModelOptionForCreateModel extends DomToModelOption {
|
|
|
3303
3314
|
* When this option is passed, "tryGetFromCache" will be ignored.
|
|
3304
3315
|
*/
|
|
3305
3316
|
recalculateTableSize?: boolean | 'all' | 'selected' | 'none';
|
|
3317
|
+
/**
|
|
3318
|
+
* When set to true, if a container element could be represented by a FormatContainer, always keep the
|
|
3319
|
+
* FormatContainer and never fall back to a paragraph, even when it only has a single child.
|
|
3320
|
+
* Set this when the intermediate FormatContainer is persisted during DOM to Content Model conversion
|
|
3321
|
+
* and is later used during formatting.
|
|
3322
|
+
*/
|
|
3323
|
+
skipFormatContainerFallbackCheck?: boolean;
|
|
3306
3324
|
}
|
|
3307
3325
|
|
|
3308
3326
|
/**
|
|
@@ -3412,6 +3430,14 @@ export interface DomIndexer {
|
|
|
3412
3430
|
* @returns True if successfully updated, otherwise false
|
|
3413
3431
|
*/
|
|
3414
3432
|
reconcileElementId: (element: HTMLElement) => boolean;
|
|
3433
|
+
/**
|
|
3434
|
+
* When src or a data-* attribute is changed on an indexed IMG element, update the related
|
|
3435
|
+
* ContentModelImage (src property or dataset) so the cached model stays valid.
|
|
3436
|
+
* @param element The element that has an attribute changed
|
|
3437
|
+
* @param attributeName The name of the changed attribute
|
|
3438
|
+
* @returns True if successfully updated, otherwise false
|
|
3439
|
+
*/
|
|
3440
|
+
reconcileImageAttribute: (element: HTMLElement, attributeName: string) => boolean;
|
|
3415
3441
|
/**
|
|
3416
3442
|
* When child list of editor content is changed, we can use this method to do sync the change from editor into content model.
|
|
3417
3443
|
* 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,
|
|
@@ -6335,7 +6361,7 @@ export interface BeforePasteEvent extends BasePluginEvent<'beforePaste'> {
|
|
|
6335
6361
|
*/
|
|
6336
6362
|
readonly htmlAttributes: Record<string, string>;
|
|
6337
6363
|
/**
|
|
6338
|
-
* Paste type option (as plain text, merge format, normal, as image)
|
|
6364
|
+
* Paste type option (as plain text, merge format, normal, as image, asMarkdown (@see MarkdownPastePlugin ))
|
|
6339
6365
|
*/
|
|
6340
6366
|
readonly pasteType: PasteType;
|
|
6341
6367
|
/**
|
|
@@ -7667,7 +7693,7 @@ export function createDomToModelContext(editorContext?: EditorContext, ...option
|
|
|
7667
7693
|
* @param config A full config object to define how to convert DOM tree to Content Model
|
|
7668
7694
|
* @param editorContext Context of editor
|
|
7669
7695
|
*/
|
|
7670
|
-
export function createDomToModelContextWithConfig(config: DomToModelSettings, editorContext?: EditorContext):
|
|
7696
|
+
export function createDomToModelContextWithConfig(config: DomToModelSettings, editorContext?: EditorContext): DomToModelContext;
|
|
7671
7697
|
|
|
7672
7698
|
/**
|
|
7673
7699
|
* Create Dom to Content Model Config object
|
|
@@ -10918,6 +10944,23 @@ export function convertMarkdownToContentModel(text: string, options?: MarkdownTo
|
|
|
10918
10944
|
*/
|
|
10919
10945
|
export function convertContentModelToMarkdown(model: ContentModelDocument, newLine?: MarkdownLineBreaks): string;
|
|
10920
10946
|
|
|
10947
|
+
/**
|
|
10948
|
+
* Detect whether the given plain text contains any markdown markup.
|
|
10949
|
+
* Recognizes block-level patterns (headings, blockquotes, lists, horizontal rules, tables)
|
|
10950
|
+
* and inline patterns (bold, italic, strikethrough, links, images).
|
|
10951
|
+
* @param text The plain text to check.
|
|
10952
|
+
* @returns True if the text contains any markdown markup, false otherwise.
|
|
10953
|
+
*/
|
|
10954
|
+
export function isContentMarkdown(text: string): boolean;
|
|
10955
|
+
|
|
10956
|
+
/**
|
|
10957
|
+
* Detect whether the given clipboard content can be interpreted as markdown.
|
|
10958
|
+
* @param editor The editor instance.
|
|
10959
|
+
* @param clipboardData The clipboard data to check.
|
|
10960
|
+
* @returns True if the content can be interpreted as markdown, false otherwise.
|
|
10961
|
+
*/
|
|
10962
|
+
export function isPastedContentMarkdown(editor: IEditor, clipboardData: ClipboardData): boolean;
|
|
10963
|
+
|
|
10921
10964
|
/**
|
|
10922
10965
|
* The characters to add line breaks and new lines
|
|
10923
10966
|
*/
|
|
@@ -10949,3 +10992,55 @@ export interface MarkdownToModelOptions {
|
|
|
10949
10992
|
direction?: 'ltr' | 'rtl' | undefined;
|
|
10950
10993
|
}
|
|
10951
10994
|
|
|
10995
|
+
/**
|
|
10996
|
+
* Markdown paste plugin. Handles the BeforePaste event and, when the pasted content
|
|
10997
|
+
* can be interpreted as markdown, converts the plain text into a Content Model and
|
|
10998
|
+
* pastes it as rich markdown content instead of the original clipboard HTML.
|
|
10999
|
+
*/
|
|
11000
|
+
export class MarkdownPastePlugin implements EditorPlugin {
|
|
11001
|
+
private editor;
|
|
11002
|
+
private options;
|
|
11003
|
+
/**
|
|
11004
|
+
* Construct a new instance of MarkdownPastePlugin
|
|
11005
|
+
* @param options Options to control the markdown paste behavior
|
|
11006
|
+
*/
|
|
11007
|
+
constructor(options?: MarkdownPasteOptions);
|
|
11008
|
+
/**
|
|
11009
|
+
* Get name of this plugin
|
|
11010
|
+
*/
|
|
11011
|
+
getName(): string;
|
|
11012
|
+
/**
|
|
11013
|
+
* The first method that editor will call to a plugin when editor is initializing.
|
|
11014
|
+
* It will pass in the editor instance, plugin should take this chance to save the
|
|
11015
|
+
* editor reference so that it can call to any editor method or format API later.
|
|
11016
|
+
* @param editor The editor object
|
|
11017
|
+
*/
|
|
11018
|
+
initialize(editor: IEditor): void;
|
|
11019
|
+
/**
|
|
11020
|
+
* The last method that editor will call to a plugin before it is disposed.
|
|
11021
|
+
* Plugin can take this chance to clear the reference to editor. After this method is
|
|
11022
|
+
* called, plugin should not call to any editor method since it will result in error.
|
|
11023
|
+
*/
|
|
11024
|
+
dispose(): void;
|
|
11025
|
+
/**
|
|
11026
|
+
* Core method for a plugin. Once an event happens in editor, editor will call this
|
|
11027
|
+
* method of each plugin to handle the event as long as the event is not handled
|
|
11028
|
+
* exclusively by another plugin.
|
|
11029
|
+
* @param event The event to handle:
|
|
11030
|
+
*/
|
|
11031
|
+
onPluginEvent(event: PluginEvent): void;
|
|
11032
|
+
}
|
|
11033
|
+
|
|
11034
|
+
/**
|
|
11035
|
+
* Options for MarkdownPastePlugin
|
|
11036
|
+
*/
|
|
11037
|
+
export interface MarkdownPasteOptions {
|
|
11038
|
+
/**
|
|
11039
|
+
* When true, content that can be interpreted as markdown is automatically converted
|
|
11040
|
+
* into rich content on every paste, without requiring an explicit "Paste as Markdown"
|
|
11041
|
+
* command.
|
|
11042
|
+
* @default false
|
|
11043
|
+
*/
|
|
11044
|
+
autoConversion: boolean;
|
|
11045
|
+
}
|
|
11046
|
+
|
package/dist/rooster-amd.js
CHANGED
|
@@ -8019,6 +8019,15 @@ function formatInsertPointWithContentModel(editor, insertPoint, callback, option
|
|
|
8019
8019
|
textWithSelection: getShadowTextProcessor(bundle),
|
|
8020
8020
|
},
|
|
8021
8021
|
tryGetFromCache: false,
|
|
8022
|
+
// When an element carries "container level" styles such as margin or padding, we first
|
|
8023
|
+
// wrap it in a FormatContainer. After all its child nodes are processed, we decide whether
|
|
8024
|
+
// to keep the FormatContainer or fall back to a plain paragraph when it only wraps a single
|
|
8025
|
+
// paragraph. However, formatInsertPointWithContentModel persists the Content Model group path
|
|
8026
|
+
// during processing so the later formatting callback can still use it (see the
|
|
8027
|
+
// DomToModelContextWithPath interface below). If the FormatContainer falls back to a paragraph,
|
|
8028
|
+
// it is removed from the model and the persisted path becomes invalid. To keep the path valid,
|
|
8029
|
+
// we skip the fallback check here and always keep the FormatContainer when one is needed.
|
|
8030
|
+
skipFormatContainerFallbackCheck: true,
|
|
8022
8031
|
});
|
|
8023
8032
|
}
|
|
8024
8033
|
exports.formatInsertPointWithContentModel = formatInsertPointWithContentModel;
|
|
@@ -10103,6 +10112,9 @@ var createContentModel = function (core, option, selectionOverride) {
|
|
|
10103
10112
|
var domToModelContext = option
|
|
10104
10113
|
? (0, roosterjs_content_model_dom_1.createDomToModelContext)(editorContext, settings.builtIn, settings.customized, option)
|
|
10105
10114
|
: (0, roosterjs_content_model_dom_1.createDomToModelContextWithConfig)(settings.calculated, editorContext);
|
|
10115
|
+
if (option === null || option === void 0 ? void 0 : option.skipFormatContainerFallbackCheck) {
|
|
10116
|
+
domToModelContext.skipFormatContainerFallbackCheck = true;
|
|
10117
|
+
}
|
|
10106
10118
|
if (selection) {
|
|
10107
10119
|
domToModelContext.selection = selection;
|
|
10108
10120
|
}
|
|
@@ -11574,6 +11586,11 @@ var CachePlugin = /** @class */ (function () {
|
|
|
11574
11586
|
_this.invalidateCache();
|
|
11575
11587
|
}
|
|
11576
11588
|
break;
|
|
11589
|
+
case 'attribute':
|
|
11590
|
+
if (!_this.state.domIndexer.reconcileImageAttribute(mutation.element, mutation.attributeName)) {
|
|
11591
|
+
_this.invalidateCache();
|
|
11592
|
+
}
|
|
11593
|
+
break;
|
|
11577
11594
|
case 'unknown':
|
|
11578
11595
|
_this.invalidateCache();
|
|
11579
11596
|
break;
|
|
@@ -12105,6 +12122,33 @@ var DomIndexerImpl = /** @class */ (function () {
|
|
|
12105
12122
|
return false;
|
|
12106
12123
|
}
|
|
12107
12124
|
};
|
|
12125
|
+
DomIndexerImpl.prototype.reconcileImageAttribute = function (element, attributeName) {
|
|
12126
|
+
var _a, _b;
|
|
12127
|
+
if ((0, roosterjs_content_model_dom_1.isElementOfType)(element, 'img')) {
|
|
12128
|
+
var image = (_a = getIndexedSegmentItem(element)) === null || _a === void 0 ? void 0 : _a.segments[0];
|
|
12129
|
+
if ((image === null || image === void 0 ? void 0 : image.segmentType) == 'Image') {
|
|
12130
|
+
if (attributeName == 'src') {
|
|
12131
|
+
// Use getAttribute('src') instead of retrieving src directly, in case the src
|
|
12132
|
+
// has port and may be stripped by browser. This matches imageProcessor.
|
|
12133
|
+
image.src = (_b = element.getAttribute('src')) !== null && _b !== void 0 ? _b : '';
|
|
12134
|
+
return true;
|
|
12135
|
+
}
|
|
12136
|
+
else if (attributeName.indexOf('data-') == 0) {
|
|
12137
|
+
// A data-* attribute may be added, modified or removed. Rebuild the whole
|
|
12138
|
+
// dataset from DOM to keep it in sync, the same way imageProcessor builds it.
|
|
12139
|
+
var dataset_1 = image.dataset;
|
|
12140
|
+
(0, roosterjs_content_model_dom_1.getObjectKeys)(dataset_1).forEach(function (key) {
|
|
12141
|
+
delete dataset_1[key];
|
|
12142
|
+
});
|
|
12143
|
+
(0, roosterjs_content_model_dom_1.getObjectKeys)(element.dataset).forEach(function (key) {
|
|
12144
|
+
dataset_1[key] = element.dataset[key] || '';
|
|
12145
|
+
});
|
|
12146
|
+
return true;
|
|
12147
|
+
}
|
|
12148
|
+
}
|
|
12149
|
+
}
|
|
12150
|
+
return false;
|
|
12151
|
+
};
|
|
12108
12152
|
DomIndexerImpl.prototype.onBlockEntityDelimiter = function (node, entity, parent) {
|
|
12109
12153
|
if ((0, roosterjs_content_model_dom_1.isNodeOfType)(node, 'ELEMENT_NODE') && (0, roosterjs_content_model_dom_1.isEntityDelimiter)(node) && node.firstChild) {
|
|
12110
12154
|
var indexedDelimiter = node.firstChild;
|
|
@@ -12420,6 +12464,16 @@ var TextMutationObserverImpl = /** @class */ (function () {
|
|
|
12420
12464
|
(0, roosterjs_content_model_dom_1.isNodeOfType)(target, 'ELEMENT_NODE')) {
|
|
12421
12465
|
_this.onMutation({ type: 'elementId', element: target });
|
|
12422
12466
|
}
|
|
12467
|
+
else if (mutation.attributeName &&
|
|
12468
|
+
(0, roosterjs_content_model_dom_1.isNodeOfType)(target, 'ELEMENT_NODE') &&
|
|
12469
|
+
(mutation.attributeName == 'src' ||
|
|
12470
|
+
mutation.attributeName.indexOf('data-') == 0)) {
|
|
12471
|
+
_this.onMutation({
|
|
12472
|
+
type: 'attribute',
|
|
12473
|
+
element: target,
|
|
12474
|
+
attributeName: mutation.attributeName,
|
|
12475
|
+
});
|
|
12476
|
+
}
|
|
12423
12477
|
else {
|
|
12424
12478
|
// We cannot handle attributes changes on editor content for now
|
|
12425
12479
|
canHandle = false;
|
|
@@ -18136,7 +18190,9 @@ var formatContainerProcessorInternal = function (group, element, context, forceF
|
|
|
18136
18190
|
if (element.style.fontSize && parseInt(element.style.fontSize) == 0) {
|
|
18137
18191
|
formatContainer.zeroFontSize = true;
|
|
18138
18192
|
}
|
|
18139
|
-
if (
|
|
18193
|
+
if (!context.skipFormatContainerFallbackCheck &&
|
|
18194
|
+
shouldFallbackToParagraph(formatContainer) &&
|
|
18195
|
+
!forceFormatContainer) {
|
|
18140
18196
|
// For DIV container that only has one paragraph child, container style can be merged into paragraph
|
|
18141
18197
|
// and no need to have this container
|
|
18142
18198
|
var paragraph = formatContainer.blocks[0];
|
|
@@ -28673,6 +28729,7 @@ function internalIterateSelections(path, callback, option, table, treatAllAsSele
|
|
|
28673
28729
|
|
|
28674
28730
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
28675
28731
|
exports.setSelection = void 0;
|
|
28732
|
+
var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
|
|
28676
28733
|
var isGeneralSegment_1 = __webpack_require__(/*! ../typeCheck/isGeneralSegment */ "./packages/roosterjs-content-model-dom/lib/modelApi/typeCheck/isGeneralSegment.ts");
|
|
28677
28734
|
var mutate_1 = __webpack_require__(/*! ../common/mutate */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/mutate.ts");
|
|
28678
28735
|
/**
|
|
@@ -28710,6 +28767,7 @@ function setSelectionToBlockGroup(group, isInSelection, start, end) {
|
|
|
28710
28767
|
});
|
|
28711
28768
|
}
|
|
28712
28769
|
function setSelectionToBlock(block, isInSelection, start, end) {
|
|
28770
|
+
var _a;
|
|
28713
28771
|
switch (block.blockType) {
|
|
28714
28772
|
case 'BlockGroup':
|
|
28715
28773
|
return setSelectionToBlockGroup(block, isInSelection, start, end);
|
|
@@ -28730,16 +28788,31 @@ function setSelectionToBlock(block, isInSelection, start, end) {
|
|
|
28730
28788
|
return isInSelection;
|
|
28731
28789
|
});
|
|
28732
28790
|
case 'Paragraph':
|
|
28733
|
-
var
|
|
28791
|
+
var state_1 = {
|
|
28792
|
+
segmentsToDelete: [],
|
|
28793
|
+
boundaryMarkers: [],
|
|
28794
|
+
hasSelectedNonMarker: false,
|
|
28795
|
+
};
|
|
28734
28796
|
block.segments.forEach(function (segment, i) {
|
|
28735
28797
|
isInSelection = handleSelection(isInSelection, segment, start, end, function (isInSelection) {
|
|
28736
|
-
return setSelectionToSegment(block, segment, isInSelection,
|
|
28798
|
+
return setSelectionToSegment(block, segment, isInSelection, state_1, start, end, i);
|
|
28737
28799
|
});
|
|
28738
28800
|
});
|
|
28739
|
-
if (
|
|
28801
|
+
if (state_1.hasSelectedNonMarker) {
|
|
28802
|
+
// This paragraph contains a real (non-marker) selected segment, so any leading/trailing
|
|
28803
|
+
// selection marker of the range is redundant within this paragraph and can be removed.
|
|
28804
|
+
// We only do this within the same paragraph: a boundary marker at a paragraph edge must be
|
|
28805
|
+
// kept to distinguish "selection starts at the beginning of this line" from "selection
|
|
28806
|
+
// starts at the end of the previous line".
|
|
28807
|
+
(_a = state_1.segmentsToDelete).push.apply(_a, (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(state_1.boundaryMarkers), false));
|
|
28808
|
+
}
|
|
28809
|
+
if (state_1.segmentsToDelete.length > 0) {
|
|
28740
28810
|
var mutablePara = (0, mutate_1.mutateBlock)(block);
|
|
28741
28811
|
var index = void 0;
|
|
28742
|
-
|
|
28812
|
+
// Sort ascending so the pop()-based splice below always removes the highest index first,
|
|
28813
|
+
// keeping the remaining indices valid (boundary markers may sit before queued deletions).
|
|
28814
|
+
state_1.segmentsToDelete.sort(function (a, b) { return a - b; });
|
|
28815
|
+
while ((index = state_1.segmentsToDelete.pop()) !== undefined) {
|
|
28743
28816
|
if (index >= 0) {
|
|
28744
28817
|
mutablePara.segments.splice(index, 1);
|
|
28745
28818
|
}
|
|
@@ -28788,14 +28861,23 @@ function findCell(table, cell) {
|
|
|
28788
28861
|
: -1;
|
|
28789
28862
|
return { row: row, col: col };
|
|
28790
28863
|
}
|
|
28791
|
-
function setSelectionToSegment(paragraph, segment, isInSelection,
|
|
28864
|
+
function setSelectionToSegment(paragraph, segment, isInSelection, state, start, end, i) {
|
|
28865
|
+
if (segment.segmentType != 'SelectionMarker' && isInSelection) {
|
|
28866
|
+
state.hasSelectedNonMarker = true;
|
|
28867
|
+
}
|
|
28792
28868
|
switch (segment.segmentType) {
|
|
28793
28869
|
case 'SelectionMarker':
|
|
28794
28870
|
if (!isInSelection || (segment != start && segment != end)) {
|
|
28795
28871
|
// Delete the selection marker when
|
|
28796
28872
|
// 1. It is not in selection any more. Or
|
|
28797
28873
|
// 2. It is in middle of selection, so no need to have it
|
|
28798
|
-
segmentsToDelete.push(i);
|
|
28874
|
+
state.segmentsToDelete.push(i);
|
|
28875
|
+
}
|
|
28876
|
+
else {
|
|
28877
|
+
// It is a leading/trailing selection marker of a range selection. Keep it for now, but
|
|
28878
|
+
// remember it so it can be removed later if this same paragraph also contains a real
|
|
28879
|
+
// (non-marker) selected segment, in which case the marker is redundant.
|
|
28880
|
+
state.boundaryMarkers.push(i);
|
|
28799
28881
|
}
|
|
28800
28882
|
return isInSelection;
|
|
28801
28883
|
case 'General':
|
|
@@ -29236,7 +29318,7 @@ var handleBlockGroupChildren = function (doc, parent, group, context) {
|
|
|
29236
29318
|
(_a = context.domIndexer) === null || _a === void 0 ? void 0 : _a.onBlockEntity(childBlock, group);
|
|
29237
29319
|
}
|
|
29238
29320
|
});
|
|
29239
|
-
cleanUpNodeStack(listFormat.nodeStack, context);
|
|
29321
|
+
cleanUpNodeStack(listFormat.nodeStack, context, parent);
|
|
29240
29322
|
// Remove all rest node if any since they don't appear in content model
|
|
29241
29323
|
(0, cleanUpRestNodes_1.cleanUpRestNodes)(refNode, context.rewriteFromModel);
|
|
29242
29324
|
}
|
|
@@ -29245,7 +29327,7 @@ var handleBlockGroupChildren = function (doc, parent, group, context) {
|
|
|
29245
29327
|
}
|
|
29246
29328
|
};
|
|
29247
29329
|
exports.handleBlockGroupChildren = handleBlockGroupChildren;
|
|
29248
|
-
function cleanUpNodeStack(nodeStack, context) {
|
|
29330
|
+
function cleanUpNodeStack(nodeStack, context, leavingParent) {
|
|
29249
29331
|
var _a, _b;
|
|
29250
29332
|
if (nodeStack.length > 0) {
|
|
29251
29333
|
// Clear list stack, only run to nodeStack[1] because nodeStack[0] is the parent node
|
|
@@ -29253,6 +29335,13 @@ function cleanUpNodeStack(nodeStack, context) {
|
|
|
29253
29335
|
var node = (_b = (_a = nodeStack.pop()) === null || _a === void 0 ? void 0 : _a.refNode) !== null && _b !== void 0 ? _b : null;
|
|
29254
29336
|
(0, cleanUpRestNodes_1.cleanUpRestNodes)(node, context.rewriteFromModel);
|
|
29255
29337
|
}
|
|
29338
|
+
if (leavingParent && nodeStack[0].node == leavingParent) {
|
|
29339
|
+
// When leaving a parent node that is the same with the root of node stack
|
|
29340
|
+
// It means the whole list node stack is being invalidated, so we clear it
|
|
29341
|
+
while (nodeStack.length > 0) {
|
|
29342
|
+
nodeStack.pop();
|
|
29343
|
+
}
|
|
29344
|
+
}
|
|
29256
29345
|
}
|
|
29257
29346
|
}
|
|
29258
29347
|
|
|
@@ -30641,11 +30730,17 @@ exports.MarkdownHeadings = {
|
|
|
30641
30730
|
"use strict";
|
|
30642
30731
|
|
|
30643
30732
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
30644
|
-
exports.convertContentModelToMarkdown = exports.convertMarkdownToContentModel = void 0;
|
|
30733
|
+
exports.MarkdownPastePlugin = exports.isPastedContentMarkdown = exports.isContentMarkdown = exports.convertContentModelToMarkdown = exports.convertMarkdownToContentModel = void 0;
|
|
30645
30734
|
var convertMarkdownToContentModel_1 = __webpack_require__(/*! ./markdownToModel/convertMarkdownToContentModel */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts");
|
|
30646
30735
|
Object.defineProperty(exports, "convertMarkdownToContentModel", ({ enumerable: true, get: function () { return convertMarkdownToContentModel_1.convertMarkdownToContentModel; } }));
|
|
30647
30736
|
var convertContentModelToMarkdown_1 = __webpack_require__(/*! ./modelToMarkdown/convertContentModelToMarkdown */ "./packages/roosterjs-content-model-markdown/lib/modelToMarkdown/convertContentModelToMarkdown.ts");
|
|
30648
30737
|
Object.defineProperty(exports, "convertContentModelToMarkdown", ({ enumerable: true, get: function () { return convertContentModelToMarkdown_1.convertContentModelToMarkdown; } }));
|
|
30738
|
+
var isContentMarkdown_1 = __webpack_require__(/*! ./publicApi/isContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts");
|
|
30739
|
+
Object.defineProperty(exports, "isContentMarkdown", ({ enumerable: true, get: function () { return isContentMarkdown_1.isContentMarkdown; } }));
|
|
30740
|
+
var isPastedContentMarkdown_1 = __webpack_require__(/*! ./publicApi/isPastedContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts");
|
|
30741
|
+
Object.defineProperty(exports, "isPastedContentMarkdown", ({ enumerable: true, get: function () { return isPastedContentMarkdown_1.isPastedContentMarkdown; } }));
|
|
30742
|
+
var MarkdownPastePlugin_1 = __webpack_require__(/*! ./plugins/MarkdownPastePlugin */ "./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts");
|
|
30743
|
+
Object.defineProperty(exports, "MarkdownPastePlugin", ({ enumerable: true, get: function () { return MarkdownPastePlugin_1.MarkdownPastePlugin; } }));
|
|
30649
30744
|
|
|
30650
30745
|
|
|
30651
30746
|
/***/ },
|
|
@@ -31335,6 +31430,13 @@ function parseInlineSegments(text, segments, state, link) {
|
|
|
31335
31430
|
};
|
|
31336
31431
|
while (i < text.length) {
|
|
31337
31432
|
var remaining = text.substring(i);
|
|
31433
|
+
// Escaped character: a backslash followed by an ASCII punctuation character emits
|
|
31434
|
+
// that character literally (e.g. "\*" -> "*") and is never treated as a marker.
|
|
31435
|
+
if (text[i] === '\\' && i + 1 < text.length && isEscapable(text[i + 1])) {
|
|
31436
|
+
buffer += text[i + 1];
|
|
31437
|
+
i += 2;
|
|
31438
|
+
continue;
|
|
31439
|
+
}
|
|
31338
31440
|
// Image: 
|
|
31339
31441
|
var imgMatch = imagePattern.exec(remaining);
|
|
31340
31442
|
if (imgMatch && isValidUrl(imgMatch[2])) {
|
|
@@ -31398,6 +31500,10 @@ function shouldToggleFormatting(text, index, marker, currentState) {
|
|
|
31398
31500
|
function isWhitespace(char) {
|
|
31399
31501
|
return /\s/.test(char);
|
|
31400
31502
|
}
|
|
31503
|
+
function isEscapable(char) {
|
|
31504
|
+
// Per CommonMark, any ASCII punctuation character may be backslash-escaped.
|
|
31505
|
+
return /[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/.test(char);
|
|
31506
|
+
}
|
|
31401
31507
|
function toggleFormatting(state, type) {
|
|
31402
31508
|
switch (type) {
|
|
31403
31509
|
case 'bold':
|
|
@@ -31901,6 +32007,252 @@ function modelProcessor(model, newLine) {
|
|
|
31901
32007
|
exports.modelProcessor = modelProcessor;
|
|
31902
32008
|
|
|
31903
32009
|
|
|
32010
|
+
/***/ },
|
|
32011
|
+
|
|
32012
|
+
/***/ "./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts"
|
|
32013
|
+
/*!**************************************************************************************!*\
|
|
32014
|
+
!*** ./packages/roosterjs-content-model-markdown/lib/plugins/MarkdownPastePlugin.ts ***!
|
|
32015
|
+
\**************************************************************************************/
|
|
32016
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
32017
|
+
|
|
32018
|
+
"use strict";
|
|
32019
|
+
|
|
32020
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
32021
|
+
exports.MarkdownPastePlugin = void 0;
|
|
32022
|
+
var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
|
|
32023
|
+
var convertMarkdownToContentModel_1 = __webpack_require__(/*! ../markdownToModel/convertMarkdownToContentModel */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts");
|
|
32024
|
+
var isPastedContentMarkdown_1 = __webpack_require__(/*! ../publicApi/isPastedContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts");
|
|
32025
|
+
var DefaultOptions = {
|
|
32026
|
+
autoConversion: false,
|
|
32027
|
+
};
|
|
32028
|
+
/**
|
|
32029
|
+
* Markdown paste plugin. Handles the BeforePaste event and, when the pasted content
|
|
32030
|
+
* can be interpreted as markdown, converts the plain text into a Content Model and
|
|
32031
|
+
* pastes it as rich markdown content instead of the original clipboard HTML.
|
|
32032
|
+
*/
|
|
32033
|
+
var MarkdownPastePlugin = /** @class */ (function () {
|
|
32034
|
+
/**
|
|
32035
|
+
* Construct a new instance of MarkdownPastePlugin
|
|
32036
|
+
* @param options Options to control the markdown paste behavior
|
|
32037
|
+
*/
|
|
32038
|
+
function MarkdownPastePlugin(options) {
|
|
32039
|
+
this.editor = null;
|
|
32040
|
+
this.options = options !== null && options !== void 0 ? options : DefaultOptions;
|
|
32041
|
+
}
|
|
32042
|
+
/**
|
|
32043
|
+
* Get name of this plugin
|
|
32044
|
+
*/
|
|
32045
|
+
MarkdownPastePlugin.prototype.getName = function () {
|
|
32046
|
+
return 'MarkdownPaste';
|
|
32047
|
+
};
|
|
32048
|
+
/**
|
|
32049
|
+
* The first method that editor will call to a plugin when editor is initializing.
|
|
32050
|
+
* It will pass in the editor instance, plugin should take this chance to save the
|
|
32051
|
+
* editor reference so that it can call to any editor method or format API later.
|
|
32052
|
+
* @param editor The editor object
|
|
32053
|
+
*/
|
|
32054
|
+
MarkdownPastePlugin.prototype.initialize = function (editor) {
|
|
32055
|
+
this.editor = editor;
|
|
32056
|
+
};
|
|
32057
|
+
/**
|
|
32058
|
+
* The last method that editor will call to a plugin before it is disposed.
|
|
32059
|
+
* Plugin can take this chance to clear the reference to editor. After this method is
|
|
32060
|
+
* called, plugin should not call to any editor method since it will result in error.
|
|
32061
|
+
*/
|
|
32062
|
+
MarkdownPastePlugin.prototype.dispose = function () {
|
|
32063
|
+
this.editor = null;
|
|
32064
|
+
};
|
|
32065
|
+
/**
|
|
32066
|
+
* Core method for a plugin. Once an event happens in editor, editor will call this
|
|
32067
|
+
* method of each plugin to handle the event as long as the event is not handled
|
|
32068
|
+
* exclusively by another plugin.
|
|
32069
|
+
* @param event The event to handle:
|
|
32070
|
+
*/
|
|
32071
|
+
MarkdownPastePlugin.prototype.onPluginEvent = function (event) {
|
|
32072
|
+
if (!this.editor || event.eventType != 'beforePaste') {
|
|
32073
|
+
return;
|
|
32074
|
+
}
|
|
32075
|
+
var shouldConvert = event.pasteType === 'asMarkdown' || this.options.autoConversion;
|
|
32076
|
+
if (shouldConvert && (0, isPastedContentMarkdown_1.isPastedContentMarkdown)(this.editor, event.clipboardData)) {
|
|
32077
|
+
convertPastedTextToMarkdown(this.editor, event.fragment, event.clipboardData.text);
|
|
32078
|
+
}
|
|
32079
|
+
};
|
|
32080
|
+
return MarkdownPastePlugin;
|
|
32081
|
+
}());
|
|
32082
|
+
exports.MarkdownPastePlugin = MarkdownPastePlugin;
|
|
32083
|
+
function convertPastedTextToMarkdown(editor, fragment, text) {
|
|
32084
|
+
var model = (0, convertMarkdownToContentModel_1.convertMarkdownToContentModel)(text, {
|
|
32085
|
+
emptyLine: 'merge',
|
|
32086
|
+
});
|
|
32087
|
+
while (fragment.firstChild) {
|
|
32088
|
+
fragment.removeChild(fragment.firstChild);
|
|
32089
|
+
}
|
|
32090
|
+
(0, roosterjs_content_model_dom_1.contentModelToDom)(editor.getDocument(), fragment, model, (0, roosterjs_content_model_dom_1.createModelToDomContext)());
|
|
32091
|
+
}
|
|
32092
|
+
|
|
32093
|
+
|
|
32094
|
+
/***/ },
|
|
32095
|
+
|
|
32096
|
+
/***/ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts"
|
|
32097
|
+
/*!**************************************************************************************!*\
|
|
32098
|
+
!*** ./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts ***!
|
|
32099
|
+
\**************************************************************************************/
|
|
32100
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
32101
|
+
|
|
32102
|
+
"use strict";
|
|
32103
|
+
|
|
32104
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
32105
|
+
exports.isContentMarkdown = void 0;
|
|
32106
|
+
var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
|
|
32107
|
+
// Block-level markdown patterns. A line that matches any of these is considered markdown.
|
|
32108
|
+
var BlockPatterns = [
|
|
32109
|
+
/^#{1,6}\s.+/,
|
|
32110
|
+
/^\s*>\s.+/,
|
|
32111
|
+
/^\s*[\*\-\+]\s.+/,
|
|
32112
|
+
/^\s*\d+\.\s.+/,
|
|
32113
|
+
/^---+$/,
|
|
32114
|
+
/^\s*\|.*\|\s*$/, // table row: "| a | b |"
|
|
32115
|
+
];
|
|
32116
|
+
// Inline markdown patterns. The text contains markdown if any of these match anywhere.
|
|
32117
|
+
var InlinePatterns = [
|
|
32118
|
+
/!\[[^\[\]]+\]\([^\)\s]+\)/,
|
|
32119
|
+
/\[[^\[\]]+\]\([^\)\s]+\)/,
|
|
32120
|
+
/\*\*[^\s*][^*]*\*\*/,
|
|
32121
|
+
/(^|[^*])\*[^\s*][^*]*\*([^*]|$)/,
|
|
32122
|
+
/~~[^\s~][^~]*~~/, // strikethrough: ~~text~~
|
|
32123
|
+
];
|
|
32124
|
+
/**
|
|
32125
|
+
* Detect whether the given plain text contains any markdown markup.
|
|
32126
|
+
* Recognizes block-level patterns (headings, blockquotes, lists, horizontal rules, tables)
|
|
32127
|
+
* and inline patterns (bold, italic, strikethrough, links, images).
|
|
32128
|
+
* @param text The plain text to check.
|
|
32129
|
+
* @returns True if the text contains any markdown markup, false otherwise.
|
|
32130
|
+
*/
|
|
32131
|
+
function isContentMarkdown(text) {
|
|
32132
|
+
var e_1, _a, e_2, _b, e_3, _c;
|
|
32133
|
+
if (!text || !text.trim()) {
|
|
32134
|
+
return false;
|
|
32135
|
+
}
|
|
32136
|
+
var lines = text.split(/\r\n|\r|\n/);
|
|
32137
|
+
try {
|
|
32138
|
+
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()) {
|
|
32139
|
+
var line = lines_1_1.value;
|
|
32140
|
+
try {
|
|
32141
|
+
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()) {
|
|
32142
|
+
var pattern = BlockPatterns_1_1.value;
|
|
32143
|
+
if (pattern.test(line)) {
|
|
32144
|
+
return true;
|
|
32145
|
+
}
|
|
32146
|
+
}
|
|
32147
|
+
}
|
|
32148
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
32149
|
+
finally {
|
|
32150
|
+
try {
|
|
32151
|
+
if (BlockPatterns_1_1 && !BlockPatterns_1_1.done && (_b = BlockPatterns_1.return)) _b.call(BlockPatterns_1);
|
|
32152
|
+
}
|
|
32153
|
+
finally { if (e_2) throw e_2.error; }
|
|
32154
|
+
}
|
|
32155
|
+
}
|
|
32156
|
+
}
|
|
32157
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
32158
|
+
finally {
|
|
32159
|
+
try {
|
|
32160
|
+
if (lines_1_1 && !lines_1_1.done && (_a = lines_1.return)) _a.call(lines_1);
|
|
32161
|
+
}
|
|
32162
|
+
finally { if (e_1) throw e_1.error; }
|
|
32163
|
+
}
|
|
32164
|
+
try {
|
|
32165
|
+
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()) {
|
|
32166
|
+
var pattern = InlinePatterns_1_1.value;
|
|
32167
|
+
if (pattern.test(text)) {
|
|
32168
|
+
return true;
|
|
32169
|
+
}
|
|
32170
|
+
}
|
|
32171
|
+
}
|
|
32172
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
32173
|
+
finally {
|
|
32174
|
+
try {
|
|
32175
|
+
if (InlinePatterns_1_1 && !InlinePatterns_1_1.done && (_c = InlinePatterns_1.return)) _c.call(InlinePatterns_1);
|
|
32176
|
+
}
|
|
32177
|
+
finally { if (e_3) throw e_3.error; }
|
|
32178
|
+
}
|
|
32179
|
+
return false;
|
|
32180
|
+
}
|
|
32181
|
+
exports.isContentMarkdown = isContentMarkdown;
|
|
32182
|
+
|
|
32183
|
+
|
|
32184
|
+
/***/ },
|
|
32185
|
+
|
|
32186
|
+
/***/ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts"
|
|
32187
|
+
/*!********************************************************************************************!*\
|
|
32188
|
+
!*** ./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts ***!
|
|
32189
|
+
\********************************************************************************************/
|
|
32190
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
32191
|
+
|
|
32192
|
+
"use strict";
|
|
32193
|
+
|
|
32194
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
32195
|
+
exports.isPastedContentMarkdown = void 0;
|
|
32196
|
+
var isContentMarkdown_1 = __webpack_require__(/*! ./isContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isContentMarkdown.ts");
|
|
32197
|
+
// Tags that are considered "thin wrappers", which only add structure (such as line breaks)
|
|
32198
|
+
// around the plain text without applying any real formatting to its content.
|
|
32199
|
+
var ThinWrapperTags = new Set(['DIV', 'P', 'BR', 'SPAN']);
|
|
32200
|
+
var AllowedAttributes = new Set(['class', 'style']);
|
|
32201
|
+
/**
|
|
32202
|
+
* Detect whether the given clipboard content can be interpreted as markdown.
|
|
32203
|
+
* @param editor The editor instance.
|
|
32204
|
+
* @param clipboardData The clipboard data to check.
|
|
32205
|
+
* @returns True if the content can be interpreted as markdown, false otherwise.
|
|
32206
|
+
*/
|
|
32207
|
+
function isPastedContentMarkdown(editor, clipboardData) {
|
|
32208
|
+
var text = clipboardData.text, rawHtml = clipboardData.rawHtml;
|
|
32209
|
+
if (!text || !text.trim()) {
|
|
32210
|
+
return false;
|
|
32211
|
+
}
|
|
32212
|
+
if ((0, isContentMarkdown_1.isContentMarkdown)(text)) {
|
|
32213
|
+
if (!rawHtml) {
|
|
32214
|
+
return true;
|
|
32215
|
+
}
|
|
32216
|
+
var doc = editor.getDocument();
|
|
32217
|
+
var trustedHTMLHandler = editor.getDOMCreator();
|
|
32218
|
+
var fragment = parseHtmlToFragment(rawHtml, doc, trustedHTMLHandler);
|
|
32219
|
+
return isThinWrapperOfPlainText(fragment, text);
|
|
32220
|
+
}
|
|
32221
|
+
return false;
|
|
32222
|
+
}
|
|
32223
|
+
exports.isPastedContentMarkdown = isPastedContentMarkdown;
|
|
32224
|
+
function isThinWrapperOfPlainText(fragment, text) {
|
|
32225
|
+
var elements = fragment.querySelectorAll('*');
|
|
32226
|
+
for (var i = 0; i < elements.length; i++) {
|
|
32227
|
+
var element = elements[i];
|
|
32228
|
+
if (!ThinWrapperTags.has(element.tagName)) {
|
|
32229
|
+
return false;
|
|
32230
|
+
}
|
|
32231
|
+
for (var j = 0; j < element.attributes.length; j++) {
|
|
32232
|
+
var attr = element.attributes[j];
|
|
32233
|
+
if (!AllowedAttributes.has(attr.name) && !attr.name.startsWith('data-')) {
|
|
32234
|
+
return false;
|
|
32235
|
+
}
|
|
32236
|
+
}
|
|
32237
|
+
}
|
|
32238
|
+
return removeWhitespace(fragment.textContent || '') === removeWhitespace(text);
|
|
32239
|
+
}
|
|
32240
|
+
function removeWhitespace(text) {
|
|
32241
|
+
return text.replace(/\s/g, '');
|
|
32242
|
+
}
|
|
32243
|
+
function parseHtmlToFragment(html, doc, trustedHTMLHandler) {
|
|
32244
|
+
var parsedDoc = trustedHTMLHandler.htmlToDOM(html);
|
|
32245
|
+
var fragment = doc.createDocumentFragment();
|
|
32246
|
+
var body = parsedDoc === null || parsedDoc === void 0 ? void 0 : parsedDoc.body;
|
|
32247
|
+
if (body) {
|
|
32248
|
+
while (body.firstChild) {
|
|
32249
|
+
fragment.appendChild(body.firstChild);
|
|
32250
|
+
}
|
|
32251
|
+
}
|
|
32252
|
+
return fragment;
|
|
32253
|
+
}
|
|
32254
|
+
|
|
32255
|
+
|
|
31904
32256
|
/***/ },
|
|
31905
32257
|
|
|
31906
32258
|
/***/ "./packages/roosterjs-content-model-plugins/lib/announce/AnnouncePlugin.ts"
|