@zipify/wysiwyg 2.0.0-1 → 2.0.0-11
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/config/build/cli.config.js +8 -2
- package/dist/cli.js +3 -3
- package/dist/wysiwyg.css +41 -31
- package/dist/wysiwyg.mjs +2015 -1359
- package/example/ExampleApp.vue +10 -1
- package/lib/Wysiwyg.vue +3 -2
- package/lib/__tests__/utils/buildTestExtensions.js +2 -1
- package/lib/assets/icons/indicator.svg +4 -0
- package/lib/cli/commands/Command.js +39 -0
- package/lib/cli/commands/ToJsonCommand.js +55 -0
- package/lib/cli/commands/VersionCommand.js +11 -0
- package/lib/cli/commands/index.js +2 -0
- package/lib/cli/index.js +1 -0
- package/lib/components/base/Button.vue +6 -0
- package/lib/components/base/dropdown/Dropdown.vue +7 -1
- package/lib/components/base/dropdown/DropdownActivator.vue +25 -4
- package/lib/components/base/dropdown/__tests__/DropdownActivator.test.js +23 -1
- package/lib/components/toolbar/controls/AlignmentControl.vue +12 -1
- package/lib/components/toolbar/controls/FontColorControl.vue +14 -0
- package/lib/components/toolbar/controls/FontFamilyControl.vue +4 -0
- package/lib/components/toolbar/controls/FontSizeControl.vue +6 -1
- package/lib/components/toolbar/controls/FontWeightControl.vue +12 -0
- package/lib/components/toolbar/controls/ItalicControl.vue +14 -0
- package/lib/components/toolbar/controls/LineHeightControl.vue +15 -0
- package/lib/components/toolbar/controls/UnderlineControl.vue +13 -0
- package/lib/components/toolbar/controls/__tests__/AlignmentControl.test.js +72 -5
- package/lib/components/toolbar/controls/__tests__/FontColorControl.test.js +22 -1
- package/lib/components/toolbar/controls/__tests__/FontFamilyControl.test.js +1 -0
- package/lib/components/toolbar/controls/__tests__/FontSizeControl.test.js +1 -0
- package/lib/components/toolbar/controls/__tests__/FontWeightControl.test.js +1 -0
- package/lib/components/toolbar/controls/__tests__/ItalicControl.test.js +23 -1
- package/lib/components/toolbar/controls/__tests__/LineHeightControl.test.js +23 -1
- package/lib/components/toolbar/controls/__tests__/StylePresetControl.test.js +4 -4
- package/lib/components/toolbar/controls/__tests__/UnderlineControl.test.js +25 -1
- package/lib/composables/__tests__/useEditor.test.js +1 -1
- package/lib/composables/useEditor.js +9 -8
- package/lib/directives/__tests__/tooltip.test.js +22 -4
- package/lib/directives/tooltip.js +4 -1
- package/lib/entry-cli.js +7 -20
- package/lib/entry-lib.js +1 -1
- package/lib/enums/MarkGroups.js +4 -0
- package/lib/enums/TextSettings.js +1 -1
- package/lib/enums/index.js +1 -0
- package/lib/extensions/BackgroundColor.js +0 -1
- package/lib/extensions/FontColor.js +2 -2
- package/lib/extensions/FontFamily.js +3 -3
- package/lib/extensions/FontSize.js +2 -2
- package/lib/extensions/FontStyle.js +2 -2
- package/lib/extensions/FontWeight.js +2 -2
- package/lib/extensions/StylePreset.js +9 -2
- package/lib/extensions/Superscript.js +5 -2
- package/lib/extensions/TextDecoration.js +7 -0
- package/lib/extensions/__tests__/Alignment.test.js +2 -2
- package/lib/extensions/__tests__/BackgroundColor.test.js +4 -3
- package/lib/extensions/__tests__/FontColor.test.js +4 -3
- package/lib/extensions/__tests__/FontFamily.test.js +6 -6
- package/lib/extensions/__tests__/FontSize.test.js +9 -8
- package/lib/extensions/__tests__/FontStyle.test.js +6 -5
- package/lib/extensions/__tests__/LineHeight.test.js +2 -1
- package/lib/extensions/__tests__/StylePreset.test.js +51 -0
- package/lib/extensions/__tests__/Superscript.test.js +102 -0
- package/lib/extensions/__tests__/TextDecoration.test.js +20 -0
- package/lib/extensions/__tests__/__snapshots__/BackgroundColor.test.js.snap +25 -25
- package/lib/extensions/__tests__/__snapshots__/Superscript.test.js.snap +107 -0
- package/lib/extensions/core/Document.js +2 -1
- package/lib/extensions/core/Heading.js +2 -1
- package/lib/extensions/core/NodeProcessor.js +42 -21
- package/lib/extensions/core/Paragraph.js +2 -1
- package/lib/extensions/core/__tests__/NodeProcessor.test.js +309 -11
- package/lib/extensions/core/__tests__/TextProcessor.test.js +1 -1
- package/lib/extensions/core/__tests__/__snapshots__/NodeProcessor.test.js.snap +249 -0
- package/lib/extensions/core/steps/AddNodeMarkStep.js +6 -0
- package/lib/extensions/core/steps/AttrStep.js +6 -0
- package/lib/extensions/core/steps/RemoveNodeMarkStep.js +6 -0
- package/lib/extensions/list/List.js +70 -9
- package/lib/extensions/list/ListItem.js +27 -5
- package/lib/extensions/list/__tests__/List.test.js +26 -17
- package/lib/extensions/list/__tests__/__snapshots__/List.test.js.snap +36 -36
- package/lib/services/NodeFactory.js +69 -3
- package/lib/services/__tests__/NodeFactory.test.js +124 -0
- package/lib/services/__tests__/__snapshots__/NodeFactory.test.js.snap +326 -0
- package/lib/services/normalizer/HtmlNormalizer.js +54 -2
- package/lib/services/normalizer/JsonNormalizer.js +6 -5
- package/lib/services/normalizer/__tests__/HtmlNormalizer.test.js +14 -0
- package/lib/services/normalizer/__tests__/JsonNormalizer.test.js +20 -3
- package/lib/services/normalizer/__tests__/__snapshots__/JsonNormalizer.test.js.snap +37 -0
- package/lib/utils/__tests__/findMarkByType.test.js +17 -0
- package/lib/utils/__tests__/isMarkAppliedToParent.test.js +53 -0
- package/lib/utils/__tests__/isNodeFullySelected.test.js +44 -0
- package/lib/utils/__tests__/resolveTextPosition.test.js +39 -0
- package/lib/utils/copyMark.js +5 -0
- package/lib/utils/index.js +1 -1
- package/lib/utils/isMarkAppliedToParent.js +1 -1
- package/lib/utils/isNodeFullySelected.js +4 -7
- package/lib/utils/resolveTextPosition.js +4 -6
- package/package.json +37 -27
- package/lib/utils/resolveNodePosition.js +0 -6
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import { NodeTypes } from '../enums';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} NodeJson
|
|
5
|
+
* @property {String} type
|
|
6
|
+
* @property {Array<MarkJson>} [marks]
|
|
7
|
+
* @property {AttrsJson} [attrs]
|
|
8
|
+
*
|
|
9
|
+
* @typedef {Object} MarkJson
|
|
10
|
+
* @property {String} type
|
|
11
|
+
* @property {AttrsJson} attrs
|
|
12
|
+
*
|
|
13
|
+
* @typedef {Object} AttrsJson
|
|
14
|
+
*/
|
|
15
|
+
|
|
3
16
|
export class NodeFactory {
|
|
17
|
+
/**
|
|
18
|
+
* @param {Array<NodeJson>} content
|
|
19
|
+
* @returns {NodeJson}
|
|
20
|
+
*/
|
|
4
21
|
static doc(content) {
|
|
5
22
|
return { type: NodeTypes.DOCUMENT, content };
|
|
6
23
|
}
|
|
7
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @param {String} type
|
|
27
|
+
* @param {Array<String | NodeJson>} items
|
|
28
|
+
* @returns {NodeJson}
|
|
29
|
+
*/
|
|
8
30
|
static list(type, items) {
|
|
9
31
|
return {
|
|
10
32
|
type: NodeTypes.LIST,
|
|
@@ -16,10 +38,33 @@ export class NodeFactory {
|
|
|
16
38
|
};
|
|
17
39
|
}
|
|
18
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @type {{
|
|
43
|
+
* ((content: String | NodeJson) => NodeJson);
|
|
44
|
+
* ((attrs: AttrsJson, content: String | NodeJson) => NodeJson);
|
|
45
|
+
* ((attrs: AttrsJson, marks: Array<MarkJson>, content: String | NodeJson) => NodeJson);
|
|
46
|
+
* }}
|
|
47
|
+
*/
|
|
19
48
|
static listItem(...args) {
|
|
20
|
-
|
|
49
|
+
const { attrs, content: children, marks } = this.#normalizeTextBlockArgs(args);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
type: NodeTypes.LIST_ITEM,
|
|
53
|
+
...(attrs ? { attrs } : {}),
|
|
54
|
+
...(marks ? { marks } : {}),
|
|
55
|
+
content: [].concat(children).map((content) => {
|
|
56
|
+
return typeof content === 'string' ? this.paragraph(content) : content;
|
|
57
|
+
})
|
|
58
|
+
};
|
|
21
59
|
}
|
|
22
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @type {{
|
|
63
|
+
* ((level: Number, content: String | NodeJson) => NodeJson);
|
|
64
|
+
* ((level: Number, attrs: AttrsJson, content: String | NodeJson) => NodeJson);
|
|
65
|
+
* ((level: Number, attrs: AttrsJson, marks: Array<MarkJson>, content: String | NodeJson) => NodeJson);
|
|
66
|
+
* }}
|
|
67
|
+
*/
|
|
23
68
|
static heading(level, ...args) {
|
|
24
69
|
const config = this.#textBlock(args, this.text);
|
|
25
70
|
|
|
@@ -29,6 +74,13 @@ export class NodeFactory {
|
|
|
29
74
|
return { type: NodeTypes.HEADING, ...config };
|
|
30
75
|
}
|
|
31
76
|
|
|
77
|
+
/**
|
|
78
|
+
* @type {{
|
|
79
|
+
* ((content: String | NodeJson) => NodeJson);
|
|
80
|
+
* ((attrs: AttrsJson, content: String | NodeJson) => NodeJson);
|
|
81
|
+
* ((attrs: AttrsJson, marks: Array<MarkJson>, content: String | NodeJson) => NodeJson);
|
|
82
|
+
* }}
|
|
83
|
+
*/
|
|
32
84
|
static paragraph(...args) {
|
|
33
85
|
return {
|
|
34
86
|
type: NodeTypes.PARAGRAPH,
|
|
@@ -36,9 +88,9 @@ export class NodeFactory {
|
|
|
36
88
|
};
|
|
37
89
|
}
|
|
38
90
|
|
|
39
|
-
static #textBlock(args
|
|
91
|
+
static #textBlock(args) {
|
|
40
92
|
const { attrs, content, marks } = this.#normalizeTextBlockArgs(args);
|
|
41
|
-
const children = typeof content === 'string' ? [
|
|
93
|
+
const children = typeof content === 'string' ? [this.text(content)] : content;
|
|
42
94
|
|
|
43
95
|
return {
|
|
44
96
|
content: children,
|
|
@@ -57,6 +109,11 @@ export class NodeFactory {
|
|
|
57
109
|
return { attrs: args[0], marks: args[1], content: args[2] };
|
|
58
110
|
}
|
|
59
111
|
|
|
112
|
+
/**
|
|
113
|
+
* @param {String} text
|
|
114
|
+
* @param {Array<MarkJson>} marks
|
|
115
|
+
* @returns {NodeJson}
|
|
116
|
+
*/
|
|
60
117
|
static text(text, marks) {
|
|
61
118
|
return {
|
|
62
119
|
type: NodeTypes.TEXT,
|
|
@@ -65,10 +122,19 @@ export class NodeFactory {
|
|
|
65
122
|
};
|
|
66
123
|
}
|
|
67
124
|
|
|
125
|
+
/**
|
|
126
|
+
* @param {String} type
|
|
127
|
+
* @param {AttrsJson} attrs
|
|
128
|
+
* @returns {MarkJson}
|
|
129
|
+
*/
|
|
68
130
|
static mark(type, attrs) {
|
|
69
131
|
return { type, attrs };
|
|
70
132
|
}
|
|
71
133
|
|
|
134
|
+
/**
|
|
135
|
+
* @param {*} value
|
|
136
|
+
* @returns {{tablet, desktop, mobile}}
|
|
137
|
+
*/
|
|
72
138
|
static populateAllDevices(value) {
|
|
73
139
|
// return { mobile: value, tablet: value, desktop: value };
|
|
74
140
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { NodeFactory } from '../NodeFactory';
|
|
2
|
+
import { ListTypes, TextSettings } from '../../enums';
|
|
3
|
+
|
|
4
|
+
describe('build node', () => {
|
|
5
|
+
test('should build doc node', () => {
|
|
6
|
+
expect(NodeFactory.doc([])).toMatchSnapshot();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('should build list node with text items', () => {
|
|
10
|
+
const node = NodeFactory.list(ListTypes.DISC, [
|
|
11
|
+
'lorem ipsum 1',
|
|
12
|
+
'lorem ipsum 2'
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
expect(node).toMatchSnapshot();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should build list node with node items', () => {
|
|
19
|
+
const node = NodeFactory.list(ListTypes.DISC, [
|
|
20
|
+
NodeFactory.paragraph('lorem ipsum 1'),
|
|
21
|
+
NodeFactory.paragraph('lorem ipsum 2')
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
expect(node).toMatchSnapshot();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('should build list node with custom list item nodes', () => {
|
|
28
|
+
const node = NodeFactory.list(ListTypes.DISC, [
|
|
29
|
+
NodeFactory.listItem(null, [
|
|
30
|
+
NodeFactory.mark(TextSettings.FONT_WEIGHT, { value: '700' })
|
|
31
|
+
], 'lorem ipsum 1'),
|
|
32
|
+
|
|
33
|
+
NodeFactory.listItem(null, [
|
|
34
|
+
NodeFactory.mark(TextSettings.FONT_WEIGHT, { value: '700' })
|
|
35
|
+
], 'lorem ipsum 2')
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
expect(node).toMatchSnapshot();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('should build heading node', () => {
|
|
42
|
+
const node = NodeFactory.heading(1, 'lorem ipsum');
|
|
43
|
+
|
|
44
|
+
expect(node).toMatchSnapshot();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('should build heading node with attrs', () => {
|
|
48
|
+
const node = NodeFactory.heading(1, { preset: { id: 'h1' } }, 'lorem ipsum');
|
|
49
|
+
|
|
50
|
+
expect(node).toMatchSnapshot();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should build heading node with marks', () => {
|
|
54
|
+
const node = NodeFactory.heading(1, null, [
|
|
55
|
+
NodeFactory.mark(TextSettings.FONT_WEIGHT, { value: '700' })
|
|
56
|
+
], 'lorem ipsum');
|
|
57
|
+
|
|
58
|
+
expect(node).toMatchSnapshot();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should build heading with custom text node', () => {
|
|
62
|
+
const node = NodeFactory.heading(1, [
|
|
63
|
+
NodeFactory.text('lorem ipsum', [
|
|
64
|
+
NodeFactory.mark(TextSettings.FONT_WEIGHT, { value: '700' })
|
|
65
|
+
])
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
expect(node).toMatchSnapshot();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('should build paragraph node', () => {
|
|
72
|
+
const node = NodeFactory.paragraph('lorem ipsum');
|
|
73
|
+
|
|
74
|
+
expect(node).toMatchSnapshot();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('should build paragraph node with attrs', () => {
|
|
78
|
+
const node = NodeFactory.paragraph({ preset: { id: 'regular-1' } }, 'lorem ipsum');
|
|
79
|
+
|
|
80
|
+
expect(node).toMatchSnapshot();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('should build paragraph node with marks', () => {
|
|
84
|
+
const node = NodeFactory.paragraph(null, [
|
|
85
|
+
NodeFactory.mark(TextSettings.FONT_WEIGHT, { value: '700' })
|
|
86
|
+
], 'lorem ipsum');
|
|
87
|
+
|
|
88
|
+
expect(node).toMatchSnapshot();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('should build paragraph with custom text node', () => {
|
|
92
|
+
const node = NodeFactory.paragraph([
|
|
93
|
+
NodeFactory.text('lorem ipsum', [
|
|
94
|
+
NodeFactory.mark(TextSettings.FONT_WEIGHT, { value: '700' })
|
|
95
|
+
])
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
expect(node).toMatchSnapshot();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('should build text node', () => {
|
|
102
|
+
const node = NodeFactory.text('lorem ipsum');
|
|
103
|
+
|
|
104
|
+
expect(node).toMatchSnapshot();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('should build text node with marks', () => {
|
|
108
|
+
const node = NodeFactory.text('lorem ipsum');
|
|
109
|
+
|
|
110
|
+
expect(node).toMatchSnapshot();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('should build mark', () => {
|
|
114
|
+
const mark = NodeFactory.mark(TextSettings.FONT_WEIGHT, { value: '700' });
|
|
115
|
+
|
|
116
|
+
expect(mark).toMatchSnapshot();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('should populate value to all devices', () => {
|
|
120
|
+
const value = NodeFactory.populateAllDevices('18');
|
|
121
|
+
|
|
122
|
+
expect(value).toMatchSnapshot();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`build node should build doc node 1`] = `
|
|
4
|
+
Object {
|
|
5
|
+
"content": Array [],
|
|
6
|
+
"type": "doc",
|
|
7
|
+
}
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
exports[`build node should build heading node 1`] = `
|
|
11
|
+
Object {
|
|
12
|
+
"attrs": Object {
|
|
13
|
+
"level": 1,
|
|
14
|
+
},
|
|
15
|
+
"content": Array [
|
|
16
|
+
Object {
|
|
17
|
+
"text": "lorem ipsum",
|
|
18
|
+
"type": "text",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
"type": "heading",
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
exports[`build node should build heading node with attrs 1`] = `
|
|
26
|
+
Object {
|
|
27
|
+
"attrs": Object {
|
|
28
|
+
"level": 1,
|
|
29
|
+
"preset": Object {
|
|
30
|
+
"id": "h1",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
"content": Array [
|
|
34
|
+
Object {
|
|
35
|
+
"text": "lorem ipsum",
|
|
36
|
+
"type": "text",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
"type": "heading",
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
exports[`build node should build heading node with marks 1`] = `
|
|
44
|
+
Object {
|
|
45
|
+
"attrs": Object {
|
|
46
|
+
"level": 1,
|
|
47
|
+
},
|
|
48
|
+
"content": Array [
|
|
49
|
+
Object {
|
|
50
|
+
"text": "lorem ipsum",
|
|
51
|
+
"type": "text",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
"marks": Array [
|
|
55
|
+
Object {
|
|
56
|
+
"attrs": Object {
|
|
57
|
+
"value": "700",
|
|
58
|
+
},
|
|
59
|
+
"type": "font_weight",
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
"type": "heading",
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
exports[`build node should build heading with custom text node 1`] = `
|
|
67
|
+
Object {
|
|
68
|
+
"attrs": Object {
|
|
69
|
+
"level": 1,
|
|
70
|
+
},
|
|
71
|
+
"content": Array [
|
|
72
|
+
Object {
|
|
73
|
+
"marks": Array [
|
|
74
|
+
Object {
|
|
75
|
+
"attrs": Object {
|
|
76
|
+
"value": "700",
|
|
77
|
+
},
|
|
78
|
+
"type": "font_weight",
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
"text": "lorem ipsum",
|
|
82
|
+
"type": "text",
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
"type": "heading",
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
exports[`build node should build list node with custom list item nodes 1`] = `
|
|
90
|
+
Object {
|
|
91
|
+
"attrs": Object {
|
|
92
|
+
"bullet": Object {
|
|
93
|
+
"type": "disc",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
"content": Array [
|
|
97
|
+
Object {
|
|
98
|
+
"content": Array [
|
|
99
|
+
Object {
|
|
100
|
+
"content": Array [
|
|
101
|
+
Object {
|
|
102
|
+
"text": "lorem ipsum 1",
|
|
103
|
+
"type": "text",
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
"type": "paragraph",
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
"marks": Array [
|
|
110
|
+
Object {
|
|
111
|
+
"attrs": Object {
|
|
112
|
+
"value": "700",
|
|
113
|
+
},
|
|
114
|
+
"type": "font_weight",
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
"type": "listItem",
|
|
118
|
+
},
|
|
119
|
+
Object {
|
|
120
|
+
"content": Array [
|
|
121
|
+
Object {
|
|
122
|
+
"content": Array [
|
|
123
|
+
Object {
|
|
124
|
+
"text": "lorem ipsum 2",
|
|
125
|
+
"type": "text",
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
"type": "paragraph",
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
"marks": Array [
|
|
132
|
+
Object {
|
|
133
|
+
"attrs": Object {
|
|
134
|
+
"value": "700",
|
|
135
|
+
},
|
|
136
|
+
"type": "font_weight",
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
"type": "listItem",
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
"type": "list",
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
exports[`build node should build list node with node items 1`] = `
|
|
147
|
+
Object {
|
|
148
|
+
"attrs": Object {
|
|
149
|
+
"bullet": Object {
|
|
150
|
+
"type": "disc",
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
"content": Array [
|
|
154
|
+
Object {
|
|
155
|
+
"content": Array [
|
|
156
|
+
Object {
|
|
157
|
+
"content": Array [
|
|
158
|
+
Object {
|
|
159
|
+
"text": "lorem ipsum 1",
|
|
160
|
+
"type": "text",
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
"type": "paragraph",
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
"type": "listItem",
|
|
167
|
+
},
|
|
168
|
+
Object {
|
|
169
|
+
"content": Array [
|
|
170
|
+
Object {
|
|
171
|
+
"content": Array [
|
|
172
|
+
Object {
|
|
173
|
+
"text": "lorem ipsum 2",
|
|
174
|
+
"type": "text",
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
"type": "paragraph",
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
"type": "listItem",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
"type": "list",
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
exports[`build node should build list node with text items 1`] = `
|
|
188
|
+
Object {
|
|
189
|
+
"attrs": Object {
|
|
190
|
+
"bullet": Object {
|
|
191
|
+
"type": "disc",
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
"content": Array [
|
|
195
|
+
Object {
|
|
196
|
+
"content": Array [
|
|
197
|
+
Object {
|
|
198
|
+
"content": Array [
|
|
199
|
+
Object {
|
|
200
|
+
"text": "lorem ipsum 1",
|
|
201
|
+
"type": "text",
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
"type": "paragraph",
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
"type": "listItem",
|
|
208
|
+
},
|
|
209
|
+
Object {
|
|
210
|
+
"content": Array [
|
|
211
|
+
Object {
|
|
212
|
+
"content": Array [
|
|
213
|
+
Object {
|
|
214
|
+
"text": "lorem ipsum 2",
|
|
215
|
+
"type": "text",
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
"type": "paragraph",
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
"type": "listItem",
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
"type": "list",
|
|
225
|
+
}
|
|
226
|
+
`;
|
|
227
|
+
|
|
228
|
+
exports[`build node should build mark 1`] = `
|
|
229
|
+
Object {
|
|
230
|
+
"attrs": Object {
|
|
231
|
+
"value": "700",
|
|
232
|
+
},
|
|
233
|
+
"type": "font_weight",
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
exports[`build node should build paragraph node 1`] = `
|
|
238
|
+
Object {
|
|
239
|
+
"content": Array [
|
|
240
|
+
Object {
|
|
241
|
+
"text": "lorem ipsum",
|
|
242
|
+
"type": "text",
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
"type": "paragraph",
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
|
|
249
|
+
exports[`build node should build paragraph node with attrs 1`] = `
|
|
250
|
+
Object {
|
|
251
|
+
"attrs": Object {
|
|
252
|
+
"preset": Object {
|
|
253
|
+
"id": "regular-1",
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
"content": Array [
|
|
257
|
+
Object {
|
|
258
|
+
"text": "lorem ipsum",
|
|
259
|
+
"type": "text",
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
"type": "paragraph",
|
|
263
|
+
}
|
|
264
|
+
`;
|
|
265
|
+
|
|
266
|
+
exports[`build node should build paragraph node with marks 1`] = `
|
|
267
|
+
Object {
|
|
268
|
+
"content": Array [
|
|
269
|
+
Object {
|
|
270
|
+
"text": "lorem ipsum",
|
|
271
|
+
"type": "text",
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
"marks": Array [
|
|
275
|
+
Object {
|
|
276
|
+
"attrs": Object {
|
|
277
|
+
"value": "700",
|
|
278
|
+
},
|
|
279
|
+
"type": "font_weight",
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
"type": "paragraph",
|
|
283
|
+
}
|
|
284
|
+
`;
|
|
285
|
+
|
|
286
|
+
exports[`build node should build paragraph with custom text node 1`] = `
|
|
287
|
+
Object {
|
|
288
|
+
"content": Array [
|
|
289
|
+
Object {
|
|
290
|
+
"marks": Array [
|
|
291
|
+
Object {
|
|
292
|
+
"attrs": Object {
|
|
293
|
+
"value": "700",
|
|
294
|
+
},
|
|
295
|
+
"type": "font_weight",
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
"text": "lorem ipsum",
|
|
299
|
+
"type": "text",
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
"type": "paragraph",
|
|
303
|
+
}
|
|
304
|
+
`;
|
|
305
|
+
|
|
306
|
+
exports[`build node should build text node 1`] = `
|
|
307
|
+
Object {
|
|
308
|
+
"text": "lorem ipsum",
|
|
309
|
+
"type": "text",
|
|
310
|
+
}
|
|
311
|
+
`;
|
|
312
|
+
|
|
313
|
+
exports[`build node should build text node with marks 1`] = `
|
|
314
|
+
Object {
|
|
315
|
+
"text": "lorem ipsum",
|
|
316
|
+
"type": "text",
|
|
317
|
+
}
|
|
318
|
+
`;
|
|
319
|
+
|
|
320
|
+
exports[`build node should populate value to all devices 1`] = `
|
|
321
|
+
Object {
|
|
322
|
+
"desktop": "18",
|
|
323
|
+
"mobile": null,
|
|
324
|
+
"tablet": "18",
|
|
325
|
+
}
|
|
326
|
+
`;
|
|
@@ -2,6 +2,7 @@ import { BaseNormalizer } from './BaseNormalizer';
|
|
|
2
2
|
|
|
3
3
|
export class HtmlNormalizer extends BaseNormalizer {
|
|
4
4
|
static BLOCK_NODE_NAMES = ['P', 'H1', 'H2', 'H3', 'H4'];
|
|
5
|
+
static ROOT_NODE_NAMES = HtmlNormalizer.BLOCK_NODE_NAMES.concat('UL', 'OL');
|
|
5
6
|
|
|
6
7
|
static BLOCK_STYLES = [
|
|
7
8
|
'text-align',
|
|
@@ -30,10 +31,12 @@ export class HtmlNormalizer extends BaseNormalizer {
|
|
|
30
31
|
this.dom = this.#parser.parse(this.content.replace(/(\r)?\n/g, ''));
|
|
31
32
|
|
|
32
33
|
this.#removeComments();
|
|
34
|
+
this.#normalizeRootTags();
|
|
33
35
|
this.#iterateNodes(this.#normalizeBreakLines, (node) => node.tagName === 'BR');
|
|
34
36
|
this.#iterateNodes(this.#removeEmptyNodes, this.#isBlockNode);
|
|
35
37
|
this.#iterateNodes(this.#normalizeListItems, (node) => node.tagName === 'LI');
|
|
36
38
|
this.#normalizeBlockTextDecoration();
|
|
39
|
+
this.#normalizeBlockBackgroundColor();
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
get normalizedHTML() {
|
|
@@ -54,6 +57,30 @@ export class HtmlNormalizer extends BaseNormalizer {
|
|
|
54
57
|
this.#runIterator(iterator, (node) => node.remove());
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
#normalizeRootTags() {
|
|
61
|
+
const children = Array.from(this.dom.body.childNodes);
|
|
62
|
+
const fragment = this.dom.createDocumentFragment();
|
|
63
|
+
let capturingParagraph;
|
|
64
|
+
|
|
65
|
+
for (const node of children) {
|
|
66
|
+
if (this.#isRootNode(node)) {
|
|
67
|
+
fragment.append(node);
|
|
68
|
+
capturingParagraph = null;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!capturingParagraph) {
|
|
73
|
+
capturingParagraph = this.dom.createElement('p');
|
|
74
|
+
fragment.append(capturingParagraph);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
capturingParagraph.append(node);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.dom.body.innerHTML = '';
|
|
81
|
+
this.dom.body.append(fragment);
|
|
82
|
+
}
|
|
83
|
+
|
|
57
84
|
#createNodeIterator(whatToShow, filter) {
|
|
58
85
|
return this.dom.createNodeIterator(this.dom.body, whatToShow, filter);
|
|
59
86
|
}
|
|
@@ -84,8 +111,7 @@ export class HtmlNormalizer extends BaseNormalizer {
|
|
|
84
111
|
#normalizeListItems(itemEl) {
|
|
85
112
|
const fragment = this.dom.createDocumentFragment();
|
|
86
113
|
const children = Array.from(itemEl.childNodes);
|
|
87
|
-
let capturingParagraph;
|
|
88
|
-
let previousNode;
|
|
114
|
+
let capturingParagraph, previousNode;
|
|
89
115
|
|
|
90
116
|
const append = (node) => {
|
|
91
117
|
this.#assignElementProperties(node, itemEl, HtmlNormalizer.BLOCK_STYLES);
|
|
@@ -135,6 +161,10 @@ export class HtmlNormalizer extends BaseNormalizer {
|
|
|
135
161
|
return HtmlNormalizer.BLOCK_NODE_NAMES.includes(node.tagName);
|
|
136
162
|
}
|
|
137
163
|
|
|
164
|
+
#isRootNode(node) {
|
|
165
|
+
return HtmlNormalizer.ROOT_NODE_NAMES.includes(node.tagName);
|
|
166
|
+
}
|
|
167
|
+
|
|
138
168
|
#assignElementProperties(target, source, properties) {
|
|
139
169
|
for (const property of properties) {
|
|
140
170
|
const value = source.style.getPropertyValue(property);
|
|
@@ -232,6 +262,28 @@ export class HtmlNormalizer extends BaseNormalizer {
|
|
|
232
262
|
};
|
|
233
263
|
}
|
|
234
264
|
|
|
265
|
+
#normalizeBlockBackgroundColor() {
|
|
266
|
+
const blockEls = this.dom.querySelectorAll('[style*="background-color"]:where(p, h1, h2, h3, h4, li)');
|
|
267
|
+
|
|
268
|
+
for (const blockEl of blockEls) {
|
|
269
|
+
this.#moveBackgroundColorToChildren(blockEl);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#moveBackgroundColorToChildren(blockEl) {
|
|
274
|
+
const blockColor = blockEl.style.backgroundColor;
|
|
275
|
+
|
|
276
|
+
blockEl.style.removeProperty('background-color');
|
|
277
|
+
if (!blockEl.style.cssText) blockEl.removeAttribute('style');
|
|
278
|
+
|
|
279
|
+
for (const childNode of blockEl.childNodes) {
|
|
280
|
+
const textEl = this.#wrapTextNode(blockEl, childNode);
|
|
281
|
+
const color = textEl.style.backgroundColor || blockColor;
|
|
282
|
+
|
|
283
|
+
textEl.style.backgroundColor = color;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
235
287
|
#wrapTextNode(parent, node) {
|
|
236
288
|
if (node.nodeType !== this.#Node.TEXT_NODE) return node;
|
|
237
289
|
|
|
@@ -14,8 +14,8 @@ export class JsonNormalizer extends BaseNormalizer {
|
|
|
14
14
|
|
|
15
15
|
#iterateChildNodes(node, handler) {
|
|
16
16
|
for (const child of node.content) {
|
|
17
|
-
handler.call(this, child);
|
|
18
17
|
child.content && this.#iterateChildNodes(child, handler);
|
|
18
|
+
handler.call(this, child);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -27,14 +27,14 @@ export class JsonNormalizer extends BaseNormalizer {
|
|
|
27
27
|
if (!child.marks) continue;
|
|
28
28
|
|
|
29
29
|
for (const childMark of child.marks.slice()) {
|
|
30
|
-
if (this.#
|
|
30
|
+
if (this.#includesMark(node, childMark)) {
|
|
31
31
|
this.#removeMark(child, childMark);
|
|
32
|
-
this.#addMark(node, childMark);
|
|
33
32
|
continue;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
if (this.#
|
|
35
|
+
if (this.#canBubbleMark(node, childMark)) {
|
|
37
36
|
this.#removeMark(child, childMark);
|
|
37
|
+
this.#addMark(node, childMark);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -45,8 +45,9 @@ export class JsonNormalizer extends BaseNormalizer {
|
|
|
45
45
|
if (this.#includesMarkType(node, childMark.type)) return false;
|
|
46
46
|
|
|
47
47
|
for (const child of node.content) {
|
|
48
|
+
if (!child.content && node.type === NodeTypes.LIST_ITEM) continue;
|
|
48
49
|
if (!child.marks) return false;
|
|
49
|
-
if (!this.#
|
|
50
|
+
if (!this.#includesMark(child, childMark)) return false;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
return true;
|