suneditor 3.3.1 → 3.3.2
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/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +3 -2
- package/src/assets/suneditor.css +19 -3
- package/src/core/editor.js +6 -1
- package/src/core/event/effects/keydown.registry.js +14 -9
- package/src/core/event/effects/ruleHelpers.js +119 -1
- package/src/core/event/eventOrchestrator.js +15 -6
- package/src/core/event/rules/keydown.rule.backspace.js +10 -47
- package/src/core/event/rules/keydown.rule.delete.js +28 -66
- package/src/core/logic/dom/format.js +1 -1
- package/src/core/logic/dom/html.js +54 -5
- package/src/core/logic/dom/nodeTransform.js +5 -3
- package/src/core/logic/shell/ui.js +6 -1
- package/src/core/schema/options.js +4 -3
- package/src/events.js +1 -1
- package/src/helper/dom/domQuery.js +10 -4
- package/src/helper/googleDocs.js +40 -0
- package/src/helper/index.js +3 -0
- package/types/core/event/effects/ruleHelpers.d.ts +56 -0
- package/types/core/schema/options.d.ts +6 -5
- package/types/events.d.ts +2 -2
- package/types/helper/dom/domQuery.d.ts +4 -2
- package/types/helper/googleDocs.d.ts +19 -0
- package/types/helper/index.d.ts +5 -0
|
@@ -71,6 +71,7 @@ export const DEFAULTS = {
|
|
|
71
71
|
'@text': 'font-family|font-size|color|background-color|width|height',
|
|
72
72
|
'@line': 'text-align|margin|margin-left|margin-right|line-height',
|
|
73
73
|
'@component': 'width|height|min-width',
|
|
74
|
+
li: 'font-family|font-size|color|background-color|font-weight|font-style',
|
|
74
75
|
'table|th|td':
|
|
75
76
|
'border|border-[a-z]+|color|background-color|text-align|float|font-weight|text-decoration|font-style|vertical-align',
|
|
76
77
|
'table|td': 'width',
|
|
@@ -457,7 +458,7 @@ export const DEFAULTS = {
|
|
|
457
458
|
* - Value is a pipe-delimited list of allowed style names.
|
|
458
459
|
* - Resolution order when filtering an element: `@component` (for `.se-component` containers) → explicit tag entry → `@line` (for formatLine elements) → `@text` (for textStyleTags).
|
|
459
460
|
* - `@component` guards the inline sizing (`width`/`height`/`min-width`) the editor writes on a media component's container for percentage-based sizes; keep these so a clean() round-trip does not reset the component to full width.
|
|
460
|
-
* - An explicit tag entry **
|
|
461
|
+
* - An explicit tag entry is **merged** with its category default when the tag belongs to one (`@line` for formatLine elements, else `@text` for textStyleTags) — the entry adds styles on top of the category's.
|
|
461
462
|
* - Merged with {@link DEFAULTS.TAG_STYLES}; user-supplied keys win.
|
|
462
463
|
* ```js
|
|
463
464
|
* {
|
|
@@ -465,8 +466,8 @@ export const DEFAULTS = {
|
|
|
465
466
|
* '@text': 'color|font-size|background-color', // default for span, b, i, em, ...
|
|
466
467
|
* '@line': 'text-align|margin|line-height', // default for p, h1-h6, div, li, ...
|
|
467
468
|
* 'table|td': 'border|color|background-color', // per-tag whitelist
|
|
468
|
-
*
|
|
469
|
-
*
|
|
469
|
+
* div: 'color', // merged with the `@line` default (div is a line element)
|
|
470
|
+
* hr: 'border-top',
|
|
470
471
|
* }
|
|
471
472
|
* }
|
|
472
473
|
* ```
|
package/src/events.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* @property {Event} event - event object
|
|
18
18
|
* @property {string} data - drop data
|
|
19
19
|
* @property {boolean} maxCharCount - is max char count
|
|
20
|
-
* @property {string} from - `"SE"`|`"MS"`|`""` - source
|
|
20
|
+
* @property {string} from - `"SE"`|`"MS"`|`"GOOGLE"`|`""` - source
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
// --- media
|
|
@@ -513,7 +513,8 @@ export function getEdgeChildNodes(first, last) {
|
|
|
513
513
|
|
|
514
514
|
/**
|
|
515
515
|
* @template {Node} T
|
|
516
|
-
* @description Gets the previous sibling last child. If there is no sibling, then it'll take it from the closest ancestor with child
|
|
516
|
+
* @description Gets the previous sibling last child. If there is no sibling, then it'll take it from the closest ancestor with child.
|
|
517
|
+
* - Components (image, table, etc.) are treated as a single tag and not traversed into.
|
|
517
518
|
* @param {Node} node Reference element
|
|
518
519
|
* @param {?Node} [ceiling] Highest boundary allowed
|
|
519
520
|
* @returns {T|null} Not found: `null`
|
|
@@ -533,14 +534,17 @@ export function getPreviousDeepestNode(node, ceiling) {
|
|
|
533
534
|
|
|
534
535
|
if (domCheck.isNonEditable(previousNode)) return /** @type {T} */ (/** @type {unknown} */ (previousNode));
|
|
535
536
|
|
|
536
|
-
while (previousNode
|
|
537
|
+
while (!domCheck.isComponentContainer(previousNode) && previousNode.lastChild) {
|
|
538
|
+
previousNode = previousNode.lastChild;
|
|
539
|
+
}
|
|
537
540
|
|
|
538
541
|
return /** @type {T} */ (/** @type {unknown} */ (previousNode));
|
|
539
542
|
}
|
|
540
543
|
|
|
541
544
|
/**
|
|
542
545
|
* @template {Node} T
|
|
543
|
-
* @description Gets the next sibling first child. If there is no sibling, then it'll take it from the closest ancestor with child
|
|
546
|
+
* @description Gets the next sibling first child. If there is no sibling, then it'll take it from the closest ancestor with child.
|
|
547
|
+
* - Components (image, table, etc.) are treated as a single tag and not traversed into.
|
|
544
548
|
* @param {Node} node Reference element
|
|
545
549
|
* @param {?Node} [ceiling] Highest boundary allowed
|
|
546
550
|
* @returns {T|null} Not found: `null`
|
|
@@ -560,7 +564,9 @@ export function getNextDeepestNode(node, ceiling) {
|
|
|
560
564
|
|
|
561
565
|
if (domCheck.isNonEditable(nextNode)) return /** @type {T} */ (/** @type {unknown} */ (nextNode));
|
|
562
566
|
|
|
563
|
-
while (nextNode
|
|
567
|
+
while (!domCheck.isComponentContainer(nextNode) && nextNode.firstChild) {
|
|
568
|
+
nextNode = nextNode.firstChild;
|
|
569
|
+
}
|
|
564
570
|
|
|
565
571
|
return /** @type {T} */ (/** @type {unknown} */ (nextNode));
|
|
566
572
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Cleans Google Docs clipboard HTML.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Matches the Google Docs clipboard wrapper id */
|
|
6
|
+
const _RE_GUID = /id=["']?docs-internal-guid-/i;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @description Whether the HTML string is a Google Docs clipboard payload.
|
|
10
|
+
* @param {string} html HTML string
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
export function isGoogleDocs(html) {
|
|
14
|
+
return _RE_GUID.test(html);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @description Removes the Google Docs clipboard wrapper tags, keeping their children.
|
|
19
|
+
* - Only the guid-carrying inline wrappers (`b`/`span`) are unwrapped; real formatting tags inside are untouched.
|
|
20
|
+
* @param {string} html HTML string
|
|
21
|
+
* @returns {string} HTML string
|
|
22
|
+
*/
|
|
23
|
+
export function cleanHTML(html) {
|
|
24
|
+
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
25
|
+
const wrappers = doc.body.querySelectorAll('b[id^="docs-internal-guid-"], span[id^="docs-internal-guid-"]');
|
|
26
|
+
if (wrappers.length === 0) return html;
|
|
27
|
+
|
|
28
|
+
for (let i = 0, len = wrappers.length, w; i < len; i++) {
|
|
29
|
+
w = wrappers[i];
|
|
30
|
+
while (w.firstChild) w.parentNode.insertBefore(w.firstChild, w);
|
|
31
|
+
w.parentNode.removeChild(w);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return doc.body.innerHTML;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default {
|
|
38
|
+
isGoogleDocs,
|
|
39
|
+
cleanHTML,
|
|
40
|
+
};
|
package/src/helper/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import KeyCodeMap from './keyCodeMap';
|
|
|
7
7
|
import Clipboard from './clipboard';
|
|
8
8
|
import Markdown from './markdown';
|
|
9
9
|
import MSOffice from './msOffice';
|
|
10
|
+
import GoogleDocs from './googleDocs';
|
|
10
11
|
|
|
11
12
|
export const env = Env;
|
|
12
13
|
export const unicode = Unicode;
|
|
@@ -17,6 +18,7 @@ export const keyCodeMap = KeyCodeMap;
|
|
|
17
18
|
export const clipboard = Clipboard;
|
|
18
19
|
export const markdown = Markdown;
|
|
19
20
|
export const msOffice = MSOffice;
|
|
21
|
+
export const googleDocs = GoogleDocs;
|
|
20
22
|
|
|
21
23
|
export default {
|
|
22
24
|
env,
|
|
@@ -28,4 +30,5 @@ export default {
|
|
|
28
30
|
clipboard,
|
|
29
31
|
markdown,
|
|
30
32
|
msOffice,
|
|
33
|
+
googleDocs,
|
|
31
34
|
};
|
|
@@ -51,3 +51,59 @@ export function isRtlBidiMismatch(
|
|
|
51
51
|
detectedEdge: 'front' | 'end',
|
|
52
52
|
doc: Document,
|
|
53
53
|
): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* @description Whether the caret sits on a bare `<br>` that stands at the front/end edge of its `line`.
|
|
56
|
+
* @param {Range} range - The current range
|
|
57
|
+
* @param {Node} selectionNode - Current selection node
|
|
58
|
+
* @param {'front'|'end'} edge - Edge to test: `front` for Backspace, `end` for Delete
|
|
59
|
+
* @returns {boolean} `true` if the caret is on an edge `<br>`
|
|
60
|
+
*/
|
|
61
|
+
export function isEdgeBreakCaret(range: Range, selectionNode: Node, edge: 'front' | 'end'): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* @description The previous/next element in document order — crossing in and out of blocks (list, quote).
|
|
64
|
+
* - Out of list-cell ancestors (a nested list), stopping at a closure block (table cell).
|
|
65
|
+
* @param {EventPorts['format']} format - Format module
|
|
66
|
+
* @param {?HTMLElement} line - The caret's `line` element
|
|
67
|
+
* @param {'front'|'end'} edge - `front` for the previous element, `end` for the next one
|
|
68
|
+
* @returns {?HTMLElement} The adjacent element, or `null` at the document edge
|
|
69
|
+
*/
|
|
70
|
+
export function getAdjacentElement(
|
|
71
|
+
format: EventPorts['format'],
|
|
72
|
+
line: HTMLElement | null,
|
|
73
|
+
edge: 'front' | 'end',
|
|
74
|
+
): HTMLElement | null;
|
|
75
|
+
/**
|
|
76
|
+
* @description The previous/next `line` in document order — {@link getAdjacentElement} filtered to lines.
|
|
77
|
+
* - `null` means either the document edge or a non-`line` neighbour (a component); use
|
|
78
|
+
* {@link getAdjacentElement} when the two must be told apart.
|
|
79
|
+
* @param {EventPorts['format']} format - Format module
|
|
80
|
+
* @param {?HTMLElement} line - The caret's `line` element
|
|
81
|
+
* @param {'front'|'end'} edge - `front` for the previous line, `end` for the next one
|
|
82
|
+
* @returns {?HTMLElement} The adjacent line, or `null`
|
|
83
|
+
*/
|
|
84
|
+
export function getAdjacentLine(
|
|
85
|
+
format: EventPorts['format'],
|
|
86
|
+
line: HTMLElement | null,
|
|
87
|
+
edge: 'front' | 'end',
|
|
88
|
+
): HTMLElement | null;
|
|
89
|
+
/**
|
|
90
|
+
* @description The neighbouring `line` an empty line collapses into, or `null` when there is nothing to merge.
|
|
91
|
+
* - A cell owning a nested list belongs to {@link getNestedListTarget} instead.
|
|
92
|
+
* @param {EventPorts['format']} format - Format module
|
|
93
|
+
* @param {?HTMLElement} formatEl - The caret's `line` element
|
|
94
|
+
* @param {'front'|'end'} edge - `front` for Backspace (previous line), `end` for Delete (next line)
|
|
95
|
+
* @returns {?HTMLElement} The neighbouring line to merge into, or `null`
|
|
96
|
+
*/
|
|
97
|
+
export function getEmptyLineMergeTarget(
|
|
98
|
+
format: EventPorts['format'],
|
|
99
|
+
formatEl: HTMLElement | null,
|
|
100
|
+
edge: 'front' | 'end',
|
|
101
|
+
): HTMLElement | null;
|
|
102
|
+
/**
|
|
103
|
+
* @description The nested list a list-cell Backspace/Delete would lift, or `null` when there is none.
|
|
104
|
+
* - The rules gate their list branch on it so the branch can't claim the key with nothing to do.
|
|
105
|
+
* @param {HTMLElement} formatEl - The caret's list cell
|
|
106
|
+
* @param {HTMLElement} rangeEl - The list (`UL`/`OL`) the cell belongs to
|
|
107
|
+
* @returns {?HTMLElement} The element carrying the nested list, or `null`
|
|
108
|
+
*/
|
|
109
|
+
export function getNestedListTarget(formatEl: HTMLElement, rangeEl: HTMLElement): HTMLElement | null;
|
|
@@ -28,6 +28,7 @@ export namespace DEFAULTS {
|
|
|
28
28
|
'@text': string;
|
|
29
29
|
'@line': string;
|
|
30
30
|
'@component': string;
|
|
31
|
+
li: string;
|
|
31
32
|
'table|th|td': string;
|
|
32
33
|
'table|td': string;
|
|
33
34
|
tr: string;
|
|
@@ -400,7 +401,7 @@ export namespace DEFAULTS {
|
|
|
400
401
|
* - Value is a pipe-delimited list of allowed style names.
|
|
401
402
|
* - Resolution order when filtering an element: `@component` (for `.se-component` containers) → explicit tag entry → `@line` (for formatLine elements) → `@text` (for textStyleTags).
|
|
402
403
|
* - `@component` guards the inline sizing (`width`/`height`/`min-width`) the editor writes on a media component's container for percentage-based sizes; keep these so a clean() round-trip does not reset the component to full width.
|
|
403
|
-
* - An explicit tag entry **
|
|
404
|
+
* - An explicit tag entry is **merged** with its category default when the tag belongs to one (`@line` for formatLine elements, else `@text` for textStyleTags) — the entry adds styles on top of the category's.
|
|
404
405
|
* - Merged with {@link DEFAULTS.TAG_STYLES}; user-supplied keys win.
|
|
405
406
|
* ```js
|
|
406
407
|
* {
|
|
@@ -408,8 +409,8 @@ export namespace DEFAULTS {
|
|
|
408
409
|
* '@text': 'color|font-size|background-color', // default for span, b, i, em, ...
|
|
409
410
|
* '@line': 'text-align|margin|line-height', // default for p, h1-h6, div, li, ...
|
|
410
411
|
* 'table|td': 'border|color|background-color', // per-tag whitelist
|
|
411
|
-
*
|
|
412
|
-
*
|
|
412
|
+
* div: 'color', // merged with the `@line` default (div is a line element)
|
|
413
|
+
* hr: 'border-top',
|
|
413
414
|
* }
|
|
414
415
|
* }
|
|
415
416
|
* ```
|
|
@@ -1254,7 +1255,7 @@ export type EditorBaseOptions = {
|
|
|
1254
1255
|
* - Value is a pipe-delimited list of allowed style names.
|
|
1255
1256
|
* - Resolution order when filtering an element: `@component` (for `.se-component` containers) → explicit tag entry → `@line` (for formatLine elements) → `@text` (for textStyleTags).
|
|
1256
1257
|
* - `@component` guards the inline sizing (`width`/`height`/`min-width`) the editor writes on a media component's container for percentage-based sizes; keep these so a clean() round-trip does not reset the component to full width.
|
|
1257
|
-
* - An explicit tag entry **
|
|
1258
|
+
* - An explicit tag entry is **merged** with its category default when the tag belongs to one (`@line` for formatLine elements, else `@text` for textStyleTags) — the entry adds styles on top of the category's.
|
|
1258
1259
|
* - Merged with {@link DEFAULTS.TAG_STYLES}; user-supplied keys win.
|
|
1259
1260
|
* ```js
|
|
1260
1261
|
* {
|
|
@@ -1262,7 +1263,7 @@ export type EditorBaseOptions = {
|
|
|
1262
1263
|
* '@text': 'color|font-size|background-color', // default for span, b, i, em, ...
|
|
1263
1264
|
* '@line': 'text-align|margin|line-height', // default for p, h1-h6, div, li, ...
|
|
1264
1265
|
* 'table|td': 'border|color|background-color', // per-tag whitelist
|
|
1265
|
-
* div: 'color',
|
|
1266
|
+
* div: 'color', // merged with the `@line` default (div is a line element)
|
|
1266
1267
|
* hr: 'border-top',
|
|
1267
1268
|
* }
|
|
1268
1269
|
* }
|
package/types/events.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export type ClipboardEvent = {
|
|
|
35
35
|
*/
|
|
36
36
|
maxCharCount: boolean;
|
|
37
37
|
/**
|
|
38
|
-
* - `"SE"`|`"MS"`|`""` - source
|
|
38
|
+
* - `"SE"`|`"MS"`|`"GOOGLE"`|`""` - source
|
|
39
39
|
*/
|
|
40
40
|
from: string;
|
|
41
41
|
};
|
|
@@ -289,7 +289,7 @@ export type EventHandlers = {
|
|
|
289
289
|
* @property {Event} event - event object
|
|
290
290
|
* @property {string} data - drop data
|
|
291
291
|
* @property {boolean} maxCharCount - is max char count
|
|
292
|
-
* @property {string} from - `"SE"`|`"MS"`|`""` - source
|
|
292
|
+
* @property {string} from - `"SE"`|`"MS"`|`"GOOGLE"`|`""` - source
|
|
293
293
|
*/
|
|
294
294
|
/**
|
|
295
295
|
* @typedef {Object} FileManagementInfo
|
|
@@ -198,7 +198,8 @@ export function getEdgeChildNodes(
|
|
|
198
198
|
};
|
|
199
199
|
/**
|
|
200
200
|
* @template {Node} T
|
|
201
|
-
* @description Gets the previous sibling last child. If there is no sibling, then it'll take it from the closest ancestor with child
|
|
201
|
+
* @description Gets the previous sibling last child. If there is no sibling, then it'll take it from the closest ancestor with child.
|
|
202
|
+
* - Components (image, table, etc.) are treated as a single tag and not traversed into.
|
|
202
203
|
* @param {Node} node Reference element
|
|
203
204
|
* @param {?Node} [ceiling] Highest boundary allowed
|
|
204
205
|
* @returns {T|null} Not found: `null`
|
|
@@ -206,7 +207,8 @@ export function getEdgeChildNodes(
|
|
|
206
207
|
export function getPreviousDeepestNode<T extends Node>(node: Node, ceiling?: Node | null): T | null;
|
|
207
208
|
/**
|
|
208
209
|
* @template {Node} T
|
|
209
|
-
* @description Gets the next sibling first child. If there is no sibling, then it'll take it from the closest ancestor with child
|
|
210
|
+
* @description Gets the next sibling first child. If there is no sibling, then it'll take it from the closest ancestor with child.
|
|
211
|
+
* - Components (image, table, etc.) are treated as a single tag and not traversed into.
|
|
210
212
|
* @param {Node} node Reference element
|
|
211
213
|
* @param {?Node} [ceiling] Highest boundary allowed
|
|
212
214
|
* @returns {T|null} Not found: `null`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type {} from '../typedef';
|
|
2
|
+
/**
|
|
3
|
+
* @description Whether the HTML string is a Google Docs clipboard payload.
|
|
4
|
+
* @param {string} html HTML string
|
|
5
|
+
* @returns {boolean}
|
|
6
|
+
*/
|
|
7
|
+
export function isGoogleDocs(html: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* @description Removes the Google Docs clipboard wrapper tags, keeping their children.
|
|
10
|
+
* - Only the guid-carrying inline wrappers (`b`/`span`) are unwrapped; real formatting tags inside are untouched.
|
|
11
|
+
* @param {string} html HTML string
|
|
12
|
+
* @returns {string} HTML string
|
|
13
|
+
*/
|
|
14
|
+
export function cleanHTML(html: string): string;
|
|
15
|
+
declare namespace _default {
|
|
16
|
+
export { isGoogleDocs };
|
|
17
|
+
export { cleanHTML };
|
|
18
|
+
}
|
|
19
|
+
export default _default;
|
package/types/helper/index.d.ts
CHANGED
|
@@ -178,6 +178,10 @@ export const markdown: {
|
|
|
178
178
|
export const msOffice: {
|
|
179
179
|
cleanHTML: typeof import('./msOffice').cleanHTML;
|
|
180
180
|
};
|
|
181
|
+
export const googleDocs: {
|
|
182
|
+
isGoogleDocs: typeof import('./googleDocs').isGoogleDocs;
|
|
183
|
+
cleanHTML: typeof import('./googleDocs').cleanHTML;
|
|
184
|
+
};
|
|
181
185
|
declare namespace _default {
|
|
182
186
|
export { env };
|
|
183
187
|
export { unicode };
|
|
@@ -188,5 +192,6 @@ declare namespace _default {
|
|
|
188
192
|
export { clipboard };
|
|
189
193
|
export { markdown };
|
|
190
194
|
export { msOffice };
|
|
195
|
+
export { googleDocs };
|
|
191
196
|
}
|
|
192
197
|
export default _default;
|