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