@modusoperandi/licit 1.4.2 → 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.
Files changed (228) hide show
  1. package/BlockquoteInsertNewLineCommand.js.flow +70 -70
  2. package/BlockquoteNodeSpec.js.flow +29 -29
  3. package/BlockquoteToggleCommand.js.flow +56 -56
  4. package/BookmarkNodeSpec.js.flow +39 -39
  5. package/BulletListNodeSpec.js.flow +61 -61
  6. package/CZIProseMirror.js.flow +90 -90
  7. package/CodeBlockCommand.js.flow +65 -65
  8. package/CodeBlockNodeSpec.js.flow +24 -24
  9. package/CodeMarkSpec.js.flow +14 -14
  10. package/ContentPlaceholderPlugin.js.flow +183 -183
  11. package/CursorPlaceholderPlugin.js.flow +113 -113
  12. package/DocLayoutCommand.js.flow +97 -97
  13. package/DocNodeSpec.js.flow +62 -62
  14. package/EMMarkSpec.js.flow +39 -39
  15. package/EditorCommands.js.flow +124 -124
  16. package/EditorKeyMap.js.flow +173 -173
  17. package/EditorMarks.js +4 -2
  18. package/EditorMarks.js.flow +77 -74
  19. package/EditorNodes.js.flow +55 -55
  20. package/EditorPageLayoutPlugin.js.flow +67 -67
  21. package/EditorPlugins.js.flow +8 -8
  22. package/EditorSchema.js.flow +12 -12
  23. package/EditorState.js.flow +7 -7
  24. package/FontSizeMarkSpec.js.flow +54 -54
  25. package/FontTypeMarkSpec.js.flow +89 -89
  26. package/HTMLMutator.js.flow +58 -58
  27. package/HangingIndentMarkSpec.js +34 -0
  28. package/HangingIndentMarkSpec.js.flow +30 -0
  29. package/HardBreakNodeSpec.js.flow +15 -15
  30. package/HeadingNodeSpec.js.flow +42 -42
  31. package/HistoryRedoCommand.js.flow +41 -41
  32. package/HistoryUndoCommand.js.flow +41 -41
  33. package/HorizontalRuleCommand.js.flow +71 -71
  34. package/HorizontalRuleNodeSpec.js.flow +39 -39
  35. package/ImageUploadPlaceholderPlugin.js.flow +187 -187
  36. package/LICENSE +22 -22
  37. package/LinkMarkSpec.js +4 -2
  38. package/LinkMarkSpec.js.flow +38 -37
  39. package/LinkSetURLCommand.js +1 -1
  40. package/LinkSetURLCommand.js.flow +129 -129
  41. package/LinkTooltipPlugin.js +32 -38
  42. package/LinkTooltipPlugin.js.flow +204 -217
  43. package/ListItemInsertNewLineCommand.js.flow +76 -76
  44. package/ListItemMergeCommand.js.flow +197 -197
  45. package/ListItemNodeSpec.js.flow +52 -52
  46. package/ListSplitCommand.js.flow +54 -54
  47. package/ListToggleCommand.js.flow +99 -99
  48. package/MarkNames.js +2 -1
  49. package/MarkNames.js.flow +20 -19
  50. package/MarksClearCommand.js.flow +66 -66
  51. package/NodeNames.js.flow +23 -23
  52. package/OrderedListNodeSpec.js.flow +131 -131
  53. package/OverrideMarkSpec.js.flow +49 -49
  54. package/ParagraphNodeSpec.js +21 -2
  55. package/ParagraphNodeSpec.js.flow +233 -215
  56. package/ParagraphSpacingCommand.js.flow +139 -139
  57. package/PrintCommand.js.flow +53 -53
  58. package/SelectionPlaceholderPlugin.js.flow +129 -129
  59. package/SpacerMarkSpec.js.flow +47 -47
  60. package/StrikeMarkSpec.js.flow +24 -24
  61. package/StrongMarkSpec.js.flow +39 -39
  62. package/StyleView.js.flow +18 -18
  63. package/TableBackgroundColorCommand.js.flow +79 -79
  64. package/TableBorderColorCommand.js +5 -3
  65. package/TableBorderColorCommand.js.flow +108 -106
  66. package/TableCellColorCommand.js.flow +73 -73
  67. package/TableCellMenuPlugin.js.flow +129 -129
  68. package/TableInsertCommand.js.flow +117 -117
  69. package/TableMergeCellsCommand.js.flow +113 -113
  70. package/TableNodesSpecs.js.flow +144 -144
  71. package/TablePlugins.js.flow +14 -14
  72. package/TableResizePlugin.js.flow +636 -636
  73. package/TextColorMarkSpec.js.flow +40 -40
  74. package/TextHighlightMarkSpec.js.flow +47 -47
  75. package/TextInsertTabSpaceCommand.js +0 -3
  76. package/TextInsertTabSpaceCommand.js.flow +103 -106
  77. package/TextNoWrapMarkSpec.js.flow +14 -14
  78. package/TextNodeSpec.js.flow +7 -7
  79. package/TextSelectionMarkSpec.js.flow +24 -24
  80. package/TextSubMarkSpec.js.flow +28 -28
  81. package/TextSuperMarkSpec.js.flow +28 -28
  82. package/TextUnderlineMarkSpec.js.flow +24 -24
  83. package/Types.js.flow +77 -77
  84. package/WebFontLoader.js.flow +22 -22
  85. package/blockQuoteInputRule.js.flow +36 -36
  86. package/bom.xml +8645 -8637
  87. package/browser.js.flow +7 -7
  88. package/buildEditorPlugins.js.flow +48 -48
  89. package/buildInputRules.js.flow +85 -85
  90. package/client/CollabConnector.js.flow +90 -90
  91. package/client/EditorConnection.js.flow +324 -324
  92. package/client/Licit.js +40 -40
  93. package/client/Licit.js.flow +649 -648
  94. package/client/Licit.test.js.flow +104 -104
  95. package/client/Reporter.js.flow +35 -35
  96. package/client/SimpleConnector.js.flow +61 -61
  97. package/client/http.js.flow +60 -60
  98. package/client/licit.css +12 -12
  99. package/client/throttle.js.flow +27 -27
  100. package/convertFromDOMElement.js.flow +33 -33
  101. package/convertFromHTML.js.flow +15 -15
  102. package/convertFromJSON.js.flow +53 -53
  103. package/convertToCSSPTValue.js.flow +19 -19
  104. package/convertToJSON.js.flow +7 -7
  105. package/createCommand.js.flow +62 -62
  106. package/createEditorKeyMap.js.flow +87 -87
  107. package/createEmptyEditorState.js.flow +29 -29
  108. package/createTableResizingPlugin.js.flow +86 -86
  109. package/findActionableCell.js.flow +74 -74
  110. package/findActiveMark.js.flow +32 -32
  111. package/hyphenize.js.flow +17 -17
  112. package/index.d.ts +167 -167
  113. package/index.js.flow +10 -10
  114. package/insertTable.js.flow +54 -54
  115. package/isEditorStateEmpty.js.flow +32 -32
  116. package/isTableNode.js.flow +15 -15
  117. package/joinDown.js.flow +25 -25
  118. package/joinListNode.js.flow +55 -55
  119. package/joinUp.js.flow +37 -37
  120. package/keymaps.js.flow +175 -175
  121. package/lookUpElement.js.flow +14 -14
  122. package/nodeAt.js.flow +12 -12
  123. package/normalizeHTML.js.flow +80 -80
  124. package/package.json +152 -152
  125. package/patchAnchorElements.js.flow +38 -38
  126. package/patchBreakElements.js.flow +22 -22
  127. package/patchElementInlineStyles.js.flow +92 -92
  128. package/patchListElements.js.flow +275 -275
  129. package/patchMathElements.js.flow +58 -58
  130. package/patchParagraphElements.js.flow +20 -20
  131. package/patchStyleElements.js.flow +197 -197
  132. package/patchTableElements.js.flow +88 -88
  133. package/rebaseDocWithSteps.js.flow +42 -42
  134. package/sanitizeURL.js.flow +13 -13
  135. package/splitListItem.js.flow +191 -191
  136. package/styles.css +46 -46
  137. package/toClosestFontPtSize.js.flow +22 -22
  138. package/toSafeHTMLDocument.js.flow +9 -9
  139. package/toggleBlockquote.js.flow +101 -101
  140. package/toggleCodeBlock.js.flow +102 -102
  141. package/ui/AlertInfo.js.flow +63 -63
  142. package/ui/BookmarkNodeView.js.flow +64 -64
  143. package/ui/CommandButton.js.flow +68 -68
  144. package/ui/CommandMenu.js.flow +74 -74
  145. package/ui/CommandMenuButton.js.flow +128 -128
  146. package/ui/CustomEditorView.js.flow +29 -29
  147. package/ui/CustomMenu.js.flow +14 -14
  148. package/ui/CustomMenuItem.js.flow +35 -35
  149. package/ui/CustomNodeView.js.flow +207 -207
  150. package/ui/CustomRadioButton.js.flow +63 -63
  151. package/ui/DocLayoutEditor.js.flow +130 -130
  152. package/ui/Editor.js.flow +281 -281
  153. package/ui/EditorFrameset.js.flow +79 -79
  154. package/ui/EditorToolbar.js.flow +197 -197
  155. package/ui/EditorToolbarConfig.js.flow +162 -162
  156. package/ui/FontSizeCommandMenuButton.js.flow +66 -66
  157. package/ui/FontTypeCommandMenuButton.js.flow +60 -60
  158. package/ui/Frag.js.flow +11 -11
  159. package/ui/Icon.js +1 -1
  160. package/ui/Icon.js.flow +82 -82
  161. package/ui/ImageInlineEditor.js.flow +66 -66
  162. package/ui/KeyCodes.js.flow +12 -12
  163. package/ui/LinkTooltip.js.flow +51 -51
  164. package/ui/LinkURLEditor.js.flow +227 -227
  165. package/ui/ListItemNodeView.js.flow +101 -101
  166. package/ui/ListTypeButton.js.flow +121 -121
  167. package/ui/ListTypeCommandButton.js.flow +85 -85
  168. package/ui/ListTypeMenu.js.flow +78 -78
  169. package/ui/LoadingIndicator.js.flow +19 -19
  170. package/ui/PasteMenu.js.flow +52 -52
  171. package/ui/ResizeObserver.js.flow +105 -105
  172. package/ui/RichTextEditor.js.flow +133 -133
  173. package/ui/SelectionObserver.js.flow +134 -134
  174. package/ui/TableCellMenu.js.flow +49 -49
  175. package/ui/TableGridSizeEditor.js.flow +185 -185
  176. package/ui/TableNodeView.js.flow +36 -36
  177. package/ui/bindScrollHandler.js.flow +46 -46
  178. package/ui/canUseCSSFont.js.flow +42 -42
  179. package/ui/czi-body-layout-editor.css +16 -16
  180. package/ui/czi-bookmark-view.css +10 -10
  181. package/ui/czi-cursor-placeholder.css +36 -36
  182. package/ui/czi-custom-menu-button.css +18 -18
  183. package/ui/czi-custom-menu-item.css +30 -30
  184. package/ui/czi-custom-menu.css +8 -8
  185. package/ui/czi-custom-radio-button.css +80 -80
  186. package/ui/czi-custom-scrollbar.css +21 -21
  187. package/ui/czi-editor-frameset.css +81 -81
  188. package/ui/czi-editor-toolbar.css +122 -122
  189. package/ui/czi-editor.css +239 -216
  190. package/ui/czi-form.css +201 -201
  191. package/ui/czi-frag.css +3 -3
  192. package/ui/czi-heading.css +40 -40
  193. package/ui/czi-icon.css +72 -72
  194. package/ui/czi-image-resize-box.css +165 -165
  195. package/ui/czi-image-upload-editor.css +57 -57
  196. package/ui/czi-image-upload-placeholder.css +50 -50
  197. package/ui/czi-image-url-editor.css +38 -38
  198. package/ui/czi-image-view.css +121 -121
  199. package/ui/czi-indent.css +137 -137
  200. package/ui/czi-inline-editor.css +20 -20
  201. package/ui/czi-link-tooltip.css +112 -112
  202. package/ui/czi-list.css +406 -406
  203. package/ui/czi-loading-indicator.css +66 -66
  204. package/ui/czi-math-view.css +62 -62
  205. package/ui/czi-selection-placeholder.css +24 -24
  206. package/ui/czi-table-cell-menu.css +16 -16
  207. package/ui/czi-table-grid-size-editor.css +37 -37
  208. package/ui/czi-table.css +89 -89
  209. package/ui/czi-vars.css +47 -45
  210. package/ui/findActiveFontSize.js.flow +55 -55
  211. package/ui/findActiveFontType.js.flow +35 -35
  212. package/ui/fonts.css +460 -460
  213. package/ui/handleEditorDrop.js.flow +28 -28
  214. package/ui/handleEditorKeyDown.js.flow +39 -39
  215. package/ui/handleEditorPaste.js.flow +33 -33
  216. package/ui/htmlElementToRect.js.flow +18 -18
  217. package/ui/icon-font.css +9 -9
  218. package/ui/injectStyleSheet.js.flow +40 -40
  219. package/ui/isElementFullyVisible.js.flow +14 -14
  220. package/ui/isOffline.js.flow +8 -8
  221. package/ui/isReactClass.js.flow +12 -12
  222. package/ui/listType.css +21 -21
  223. package/ui/resolveImage.js.flow +121 -121
  224. package/ui/toCSSColor.js.flow +51 -51
  225. package/ui/toCSSLineSpacing.js.flow +53 -53
  226. package/ui/toHexColor.js.flow +26 -26
  227. package/ui/uuid.js.flow +9 -9
  228. 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&amp;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&amp;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
+ }