@modusoperandi/licit 1.4.3 → 1.4.6
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/BlockquoteInsertNewLineCommand.js.flow +70 -70
- package/BlockquoteNodeSpec.js.flow +29 -29
- package/BlockquoteToggleCommand.js.flow +56 -56
- package/BookmarkNodeSpec.js.flow +39 -39
- package/BulletListNodeSpec.js.flow +61 -61
- package/CZIProseMirror.js.flow +90 -90
- package/CodeBlockCommand.js.flow +65 -65
- package/CodeBlockNodeSpec.js.flow +24 -24
- package/CodeMarkSpec.js.flow +14 -14
- package/ContentPlaceholderPlugin.js.flow +183 -183
- package/CursorPlaceholderPlugin.js.flow +113 -113
- package/DocLayoutCommand.js.flow +97 -97
- package/DocNodeSpec.js.flow +62 -62
- package/EMMarkSpec.js.flow +39 -39
- package/EditorCommands.js.flow +124 -124
- package/EditorKeyMap.js.flow +173 -173
- package/EditorMarks.js +4 -2
- package/EditorMarks.js.flow +77 -74
- package/EditorNodes.js.flow +55 -55
- package/EditorPageLayoutPlugin.js.flow +67 -67
- package/EditorPlugins.js.flow +8 -8
- package/EditorSchema.js.flow +12 -12
- package/EditorState.js.flow +7 -7
- package/FontSizeMarkSpec.js.flow +54 -54
- package/FontTypeMarkSpec.js.flow +89 -89
- package/HTMLMutator.js.flow +58 -58
- package/HangingIndentMarkSpec.js +34 -0
- package/HangingIndentMarkSpec.js.flow +30 -0
- package/HardBreakNodeSpec.js.flow +15 -15
- package/HeadingNodeSpec.js.flow +42 -42
- package/HistoryRedoCommand.js.flow +41 -41
- package/HistoryUndoCommand.js.flow +41 -41
- package/HorizontalRuleCommand.js.flow +71 -71
- package/HorizontalRuleNodeSpec.js.flow +39 -39
- package/ImageUploadPlaceholderPlugin.js.flow +187 -187
- package/LICENSE +22 -22
- package/LinkMarkSpec.js +4 -2
- package/LinkMarkSpec.js.flow +38 -37
- package/LinkSetURLCommand.js.flow +129 -129
- package/LinkTooltipPlugin.js +31 -37
- package/LinkTooltipPlugin.js.flow +204 -217
- package/ListItemInsertNewLineCommand.js.flow +76 -76
- package/ListItemMergeCommand.js.flow +197 -197
- package/ListItemNodeSpec.js.flow +52 -52
- package/ListSplitCommand.js.flow +54 -54
- package/ListToggleCommand.js.flow +99 -99
- package/MarkNames.js +2 -1
- package/MarkNames.js.flow +20 -19
- package/MarksClearCommand.js.flow +66 -66
- package/NodeNames.js.flow +23 -23
- package/OrderedListNodeSpec.js.flow +131 -131
- package/OverrideMarkSpec.js.flow +49 -49
- package/ParagraphNodeSpec.js +21 -2
- package/ParagraphNodeSpec.js.flow +233 -215
- package/ParagraphSpacingCommand.js.flow +139 -139
- package/PrintCommand.js.flow +53 -53
- package/SelectionPlaceholderPlugin.js.flow +129 -129
- package/SpacerMarkSpec.js.flow +47 -47
- package/StrikeMarkSpec.js.flow +24 -24
- package/StrongMarkSpec.js.flow +39 -39
- package/StyleView.js.flow +18 -18
- package/TableBackgroundColorCommand.js.flow +79 -79
- package/TableBorderColorCommand.js +5 -3
- package/TableBorderColorCommand.js.flow +108 -106
- package/TableCellColorCommand.js.flow +73 -73
- package/TableCellMenuPlugin.js.flow +129 -129
- package/TableInsertCommand.js.flow +117 -117
- package/TableMergeCellsCommand.js.flow +113 -113
- package/TableNodesSpecs.js.flow +144 -144
- package/TablePlugins.js.flow +14 -14
- package/TableResizePlugin.js.flow +636 -636
- package/TextColorMarkSpec.js.flow +40 -40
- package/TextHighlightMarkSpec.js.flow +47 -47
- package/TextInsertTabSpaceCommand.js +0 -3
- package/TextInsertTabSpaceCommand.js.flow +103 -106
- package/TextNoWrapMarkSpec.js.flow +14 -14
- package/TextNodeSpec.js.flow +7 -7
- package/TextSelectionMarkSpec.js.flow +24 -24
- package/TextSubMarkSpec.js.flow +28 -28
- package/TextSuperMarkSpec.js.flow +28 -28
- package/TextUnderlineMarkSpec.js.flow +24 -24
- package/Types.js.flow +77 -77
- package/WebFontLoader.js.flow +22 -22
- package/blockQuoteInputRule.js.flow +36 -36
- package/bom.xml +8573 -8650
- package/browser.js.flow +7 -7
- package/buildEditorPlugins.js.flow +48 -48
- package/buildInputRules.js.flow +85 -85
- package/client/CollabConnector.js.flow +90 -90
- package/client/EditorConnection.js.flow +324 -324
- package/client/Licit.js +39 -39
- package/client/Licit.js.flow +649 -649
- package/client/Licit.test.js.flow +104 -104
- package/client/Reporter.js.flow +35 -35
- package/client/SimpleConnector.js.flow +61 -61
- package/client/http.js.flow +60 -60
- package/client/licit.css +12 -12
- package/client/throttle.js.flow +27 -27
- package/convertFromDOMElement.js.flow +33 -33
- package/convertFromHTML.js.flow +15 -15
- package/convertFromJSON.js.flow +53 -53
- package/convertToCSSPTValue.js.flow +19 -19
- package/convertToJSON.js.flow +7 -7
- package/createCommand.js.flow +62 -62
- package/createEditorKeyMap.js.flow +87 -87
- package/createEmptyEditorState.js.flow +29 -29
- package/createTableResizingPlugin.js.flow +86 -86
- package/findActionableCell.js.flow +74 -74
- package/findActiveMark.js.flow +32 -32
- package/hyphenize.js.flow +17 -17
- package/index.d.ts +167 -167
- package/index.js.flow +10 -10
- package/insertTable.js.flow +54 -54
- package/isEditorStateEmpty.js.flow +32 -32
- package/isTableNode.js.flow +15 -15
- package/joinDown.js.flow +25 -25
- package/joinListNode.js.flow +55 -55
- package/joinUp.js.flow +37 -37
- package/keymaps.js.flow +175 -175
- package/lookUpElement.js.flow +14 -14
- package/nodeAt.js.flow +12 -12
- package/normalizeHTML.js.flow +80 -80
- package/package.json +152 -152
- package/patchAnchorElements.js.flow +38 -38
- package/patchBreakElements.js.flow +22 -22
- package/patchElementInlineStyles.js.flow +92 -92
- package/patchListElements.js.flow +275 -275
- package/patchMathElements.js.flow +58 -58
- package/patchParagraphElements.js.flow +20 -20
- package/patchStyleElements.js.flow +197 -197
- package/patchTableElements.js.flow +88 -88
- package/rebaseDocWithSteps.js.flow +42 -42
- package/sanitizeURL.js.flow +13 -13
- package/splitListItem.js.flow +191 -191
- package/styles.css +46 -46
- package/toClosestFontPtSize.js.flow +22 -22
- package/toSafeHTMLDocument.js.flow +9 -9
- package/toggleBlockquote.js.flow +101 -101
- package/toggleCodeBlock.js.flow +102 -102
- package/ui/AlertInfo.js.flow +63 -63
- package/ui/BookmarkNodeView.js.flow +64 -64
- package/ui/CommandButton.js.flow +68 -68
- package/ui/CommandMenu.js.flow +74 -74
- package/ui/CommandMenuButton.js.flow +128 -128
- package/ui/CustomEditorView.js.flow +29 -29
- package/ui/CustomMenu.js.flow +14 -14
- package/ui/CustomMenuItem.js.flow +35 -35
- package/ui/CustomNodeView.js.flow +207 -207
- package/ui/CustomRadioButton.js.flow +63 -63
- package/ui/DocLayoutEditor.js.flow +130 -130
- package/ui/Editor.js.flow +281 -281
- package/ui/EditorFrameset.js.flow +79 -79
- package/ui/EditorToolbar.js.flow +197 -197
- package/ui/EditorToolbarConfig.js.flow +162 -162
- package/ui/FontSizeCommandMenuButton.js.flow +66 -66
- package/ui/FontTypeCommandMenuButton.js.flow +60 -60
- package/ui/Frag.js.flow +11 -11
- package/ui/Icon.js.flow +82 -82
- package/ui/ImageInlineEditor.js.flow +66 -66
- package/ui/KeyCodes.js.flow +12 -12
- package/ui/LinkTooltip.js.flow +51 -51
- package/ui/LinkURLEditor.js.flow +227 -227
- package/ui/ListItemNodeView.js.flow +101 -101
- package/ui/ListTypeButton.js.flow +121 -121
- package/ui/ListTypeCommandButton.js.flow +85 -85
- package/ui/ListTypeMenu.js.flow +78 -78
- package/ui/LoadingIndicator.js.flow +19 -19
- package/ui/PasteMenu.js.flow +52 -52
- package/ui/ResizeObserver.js.flow +105 -105
- package/ui/RichTextEditor.js.flow +133 -133
- package/ui/SelectionObserver.js.flow +134 -134
- package/ui/TableCellMenu.js.flow +49 -49
- package/ui/TableGridSizeEditor.js.flow +185 -185
- package/ui/TableNodeView.js.flow +36 -36
- package/ui/bindScrollHandler.js.flow +46 -46
- package/ui/canUseCSSFont.js.flow +42 -42
- package/ui/czi-body-layout-editor.css +16 -16
- package/ui/czi-bookmark-view.css +10 -10
- package/ui/czi-cursor-placeholder.css +36 -36
- package/ui/czi-custom-menu-button.css +18 -18
- package/ui/czi-custom-menu-item.css +30 -30
- package/ui/czi-custom-menu.css +8 -8
- package/ui/czi-custom-radio-button.css +80 -80
- package/ui/czi-custom-scrollbar.css +21 -21
- package/ui/czi-editor-frameset.css +81 -81
- package/ui/czi-editor-toolbar.css +122 -122
- package/ui/czi-editor.css +239 -216
- package/ui/czi-form.css +201 -201
- package/ui/czi-frag.css +3 -3
- package/ui/czi-heading.css +40 -40
- package/ui/czi-icon.css +72 -72
- package/ui/czi-image-resize-box.css +165 -165
- package/ui/czi-image-upload-editor.css +57 -57
- package/ui/czi-image-upload-placeholder.css +50 -50
- package/ui/czi-image-url-editor.css +38 -38
- package/ui/czi-image-view.css +121 -121
- package/ui/czi-indent.css +137 -137
- package/ui/czi-inline-editor.css +20 -20
- package/ui/czi-link-tooltip.css +112 -112
- package/ui/czi-list.css +406 -406
- package/ui/czi-loading-indicator.css +66 -66
- package/ui/czi-math-view.css +62 -62
- package/ui/czi-selection-placeholder.css +24 -24
- package/ui/czi-table-cell-menu.css +16 -16
- package/ui/czi-table-grid-size-editor.css +37 -37
- package/ui/czi-table.css +89 -89
- package/ui/czi-vars.css +47 -45
- package/ui/findActiveFontSize.js.flow +55 -55
- package/ui/findActiveFontType.js.flow +35 -35
- package/ui/fonts.css +460 -460
- package/ui/handleEditorDrop.js.flow +28 -28
- package/ui/handleEditorKeyDown.js.flow +39 -39
- package/ui/handleEditorPaste.js.flow +33 -33
- package/ui/htmlElementToRect.js.flow +18 -18
- package/ui/icon-font.css +9 -9
- package/ui/injectStyleSheet.js.flow +40 -40
- package/ui/isElementFullyVisible.js.flow +14 -14
- package/ui/isOffline.js.flow +8 -8
- package/ui/isReactClass.js.flow +12 -12
- package/ui/listType.css +21 -21
- package/ui/resolveImage.js.flow +121 -121
- package/ui/toCSSColor.js.flow +51 -51
- package/ui/toCSSLineSpacing.js.flow +53 -53
- package/ui/toHexColor.js.flow +26 -26
- package/ui/uuid.js.flow +9 -9
- package/uuid.js.flow +9 -9
|
@@ -1,275 +1,275 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
import HTMLMutator from './HTMLMutator.js';
|
|
4
|
-
import uuid from './ui/uuid.js';
|
|
5
|
-
|
|
6
|
-
import { ATTRIBUTE_LIST_STYLE_TYPE } from './ListItemNodeSpec.js';
|
|
7
|
-
import {
|
|
8
|
-
ATTRIBUTE_INDENT,
|
|
9
|
-
EMPTY_CSS_VALUE,
|
|
10
|
-
convertMarginLeftToIndentValue,
|
|
11
|
-
} from './ParagraphNodeSpec.js';
|
|
12
|
-
import {
|
|
13
|
-
ATTRIBUTE_COUNTER_RESET,
|
|
14
|
-
ATTRIBUTE_FOLLOWING,
|
|
15
|
-
} from './OrderedListNodeSpec.js';
|
|
16
|
-
import { ATTRIBUTE_CSS_BEFORE_CONTENT } from './patchStyleElements.js';
|
|
17
|
-
|
|
18
|
-
export default function patchListElements(doc: Document): void {
|
|
19
|
-
// In Google Doc, lists are exported as indented
|
|
20
|
-
// (e.g. style="margin-left: 48pt") list elements which is the default DOM
|
|
21
|
-
// structure that `czi-prosemirror` supports. However, other doc providers
|
|
22
|
-
// (e.g Office 365) may export lists as nested list elements that can't
|
|
23
|
-
// be rendered properly.
|
|
24
|
-
// Before proceeding further, it needs to convert the nested list elements
|
|
25
|
-
// into indented list elements.
|
|
26
|
-
liftNestedListElements(doc);
|
|
27
|
-
Array.from(doc.querySelectorAll('ol, ul')).forEach(patchListElementsElement);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// This assumes that every 36pt maps to one indent level.
|
|
31
|
-
const CHAR_BULLET = '\u25cf';
|
|
32
|
-
const CHAR_CIRCLE = '\u25cb';
|
|
33
|
-
const CHAR_SQUARE = '\u25a0';
|
|
34
|
-
const CHAR_BOX = '\u274f';
|
|
35
|
-
const CHAR_ZERO_SPACE = '\u200B';
|
|
36
|
-
const INLINE_NODE_NAME_PATTERN = /^(#text|(A|SPAN|B|STRONG))$/;
|
|
37
|
-
|
|
38
|
-
function patchListElementsElement(listElement: HTMLElement): void {
|
|
39
|
-
// If the children of `listElement` all have teh same marginLeft, assume
|
|
40
|
-
// it to be indented.
|
|
41
|
-
let marginLeft;
|
|
42
|
-
let beforeContent;
|
|
43
|
-
const { parentElement, children } = listElement;
|
|
44
|
-
|
|
45
|
-
// A workaround to patch the issue when <ul /> or <ol /> is pasted as the
|
|
46
|
-
// first child of <body />, its first <li /> somehow can't be wrapped
|
|
47
|
-
// with the list. The hack is to prepend zero-width-space character
|
|
48
|
-
// before the list.
|
|
49
|
-
if (
|
|
50
|
-
parentElement &&
|
|
51
|
-
parentElement.nodeName === 'BODY' &&
|
|
52
|
-
parentElement.firstChild === listElement
|
|
53
|
-
) {
|
|
54
|
-
const tt = parentElement.ownerDocument.createTextNode(CHAR_ZERO_SPACE);
|
|
55
|
-
parentElement.insertBefore(tt, listElement);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
Array.from(children).forEach((listItemElement) => {
|
|
59
|
-
const { style } = listItemElement;
|
|
60
|
-
patchPaddingStyle(listItemElement);
|
|
61
|
-
|
|
62
|
-
const bc = listItemElement.getAttribute(ATTRIBUTE_CSS_BEFORE_CONTENT) || '';
|
|
63
|
-
if (beforeContent === undefined) {
|
|
64
|
-
beforeContent = bc;
|
|
65
|
-
}
|
|
66
|
-
if (beforeContent !== bc) {
|
|
67
|
-
beforeContent = null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const ml = (style?.marginLeft) || '';
|
|
71
|
-
if (marginLeft === undefined) {
|
|
72
|
-
marginLeft = ml;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (ml !== marginLeft) {
|
|
76
|
-
marginLeft = null;
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
if (marginLeft) {
|
|
81
|
-
const indent = convertMarginLeftToIndentValue(marginLeft);
|
|
82
|
-
if (indent) {
|
|
83
|
-
listElement.setAttribute(ATTRIBUTE_INDENT, String(indent));
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (beforeContent) {
|
|
88
|
-
beforeContent = String(beforeContent);
|
|
89
|
-
let listStyleType;
|
|
90
|
-
switch (true) {
|
|
91
|
-
case beforeContent.indexOf(CHAR_BULLET) > -1:
|
|
92
|
-
listStyleType = 'disc';
|
|
93
|
-
break;
|
|
94
|
-
|
|
95
|
-
case beforeContent.indexOf(CHAR_CIRCLE) > -1:
|
|
96
|
-
listStyleType = 'circle';
|
|
97
|
-
break;
|
|
98
|
-
|
|
99
|
-
case beforeContent.indexOf(CHAR_SQUARE) > -1:
|
|
100
|
-
listStyleType = 'square';
|
|
101
|
-
break;
|
|
102
|
-
|
|
103
|
-
case beforeContent.indexOf(CHAR_BOX) > -1:
|
|
104
|
-
listStyleType = 'box';
|
|
105
|
-
break;
|
|
106
|
-
|
|
107
|
-
case beforeContent.indexOf('lower-latin') > -1:
|
|
108
|
-
listStyleType = 'lower-latin';
|
|
109
|
-
break;
|
|
110
|
-
|
|
111
|
-
case beforeContent.indexOf('upper-latin') > -1:
|
|
112
|
-
listStyleType = 'upper-latin';
|
|
113
|
-
break;
|
|
114
|
-
|
|
115
|
-
case beforeContent.indexOf('lower-roman') > -1:
|
|
116
|
-
listStyleType = 'lower-roman';
|
|
117
|
-
break;
|
|
118
|
-
|
|
119
|
-
case beforeContent.indexOf('upper-roman') > -1:
|
|
120
|
-
listStyleType = 'upper-roman';
|
|
121
|
-
break;
|
|
122
|
-
|
|
123
|
-
case beforeContent.indexOf('-') > -1:
|
|
124
|
-
listStyleType = 'dash';
|
|
125
|
-
break;
|
|
126
|
-
|
|
127
|
-
default:
|
|
128
|
-
console.log('unknown list style type', beforeContent);
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
if (listStyleType) {
|
|
132
|
-
listElement.setAttribute(ATTRIBUTE_LIST_STYLE_TYPE, listStyleType);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// This moves the styles of <li /> into its content <p />.
|
|
138
|
-
function patchPaddingStyle(listItemElement: HTMLElement): void {
|
|
139
|
-
const { style, childNodes } = listItemElement;
|
|
140
|
-
const { paddingTop, paddingBottom, lineHeight } = style;
|
|
141
|
-
if (
|
|
142
|
-
!EMPTY_CSS_VALUE.has(paddingBottom) &&
|
|
143
|
-
!EMPTY_CSS_VALUE.has(paddingTop) &&
|
|
144
|
-
!EMPTY_CSS_VALUE.has(lineHeight)
|
|
145
|
-
) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const doc = listItemElement.ownerDocument;
|
|
150
|
-
const frag = doc.createDocumentFragment();
|
|
151
|
-
let contentIsInline = true;
|
|
152
|
-
|
|
153
|
-
Array.from(childNodes).forEach((cn) => {
|
|
154
|
-
contentIsInline =
|
|
155
|
-
contentIsInline && INLINE_NODE_NAME_PATTERN.test(cn.nodeName);
|
|
156
|
-
frag.appendChild(cn);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
if (contentIsInline) {
|
|
160
|
-
// Wrap all inline content with <p /> with the padding style applied.
|
|
161
|
-
const pEl = doc.createElement('p');
|
|
162
|
-
Object.assign(pEl.style, {
|
|
163
|
-
lineHeight,
|
|
164
|
-
paddingBottom,
|
|
165
|
-
paddingTop,
|
|
166
|
-
});
|
|
167
|
-
pEl.appendChild(frag);
|
|
168
|
-
listItemElement.appendChild(pEl);
|
|
169
|
-
} else {
|
|
170
|
-
// Unable to patch the style.
|
|
171
|
-
listItemElement.appendChild(frag);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// This converts all nested list elements into indented list elements.
|
|
176
|
-
// For instance,
|
|
177
|
-
// == UI ==
|
|
178
|
-
// 1. AA
|
|
179
|
-
// 1. BB
|
|
180
|
-
// 2. BB
|
|
181
|
-
// 2. AA
|
|
182
|
-
// == DOM Structure (Before) ==
|
|
183
|
-
// <ol> <!-- Parent List -->
|
|
184
|
-
// <li>
|
|
185
|
-
// AA
|
|
186
|
-
// <ol> <!-- Child (nested) List -->
|
|
187
|
-
// <li>BB</li>
|
|
188
|
-
// <li>BB</li>
|
|
189
|
-
// </ol>
|
|
190
|
-
// </li>
|
|
191
|
-
// <li> AA</li>
|
|
192
|
-
// </ol>
|
|
193
|
-
// == DOM Structure (After) ==
|
|
194
|
-
// <ol name="x">
|
|
195
|
-
// <li>AA</li>
|
|
196
|
-
// </ol>
|
|
197
|
-
// <ol data-indent="1">
|
|
198
|
-
// <li>BB</li>
|
|
199
|
-
// <li>BB</li>
|
|
200
|
-
// </ol>
|
|
201
|
-
// <ol data-following="x" data-counter-reset-"none">
|
|
202
|
-
// <li>AA</li>
|
|
203
|
-
// </ol>
|
|
204
|
-
function liftNestedListElements(doc: Document): void {
|
|
205
|
-
const selector = 'li > ol, li > ul';
|
|
206
|
-
const els = Array.from(doc.querySelectorAll(selector));
|
|
207
|
-
const htmlMutator = new HTMLMutator(doc);
|
|
208
|
-
|
|
209
|
-
els.forEach((list) => {
|
|
210
|
-
const indent = findIndentLevel(list);
|
|
211
|
-
list.setAttribute('data-indent', String(indent));
|
|
212
|
-
|
|
213
|
-
const parentListItem = list?.parentElement;
|
|
214
|
-
const parentList = parentListItem?.parentElement;
|
|
215
|
-
const parentListNodeName = parentList.nodeName.toLowerCase();
|
|
216
|
-
const isLast = parentList.lastElementChild === parentListItem;
|
|
217
|
-
const style = parentList.getAttribute('style') || '';
|
|
218
|
-
|
|
219
|
-
// The parent list will be split into two lists and the second list should
|
|
220
|
-
// follow the first list.
|
|
221
|
-
const followingName = parentList.getAttribute('name') || uuid();
|
|
222
|
-
parentList.setAttribute('name', followingName);
|
|
223
|
-
|
|
224
|
-
// Stub HTML snippets that will lift the list.
|
|
225
|
-
|
|
226
|
-
// Before:
|
|
227
|
-
// <ol>
|
|
228
|
-
// <li>
|
|
229
|
-
// AAA
|
|
230
|
-
// <ol><li>BBB</li></ol>
|
|
231
|
-
// </li>
|
|
232
|
-
// <li>CCC</li>
|
|
233
|
-
// </ol>
|
|
234
|
-
// After:
|
|
235
|
-
// <ol><li>AAA</li></ol>
|
|
236
|
-
// <ol><li>BBB</li></ol>
|
|
237
|
-
// <ol><li>CCC</li></ol>
|
|
238
|
-
|
|
239
|
-
// Close the parent list before the list.
|
|
240
|
-
htmlMutator.insertHTMLBefore(`</${parentListNodeName}>`, list);
|
|
241
|
-
// Open a new list after list.
|
|
242
|
-
htmlMutator.insertHTMLAfter(
|
|
243
|
-
`<${parentListNodeName}
|
|
244
|
-
style="${style}"
|
|
245
|
-
class="${parentList.className}"
|
|
246
|
-
${ATTRIBUTE_COUNTER_RESET}="none"
|
|
247
|
-
${ATTRIBUTE_FOLLOWING}="${followingName}">`,
|
|
248
|
-
list
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
if (isLast) {
|
|
252
|
-
// The new list after list is an empty list, comment it out.
|
|
253
|
-
htmlMutator
|
|
254
|
-
.insertHTMLAfter('<!--', list)
|
|
255
|
-
.insertHTMLAfter('-->', parentList);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
htmlMutator.execute();
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function findIndentLevel(el: Element): number {
|
|
263
|
-
let indent = 0;
|
|
264
|
-
let currentEl = el.parentElement;
|
|
265
|
-
while (currentEl) {
|
|
266
|
-
const { nodeName } = currentEl;
|
|
267
|
-
if (nodeName === 'OL' || nodeName === 'UL') {
|
|
268
|
-
indent++;
|
|
269
|
-
} else if (nodeName !== 'LI') {
|
|
270
|
-
break;
|
|
271
|
-
}
|
|
272
|
-
currentEl = currentEl.parentElement;
|
|
273
|
-
}
|
|
274
|
-
return indent;
|
|
275
|
-
}
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import HTMLMutator from './HTMLMutator.js';
|
|
4
|
+
import uuid from './ui/uuid.js';
|
|
5
|
+
|
|
6
|
+
import { ATTRIBUTE_LIST_STYLE_TYPE } from './ListItemNodeSpec.js';
|
|
7
|
+
import {
|
|
8
|
+
ATTRIBUTE_INDENT,
|
|
9
|
+
EMPTY_CSS_VALUE,
|
|
10
|
+
convertMarginLeftToIndentValue,
|
|
11
|
+
} from './ParagraphNodeSpec.js';
|
|
12
|
+
import {
|
|
13
|
+
ATTRIBUTE_COUNTER_RESET,
|
|
14
|
+
ATTRIBUTE_FOLLOWING,
|
|
15
|
+
} from './OrderedListNodeSpec.js';
|
|
16
|
+
import { ATTRIBUTE_CSS_BEFORE_CONTENT } from './patchStyleElements.js';
|
|
17
|
+
|
|
18
|
+
export default function patchListElements(doc: Document): void {
|
|
19
|
+
// In Google Doc, lists are exported as indented
|
|
20
|
+
// (e.g. style="margin-left: 48pt") list elements which is the default DOM
|
|
21
|
+
// structure that `czi-prosemirror` supports. However, other doc providers
|
|
22
|
+
// (e.g Office 365) may export lists as nested list elements that can't
|
|
23
|
+
// be rendered properly.
|
|
24
|
+
// Before proceeding further, it needs to convert the nested list elements
|
|
25
|
+
// into indented list elements.
|
|
26
|
+
liftNestedListElements(doc);
|
|
27
|
+
Array.from(doc.querySelectorAll('ol, ul')).forEach(patchListElementsElement);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// This assumes that every 36pt maps to one indent level.
|
|
31
|
+
const CHAR_BULLET = '\u25cf';
|
|
32
|
+
const CHAR_CIRCLE = '\u25cb';
|
|
33
|
+
const CHAR_SQUARE = '\u25a0';
|
|
34
|
+
const CHAR_BOX = '\u274f';
|
|
35
|
+
const CHAR_ZERO_SPACE = '\u200B';
|
|
36
|
+
const INLINE_NODE_NAME_PATTERN = /^(#text|(A|SPAN|B|STRONG))$/;
|
|
37
|
+
|
|
38
|
+
function patchListElementsElement(listElement: HTMLElement): void {
|
|
39
|
+
// If the children of `listElement` all have teh same marginLeft, assume
|
|
40
|
+
// it to be indented.
|
|
41
|
+
let marginLeft;
|
|
42
|
+
let beforeContent;
|
|
43
|
+
const { parentElement, children } = listElement;
|
|
44
|
+
|
|
45
|
+
// A workaround to patch the issue when <ul /> or <ol /> is pasted as the
|
|
46
|
+
// first child of <body />, its first <li /> somehow can't be wrapped
|
|
47
|
+
// with the list. The hack is to prepend zero-width-space character
|
|
48
|
+
// before the list.
|
|
49
|
+
if (
|
|
50
|
+
parentElement &&
|
|
51
|
+
parentElement.nodeName === 'BODY' &&
|
|
52
|
+
parentElement.firstChild === listElement
|
|
53
|
+
) {
|
|
54
|
+
const tt = parentElement.ownerDocument.createTextNode(CHAR_ZERO_SPACE);
|
|
55
|
+
parentElement.insertBefore(tt, listElement);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Array.from(children).forEach((listItemElement) => {
|
|
59
|
+
const { style } = listItemElement;
|
|
60
|
+
patchPaddingStyle(listItemElement);
|
|
61
|
+
|
|
62
|
+
const bc = listItemElement.getAttribute(ATTRIBUTE_CSS_BEFORE_CONTENT) || '';
|
|
63
|
+
if (beforeContent === undefined) {
|
|
64
|
+
beforeContent = bc;
|
|
65
|
+
}
|
|
66
|
+
if (beforeContent !== bc) {
|
|
67
|
+
beforeContent = null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const ml = (style?.marginLeft) || '';
|
|
71
|
+
if (marginLeft === undefined) {
|
|
72
|
+
marginLeft = ml;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (ml !== marginLeft) {
|
|
76
|
+
marginLeft = null;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (marginLeft) {
|
|
81
|
+
const indent = convertMarginLeftToIndentValue(marginLeft);
|
|
82
|
+
if (indent) {
|
|
83
|
+
listElement.setAttribute(ATTRIBUTE_INDENT, String(indent));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (beforeContent) {
|
|
88
|
+
beforeContent = String(beforeContent);
|
|
89
|
+
let listStyleType;
|
|
90
|
+
switch (true) {
|
|
91
|
+
case beforeContent.indexOf(CHAR_BULLET) > -1:
|
|
92
|
+
listStyleType = 'disc';
|
|
93
|
+
break;
|
|
94
|
+
|
|
95
|
+
case beforeContent.indexOf(CHAR_CIRCLE) > -1:
|
|
96
|
+
listStyleType = 'circle';
|
|
97
|
+
break;
|
|
98
|
+
|
|
99
|
+
case beforeContent.indexOf(CHAR_SQUARE) > -1:
|
|
100
|
+
listStyleType = 'square';
|
|
101
|
+
break;
|
|
102
|
+
|
|
103
|
+
case beforeContent.indexOf(CHAR_BOX) > -1:
|
|
104
|
+
listStyleType = 'box';
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case beforeContent.indexOf('lower-latin') > -1:
|
|
108
|
+
listStyleType = 'lower-latin';
|
|
109
|
+
break;
|
|
110
|
+
|
|
111
|
+
case beforeContent.indexOf('upper-latin') > -1:
|
|
112
|
+
listStyleType = 'upper-latin';
|
|
113
|
+
break;
|
|
114
|
+
|
|
115
|
+
case beforeContent.indexOf('lower-roman') > -1:
|
|
116
|
+
listStyleType = 'lower-roman';
|
|
117
|
+
break;
|
|
118
|
+
|
|
119
|
+
case beforeContent.indexOf('upper-roman') > -1:
|
|
120
|
+
listStyleType = 'upper-roman';
|
|
121
|
+
break;
|
|
122
|
+
|
|
123
|
+
case beforeContent.indexOf('-') > -1:
|
|
124
|
+
listStyleType = 'dash';
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
default:
|
|
128
|
+
console.log('unknown list style type', beforeContent);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
if (listStyleType) {
|
|
132
|
+
listElement.setAttribute(ATTRIBUTE_LIST_STYLE_TYPE, listStyleType);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// This moves the styles of <li /> into its content <p />.
|
|
138
|
+
function patchPaddingStyle(listItemElement: HTMLElement): void {
|
|
139
|
+
const { style, childNodes } = listItemElement;
|
|
140
|
+
const { paddingTop, paddingBottom, lineHeight } = style;
|
|
141
|
+
if (
|
|
142
|
+
!EMPTY_CSS_VALUE.has(paddingBottom) &&
|
|
143
|
+
!EMPTY_CSS_VALUE.has(paddingTop) &&
|
|
144
|
+
!EMPTY_CSS_VALUE.has(lineHeight)
|
|
145
|
+
) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const doc = listItemElement.ownerDocument;
|
|
150
|
+
const frag = doc.createDocumentFragment();
|
|
151
|
+
let contentIsInline = true;
|
|
152
|
+
|
|
153
|
+
Array.from(childNodes).forEach((cn) => {
|
|
154
|
+
contentIsInline =
|
|
155
|
+
contentIsInline && INLINE_NODE_NAME_PATTERN.test(cn.nodeName);
|
|
156
|
+
frag.appendChild(cn);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (contentIsInline) {
|
|
160
|
+
// Wrap all inline content with <p /> with the padding style applied.
|
|
161
|
+
const pEl = doc.createElement('p');
|
|
162
|
+
Object.assign(pEl.style, {
|
|
163
|
+
lineHeight,
|
|
164
|
+
paddingBottom,
|
|
165
|
+
paddingTop,
|
|
166
|
+
});
|
|
167
|
+
pEl.appendChild(frag);
|
|
168
|
+
listItemElement.appendChild(pEl);
|
|
169
|
+
} else {
|
|
170
|
+
// Unable to patch the style.
|
|
171
|
+
listItemElement.appendChild(frag);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// This converts all nested list elements into indented list elements.
|
|
176
|
+
// For instance,
|
|
177
|
+
// == UI ==
|
|
178
|
+
// 1. AA
|
|
179
|
+
// 1. BB
|
|
180
|
+
// 2. BB
|
|
181
|
+
// 2. AA
|
|
182
|
+
// == DOM Structure (Before) ==
|
|
183
|
+
// <ol> <!-- Parent List -->
|
|
184
|
+
// <li>
|
|
185
|
+
// AA
|
|
186
|
+
// <ol> <!-- Child (nested) List -->
|
|
187
|
+
// <li>BB</li>
|
|
188
|
+
// <li>BB</li>
|
|
189
|
+
// </ol>
|
|
190
|
+
// </li>
|
|
191
|
+
// <li> AA</li>
|
|
192
|
+
// </ol>
|
|
193
|
+
// == DOM Structure (After) ==
|
|
194
|
+
// <ol name="x">
|
|
195
|
+
// <li>AA</li>
|
|
196
|
+
// </ol>
|
|
197
|
+
// <ol data-indent="1">
|
|
198
|
+
// <li>BB</li>
|
|
199
|
+
// <li>BB</li>
|
|
200
|
+
// </ol>
|
|
201
|
+
// <ol data-following="x" data-counter-reset-"none">
|
|
202
|
+
// <li>AA</li>
|
|
203
|
+
// </ol>
|
|
204
|
+
function liftNestedListElements(doc: Document): void {
|
|
205
|
+
const selector = 'li > ol, li > ul';
|
|
206
|
+
const els = Array.from(doc.querySelectorAll(selector));
|
|
207
|
+
const htmlMutator = new HTMLMutator(doc);
|
|
208
|
+
|
|
209
|
+
els.forEach((list) => {
|
|
210
|
+
const indent = findIndentLevel(list);
|
|
211
|
+
list.setAttribute('data-indent', String(indent));
|
|
212
|
+
|
|
213
|
+
const parentListItem = list?.parentElement;
|
|
214
|
+
const parentList = parentListItem?.parentElement;
|
|
215
|
+
const parentListNodeName = parentList.nodeName.toLowerCase();
|
|
216
|
+
const isLast = parentList.lastElementChild === parentListItem;
|
|
217
|
+
const style = parentList.getAttribute('style') || '';
|
|
218
|
+
|
|
219
|
+
// The parent list will be split into two lists and the second list should
|
|
220
|
+
// follow the first list.
|
|
221
|
+
const followingName = parentList.getAttribute('name') || uuid();
|
|
222
|
+
parentList.setAttribute('name', followingName);
|
|
223
|
+
|
|
224
|
+
// Stub HTML snippets that will lift the list.
|
|
225
|
+
|
|
226
|
+
// Before:
|
|
227
|
+
// <ol>
|
|
228
|
+
// <li>
|
|
229
|
+
// AAA
|
|
230
|
+
// <ol><li>BBB</li></ol>
|
|
231
|
+
// </li>
|
|
232
|
+
// <li>CCC</li>
|
|
233
|
+
// </ol>
|
|
234
|
+
// After:
|
|
235
|
+
// <ol><li>AAA</li></ol>
|
|
236
|
+
// <ol><li>BBB</li></ol>
|
|
237
|
+
// <ol><li>CCC</li></ol>
|
|
238
|
+
|
|
239
|
+
// Close the parent list before the list.
|
|
240
|
+
htmlMutator.insertHTMLBefore(`</${parentListNodeName}>`, list);
|
|
241
|
+
// Open a new list after list.
|
|
242
|
+
htmlMutator.insertHTMLAfter(
|
|
243
|
+
`<${parentListNodeName}
|
|
244
|
+
style="${style}"
|
|
245
|
+
class="${parentList.className}"
|
|
246
|
+
${ATTRIBUTE_COUNTER_RESET}="none"
|
|
247
|
+
${ATTRIBUTE_FOLLOWING}="${followingName}">`,
|
|
248
|
+
list
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
if (isLast) {
|
|
252
|
+
// The new list after list is an empty list, comment it out.
|
|
253
|
+
htmlMutator
|
|
254
|
+
.insertHTMLAfter('<!--', list)
|
|
255
|
+
.insertHTMLAfter('-->', parentList);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
htmlMutator.execute();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function findIndentLevel(el: Element): number {
|
|
263
|
+
let indent = 0;
|
|
264
|
+
let currentEl = el.parentElement;
|
|
265
|
+
while (currentEl) {
|
|
266
|
+
const { nodeName } = currentEl;
|
|
267
|
+
if (nodeName === 'OL' || nodeName === 'UL') {
|
|
268
|
+
indent++;
|
|
269
|
+
} else if (nodeName !== 'LI') {
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
currentEl = currentEl.parentElement;
|
|
273
|
+
}
|
|
274
|
+
return indent;
|
|
275
|
+
}
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
export default function patchMathElements(doc: Document): void {
|
|
4
|
-
Array.from(doc.querySelectorAll('img')).forEach(patchGoogleEquationElement);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
// See https://developers.google.com/chart/image/docs/chart_params#gcharts_cht
|
|
8
|
-
const PARAM_CHART_CHART_TYPE = 'cht';
|
|
9
|
-
const PARAM_CHART_LABEL = 'chl';
|
|
10
|
-
|
|
11
|
-
// Google Doc exports math equation content as single image element that loads
|
|
12
|
-
// its content from google. For example:
|
|
13
|
-
// <img src="https://www.google.com/chart?cht=tx&c...p;chl=m%E2%88%A0C" />
|
|
14
|
-
// Unfortunately, such image often fails to load because its url contains the
|
|
15
|
-
// value that the Google Chart API does not support.
|
|
16
|
-
// The workaround is to use KaTex (https://katex.org/) whoch supports a broader
|
|
17
|
-
// set of characters that can be safely converted into math quations.
|
|
18
|
-
|
|
19
|
-
function patchGoogleEquationElement(el: HTMLElement): void {
|
|
20
|
-
const { ownerDocument, parentElement } = el;
|
|
21
|
-
if (!ownerDocument || !parentElement) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
const src = el.getAttribute('src');
|
|
25
|
-
const content = getGoogleEquationContent(src);
|
|
26
|
-
if (!content) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Replace `<img src="..." />` with `<math data-latex="..." />`.
|
|
31
|
-
// Note that this requires the schema to support `MathNodeSpec`.
|
|
32
|
-
const math = ownerDocument.createElement('math');
|
|
33
|
-
math.setAttribute('data-latex', content);
|
|
34
|
-
parentElement.insertBefore(math, el);
|
|
35
|
-
parentElement.removeChild(el);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getGoogleEquationContent(src: ?string): ?string {
|
|
39
|
-
if (!src) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
const { host, pathname, query } = new URL(src);
|
|
43
|
-
if (host !== 'www.google.com' || pathname !== '/chart') {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const params = new URL(query);
|
|
48
|
-
const chartType = params[PARAM_CHART_CHART_TYPE];
|
|
49
|
-
const label = params[PARAM_CHART_LABEL];
|
|
50
|
-
|
|
51
|
-
// Google exports math equation as a special chart with plan text only
|
|
52
|
-
// contents.
|
|
53
|
-
if (chartType !== 'tx' || !label) {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return label;
|
|
58
|
-
}
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
export default function patchMathElements(doc: Document): void {
|
|
4
|
+
Array.from(doc.querySelectorAll('img')).forEach(patchGoogleEquationElement);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// See https://developers.google.com/chart/image/docs/chart_params#gcharts_cht
|
|
8
|
+
const PARAM_CHART_CHART_TYPE = 'cht';
|
|
9
|
+
const PARAM_CHART_LABEL = 'chl';
|
|
10
|
+
|
|
11
|
+
// Google Doc exports math equation content as single image element that loads
|
|
12
|
+
// its content from google. For example:
|
|
13
|
+
// <img src="https://www.google.com/chart?cht=tx&c...p;chl=m%E2%88%A0C" />
|
|
14
|
+
// Unfortunately, such image often fails to load because its url contains the
|
|
15
|
+
// value that the Google Chart API does not support.
|
|
16
|
+
// The workaround is to use KaTex (https://katex.org/) whoch supports a broader
|
|
17
|
+
// set of characters that can be safely converted into math quations.
|
|
18
|
+
|
|
19
|
+
function patchGoogleEquationElement(el: HTMLElement): void {
|
|
20
|
+
const { ownerDocument, parentElement } = el;
|
|
21
|
+
if (!ownerDocument || !parentElement) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const src = el.getAttribute('src');
|
|
25
|
+
const content = getGoogleEquationContent(src);
|
|
26
|
+
if (!content) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Replace `<img src="..." />` with `<math data-latex="..." />`.
|
|
31
|
+
// Note that this requires the schema to support `MathNodeSpec`.
|
|
32
|
+
const math = ownerDocument.createElement('math');
|
|
33
|
+
math.setAttribute('data-latex', content);
|
|
34
|
+
parentElement.insertBefore(math, el);
|
|
35
|
+
parentElement.removeChild(el);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getGoogleEquationContent(src: ?string): ?string {
|
|
39
|
+
if (!src) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const { host, pathname, query } = new URL(src);
|
|
43
|
+
if (host !== 'www.google.com' || pathname !== '/chart') {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const params = new URL(query);
|
|
48
|
+
const chartType = params[PARAM_CHART_CHART_TYPE];
|
|
49
|
+
const label = params[PARAM_CHART_LABEL];
|
|
50
|
+
|
|
51
|
+
// Google exports math equation as a special chart with plan text only
|
|
52
|
+
// contents.
|
|
53
|
+
if (chartType !== 'tx' || !label) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return label;
|
|
58
|
+
}
|