@fiduswriter/editor 0.1.0

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 (525) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +22 -0
  3. package/dist/bibliography/tools.js +53 -0
  4. package/dist/bibliography/tools.js.map +1 -0
  5. package/dist/citations/index.js +339 -0
  6. package/dist/citations/index.js.map +1 -0
  7. package/dist/citations/render.js +47 -0
  8. package/dist/citations/render.js.map +1 -0
  9. package/dist/clipboard/copy/index.js +2 -0
  10. package/dist/clipboard/copy/index.js.map +1 -0
  11. package/dist/clipboard/copy/schema.js +54 -0
  12. package/dist/clipboard/copy/schema.js.map +1 -0
  13. package/dist/clipboard/copy/serializers.js +132 -0
  14. package/dist/clipboard/copy/serializers.js.map +1 -0
  15. package/dist/clipboard/paste/fidus_writer.js +45 -0
  16. package/dist/clipboard/paste/fidus_writer.js.map +1 -0
  17. package/dist/clipboard/paste/general.js +76 -0
  18. package/dist/clipboard/paste/general.js.map +1 -0
  19. package/dist/clipboard/paste/google_docs.js +24 -0
  20. package/dist/clipboard/paste/google_docs.js.map +1 -0
  21. package/dist/clipboard/paste/html.js +68 -0
  22. package/dist/clipboard/paste/html.js.map +1 -0
  23. package/dist/clipboard/paste/index.js +3 -0
  24. package/dist/clipboard/paste/index.js.map +1 -0
  25. package/dist/clipboard/paste/libreoffice_writer.js +27 -0
  26. package/dist/clipboard/paste/libreoffice_writer.js.map +1 -0
  27. package/dist/clipboard/paste/microsoft_word.js +79 -0
  28. package/dist/clipboard/paste/microsoft_word.js.map +1 -0
  29. package/dist/clipboard/paste/text.js +41 -0
  30. package/dist/clipboard/paste/text.js.map +1 -0
  31. package/dist/collab/chat.js +154 -0
  32. package/dist/collab/chat.js.map +1 -0
  33. package/dist/collab/colors.js +62 -0
  34. package/dist/collab/colors.js.map +1 -0
  35. package/dist/collab/doc.js +1090 -0
  36. package/dist/collab/doc.js.map +1 -0
  37. package/dist/collab/index.js +75 -0
  38. package/dist/collab/index.js.map +1 -0
  39. package/dist/collab/merge/changeset.js +90 -0
  40. package/dist/collab/merge/changeset.js.map +1 -0
  41. package/dist/collab/merge/editor.js +708 -0
  42. package/dist/collab/merge/editor.js.map +1 -0
  43. package/dist/collab/merge/footnotes.js +149 -0
  44. package/dist/collab/merge/footnotes.js.map +1 -0
  45. package/dist/collab/merge/index.js +270 -0
  46. package/dist/collab/merge/index.js.map +1 -0
  47. package/dist/collab/merge/recreate_transform.js +259 -0
  48. package/dist/collab/merge/recreate_transform.js.map +1 -0
  49. package/dist/collab/merge/schema.js +117 -0
  50. package/dist/collab/merge/schema.js.map +1 -0
  51. package/dist/collab/merge/state_plugins/action.js +138 -0
  52. package/dist/collab/merge/state_plugins/action.js.map +1 -0
  53. package/dist/collab/merge/state_plugins/clipboard.js +32 -0
  54. package/dist/collab/merge/state_plugins/clipboard.js.map +1 -0
  55. package/dist/collab/merge/state_plugins/diff.js +541 -0
  56. package/dist/collab/merge/state_plugins/diff.js.map +1 -0
  57. package/dist/collab/merge/state_plugins/index.js +3 -0
  58. package/dist/collab/merge/state_plugins/index.js.map +1 -0
  59. package/dist/collab/merge/tools.js +175 -0
  60. package/dist/collab/merge/tools.js.map +1 -0
  61. package/dist/collab/templates.js +10 -0
  62. package/dist/collab/templates.js.map +1 -0
  63. package/dist/comments/comment.js +17 -0
  64. package/dist/comments/comment.js.map +1 -0
  65. package/dist/comments/editors/answer.js +43 -0
  66. package/dist/comments/editors/answer.js.map +1 -0
  67. package/dist/comments/editors/comment.js +278 -0
  68. package/dist/comments/editors/comment.js.map +1 -0
  69. package/dist/comments/editors/index.js +4 -0
  70. package/dist/comments/editors/index.js.map +1 -0
  71. package/dist/comments/editors/notify.js +13 -0
  72. package/dist/comments/editors/notify.js.map +1 -0
  73. package/dist/comments/editors/schema.js +60 -0
  74. package/dist/comments/editors/schema.js.map +1 -0
  75. package/dist/comments/index.js +11 -0
  76. package/dist/comments/index.js.map +1 -0
  77. package/dist/comments/interactions.js +379 -0
  78. package/dist/comments/interactions.js.map +1 -0
  79. package/dist/comments/store.js +438 -0
  80. package/dist/comments/store.js.map +1 -0
  81. package/dist/contacts/add_dialog.js +88 -0
  82. package/dist/contacts/add_dialog.js.map +1 -0
  83. package/dist/contacts/templates.js +23 -0
  84. package/dist/contacts/templates.js.map +1 -0
  85. package/dist/copyright_dialog/index.js +148 -0
  86. package/dist/copyright_dialog/index.js.map +1 -0
  87. package/dist/copyright_dialog/templates.js +48 -0
  88. package/dist/copyright_dialog/templates.js.map +1 -0
  89. package/dist/databases/bibliography.js +162 -0
  90. package/dist/databases/bibliography.js.map +1 -0
  91. package/dist/databases/images.js +260 -0
  92. package/dist/databases/images.js.map +1 -0
  93. package/dist/databases/index.js +55 -0
  94. package/dist/databases/index.js.map +1 -0
  95. package/dist/dialogs/citation.js +321 -0
  96. package/dist/dialogs/citation.js.map +1 -0
  97. package/dist/dialogs/code_block.js +192 -0
  98. package/dist/dialogs/code_block.js.map +1 -0
  99. package/dist/dialogs/contributor.js +132 -0
  100. package/dist/dialogs/contributor.js.map +1 -0
  101. package/dist/dialogs/figure.js +378 -0
  102. package/dist/dialogs/figure.js.map +1 -0
  103. package/dist/dialogs/index.js +11 -0
  104. package/dist/dialogs/index.js.map +1 -0
  105. package/dist/dialogs/language.js +47 -0
  106. package/dist/dialogs/language.js.map +1 -0
  107. package/dist/dialogs/link.js +243 -0
  108. package/dist/dialogs/link.js.map +1 -0
  109. package/dist/dialogs/math.js +121 -0
  110. package/dist/dialogs/math.js.map +1 -0
  111. package/dist/dialogs/ordered_list.js +67 -0
  112. package/dist/dialogs/ordered_list.js.map +1 -0
  113. package/dist/dialogs/revision.js +36 -0
  114. package/dist/dialogs/revision.js.map +1 -0
  115. package/dist/dialogs/subsup.js +109 -0
  116. package/dist/dialogs/subsup.js.map +1 -0
  117. package/dist/dialogs/table.js +217 -0
  118. package/dist/dialogs/table.js.map +1 -0
  119. package/dist/dialogs/templates.js +366 -0
  120. package/dist/dialogs/templates.js.map +1 -0
  121. package/dist/document_template/exporter.js +83 -0
  122. package/dist/document_template/exporter.js.map +1 -0
  123. package/dist/document_template/index.js +327 -0
  124. package/dist/document_template/index.js.map +1 -0
  125. package/dist/documents/access_rights/index.js +532 -0
  126. package/dist/documents/access_rights/index.js.map +1 -0
  127. package/dist/documents/access_rights/templates.js +150 -0
  128. package/dist/documents/access_rights/templates.js.map +1 -0
  129. package/dist/e2ee/encryptor.js +201 -0
  130. package/dist/e2ee/encryptor.js.map +1 -0
  131. package/dist/e2ee/key-manager.js +171 -0
  132. package/dist/e2ee/key-manager.js.map +1 -0
  133. package/dist/e2ee/passphrase-crypto.js +365 -0
  134. package/dist/e2ee/passphrase-crypto.js.map +1 -0
  135. package/dist/e2ee/passphrase-dialog.js +548 -0
  136. package/dist/e2ee/passphrase-dialog.js.map +1 -0
  137. package/dist/e2ee/passphrase-manager.js +350 -0
  138. package/dist/e2ee/passphrase-manager.js.map +1 -0
  139. package/dist/e2ee/password-dialog.js +552 -0
  140. package/dist/e2ee/password-dialog.js.map +1 -0
  141. package/dist/e2ee/snapshot-manager.js +238 -0
  142. package/dist/e2ee/snapshot-manager.js.map +1 -0
  143. package/dist/exporter/native/copy.js +29 -0
  144. package/dist/exporter/native/copy.js.map +1 -0
  145. package/dist/exporter/native/file.js +19 -0
  146. package/dist/exporter/native/file.js.map +1 -0
  147. package/dist/exporter/native/index.js +4 -0
  148. package/dist/exporter/native/index.js.map +1 -0
  149. package/dist/exporter/native/revision.js +54 -0
  150. package/dist/exporter/native/revision.js.map +1 -0
  151. package/dist/exporter/native/zip.js +2 -0
  152. package/dist/exporter/native/zip.js.map +1 -0
  153. package/dist/footnotes/editor.js +180 -0
  154. package/dist/footnotes/editor.js.map +1 -0
  155. package/dist/footnotes/index.js +15 -0
  156. package/dist/footnotes/index.js.map +1 -0
  157. package/dist/footnotes/layout.js +125 -0
  158. package/dist/footnotes/layout.js.map +1 -0
  159. package/dist/footnotes/state_plugins/access_rights.js +25 -0
  160. package/dist/footnotes/state_plugins/access_rights.js.map +1 -0
  161. package/dist/footnotes/state_plugins/index.js +2 -0
  162. package/dist/footnotes/state_plugins/index.js.map +1 -0
  163. package/dist/images/edit_dialog/edit_dialog/index.js +186 -0
  164. package/dist/images/edit_dialog/edit_dialog/index.js.map +1 -0
  165. package/dist/images/edit_dialog/edit_dialog/model.js +152 -0
  166. package/dist/images/edit_dialog/edit_dialog/model.js.map +1 -0
  167. package/dist/images/edit_dialog/edit_dialog/templates.js +33 -0
  168. package/dist/images/edit_dialog/edit_dialog/templates.js.map +1 -0
  169. package/dist/images/edit_dialog/model.js +6 -0
  170. package/dist/images/edit_dialog/model.js.map +1 -0
  171. package/dist/images/selection_dialog/index.js +149 -0
  172. package/dist/images/selection_dialog/index.js.map +1 -0
  173. package/dist/index.js +1002 -0
  174. package/dist/index.js.map +1 -0
  175. package/dist/keymap.js +73 -0
  176. package/dist/keymap.js.map +1 -0
  177. package/dist/marginboxes/index.js +674 -0
  178. package/dist/marginboxes/index.js.map +1 -0
  179. package/dist/marginboxes/templates.js +447 -0
  180. package/dist/marginboxes/templates.js.map +1 -0
  181. package/dist/menus/code_block/index.js +2 -0
  182. package/dist/menus/code_block/index.js.map +1 -0
  183. package/dist/menus/code_block/model.js +49 -0
  184. package/dist/menus/code_block/model.js.map +1 -0
  185. package/dist/menus/figure/index.js +3 -0
  186. package/dist/menus/figure/index.js.map +1 -0
  187. package/dist/menus/figure/model.js +80 -0
  188. package/dist/menus/figure/model.js.map +1 -0
  189. package/dist/menus/figure/utils.js +6 -0
  190. package/dist/menus/figure/utils.js.map +1 -0
  191. package/dist/menus/headerbar/index.js +3 -0
  192. package/dist/menus/headerbar/index.js.map +1 -0
  193. package/dist/menus/headerbar/model.js +743 -0
  194. package/dist/menus/headerbar/model.js.map +1 -0
  195. package/dist/menus/headerbar/view.js +579 -0
  196. package/dist/menus/headerbar/view.js.map +1 -0
  197. package/dist/menus/image/index.js +2 -0
  198. package/dist/menus/image/index.js.map +1 -0
  199. package/dist/menus/image/model.js +32 -0
  200. package/dist/menus/image/model.js.map +1 -0
  201. package/dist/menus/index.js +10 -0
  202. package/dist/menus/index.js.map +1 -0
  203. package/dist/menus/navigator/index.js +2 -0
  204. package/dist/menus/navigator/index.js.map +1 -0
  205. package/dist/menus/navigator/model.js +29 -0
  206. package/dist/menus/navigator/model.js.map +1 -0
  207. package/dist/menus/ordered_list/index.js +2 -0
  208. package/dist/menus/ordered_list/index.js.map +1 -0
  209. package/dist/menus/ordered_list/model.js +17 -0
  210. package/dist/menus/ordered_list/model.js.map +1 -0
  211. package/dist/menus/selection/index.js +3 -0
  212. package/dist/menus/selection/index.js.map +1 -0
  213. package/dist/menus/selection/model.js +108 -0
  214. package/dist/menus/selection/model.js.map +1 -0
  215. package/dist/menus/selection/view.js +125 -0
  216. package/dist/menus/selection/view.js.map +1 -0
  217. package/dist/menus/table/index.js +2 -0
  218. package/dist/menus/table/index.js.map +1 -0
  219. package/dist/menus/table/model.js +319 -0
  220. package/dist/menus/table/model.js.map +1 -0
  221. package/dist/menus/toolbar/index.js +3 -0
  222. package/dist/menus/toolbar/index.js.map +1 -0
  223. package/dist/menus/toolbar/model.js +747 -0
  224. package/dist/menus/toolbar/model.js.map +1 -0
  225. package/dist/menus/toolbar/view.js +314 -0
  226. package/dist/menus/toolbar/view.js.map +1 -0
  227. package/dist/navigator/index.js +435 -0
  228. package/dist/navigator/index.js.map +1 -0
  229. package/dist/no_collab_save/index.js +169 -0
  230. package/dist/no_collab_save/index.js.map +1 -0
  231. package/dist/plugins/citation_dialog/index.js +2 -0
  232. package/dist/plugins/citation_dialog/index.js.map +1 -0
  233. package/dist/plugins/editor/index.js +2 -0
  234. package/dist/plugins/editor/index.js.map +1 -0
  235. package/dist/state_plugins/access_rights.js +30 -0
  236. package/dist/state_plugins/access_rights.js.map +1 -0
  237. package/dist/state_plugins/citation_render.js +78 -0
  238. package/dist/state_plugins/citation_render.js.map +1 -0
  239. package/dist/state_plugins/clipboard.js +217 -0
  240. package/dist/state_plugins/clipboard.js.map +1 -0
  241. package/dist/state_plugins/code_block.js +165 -0
  242. package/dist/state_plugins/code_block.js.map +1 -0
  243. package/dist/state_plugins/collab_carets.js +115 -0
  244. package/dist/state_plugins/collab_carets.js.map +1 -0
  245. package/dist/state_plugins/comments.js +197 -0
  246. package/dist/state_plugins/comments.js.map +1 -0
  247. package/dist/state_plugins/contributor_input/add_button.js +101 -0
  248. package/dist/state_plugins/contributor_input/add_button.js.map +1 -0
  249. package/dist/state_plugins/contributor_input/dropup.js +273 -0
  250. package/dist/state_plugins/contributor_input/dropup.js.map +1 -0
  251. package/dist/state_plugins/contributor_input/helpers.js +23 -0
  252. package/dist/state_plugins/contributor_input/helpers.js.map +1 -0
  253. package/dist/state_plugins/contributor_input/index.js +3 -0
  254. package/dist/state_plugins/contributor_input/index.js.map +1 -0
  255. package/dist/state_plugins/contributor_input/node_view.js +76 -0
  256. package/dist/state_plugins/contributor_input/node_view.js.map +1 -0
  257. package/dist/state_plugins/contributor_input/plugin.js +236 -0
  258. package/dist/state_plugins/contributor_input/plugin.js.map +1 -0
  259. package/dist/state_plugins/document_template.js +213 -0
  260. package/dist/state_plugins/document_template.js.map +1 -0
  261. package/dist/state_plugins/figure.js +90 -0
  262. package/dist/state_plugins/figure.js.map +1 -0
  263. package/dist/state_plugins/footnote_markers.js +187 -0
  264. package/dist/state_plugins/footnote_markers.js.map +1 -0
  265. package/dist/state_plugins/headerbar.js +10 -0
  266. package/dist/state_plugins/headerbar.js.map +1 -0
  267. package/dist/state_plugins/index.js +27 -0
  268. package/dist/state_plugins/index.js.map +1 -0
  269. package/dist/state_plugins/inline_math.js +719 -0
  270. package/dist/state_plugins/inline_math.js.map +1 -0
  271. package/dist/state_plugins/inline_reference/dropup.js +182 -0
  272. package/dist/state_plugins/inline_reference/dropup.js.map +1 -0
  273. package/dist/state_plugins/inline_reference/index.js +2 -0
  274. package/dist/state_plugins/inline_reference/index.js.map +1 -0
  275. package/dist/state_plugins/inline_reference/plugin.js +1134 -0
  276. package/dist/state_plugins/inline_reference/plugin.js.map +1 -0
  277. package/dist/state_plugins/jump_hidden_nodes.js +47 -0
  278. package/dist/state_plugins/jump_hidden_nodes.js.map +1 -0
  279. package/dist/state_plugins/links.js +622 -0
  280. package/dist/state_plugins/links.js.map +1 -0
  281. package/dist/state_plugins/marginboxes.js +13 -0
  282. package/dist/state_plugins/marginboxes.js.map +1 -0
  283. package/dist/state_plugins/ordered_list_menu.js +72 -0
  284. package/dist/state_plugins/ordered_list_menu.js.map +1 -0
  285. package/dist/state_plugins/placeholders.js +98 -0
  286. package/dist/state_plugins/placeholders.js.map +1 -0
  287. package/dist/state_plugins/search.js +212 -0
  288. package/dist/state_plugins/search.js.map +1 -0
  289. package/dist/state_plugins/selection_menu.js +10 -0
  290. package/dist/state_plugins/selection_menu.js.map +1 -0
  291. package/dist/state_plugins/settings.js +191 -0
  292. package/dist/state_plugins/settings.js.map +1 -0
  293. package/dist/state_plugins/table.js +104 -0
  294. package/dist/state_plugins/table.js.map +1 -0
  295. package/dist/state_plugins/tag_input/helpers.js +55 -0
  296. package/dist/state_plugins/tag_input/helpers.js.map +1 -0
  297. package/dist/state_plugins/tag_input/index.js +3 -0
  298. package/dist/state_plugins/tag_input/index.js.map +1 -0
  299. package/dist/state_plugins/tag_input/node_view.js +77 -0
  300. package/dist/state_plugins/tag_input/node_view.js.map +1 -0
  301. package/dist/state_plugins/tag_input/plugin.js +199 -0
  302. package/dist/state_plugins/tag_input/plugin.js.map +1 -0
  303. package/dist/state_plugins/tag_input/tag_editor.js +159 -0
  304. package/dist/state_plugins/tag_input/tag_editor.js.map +1 -0
  305. package/dist/state_plugins/tag_input/tag_editor_plugins.js +61 -0
  306. package/dist/state_plugins/tag_input/tag_editor_plugins.js.map +1 -0
  307. package/dist/state_plugins/toc_render.js +142 -0
  308. package/dist/state_plugins/toc_render.js.map +1 -0
  309. package/dist/state_plugins/toolbar.js +10 -0
  310. package/dist/state_plugins/toolbar.js.map +1 -0
  311. package/dist/state_plugins/track/find_selected_changes.js +82 -0
  312. package/dist/state_plugins/track/find_selected_changes.js.map +1 -0
  313. package/dist/state_plugins/track/helpers.js +67 -0
  314. package/dist/state_plugins/track/helpers.js.map +1 -0
  315. package/dist/state_plugins/track/index.js +3 -0
  316. package/dist/state_plugins/track/index.js.map +1 -0
  317. package/dist/state_plugins/track/plugin.js +114 -0
  318. package/dist/state_plugins/track/plugin.js.map +1 -0
  319. package/dist/tools/index.js +4 -0
  320. package/dist/tools/index.js.map +1 -0
  321. package/dist/tools/key_bindings/index.js +20 -0
  322. package/dist/tools/key_bindings/index.js.map +1 -0
  323. package/dist/tools/key_bindings/templates.js +68 -0
  324. package/dist/tools/key_bindings/templates.js.map +1 -0
  325. package/dist/tools/search_replace/index.js +275 -0
  326. package/dist/tools/search_replace/index.js.map +1 -0
  327. package/dist/tools/search_replace/templates.js +18 -0
  328. package/dist/tools/search_replace/templates.js.map +1 -0
  329. package/dist/tools/word_count/index.js +40 -0
  330. package/dist/tools/word_count/index.js.map +1 -0
  331. package/dist/tools/word_count/templates.js +27 -0
  332. package/dist/tools/word_count/templates.js.map +1 -0
  333. package/dist/track/accept.js +66 -0
  334. package/dist/track/accept.js.map +1 -0
  335. package/dist/track/accept_all.js +51 -0
  336. package/dist/track/accept_all.js.map +1 -0
  337. package/dist/track/accept_all_no_insertions.js +63 -0
  338. package/dist/track/accept_all_no_insertions.js.map +1 -0
  339. package/dist/track/amend_transaction.js +390 -0
  340. package/dist/track/amend_transaction.js.map +1 -0
  341. package/dist/track/delete.js +62 -0
  342. package/dist/track/delete.js.map +1 -0
  343. package/dist/track/index.js +6 -0
  344. package/dist/track/index.js.map +1 -0
  345. package/dist/track/mod.js +86 -0
  346. package/dist/track/mod.js.map +1 -0
  347. package/dist/track/reject.js +67 -0
  348. package/dist/track/reject.js.map +1 -0
  349. package/dist/track/reject_all.js +56 -0
  350. package/dist/track/reject_all.js.map +1 -0
  351. package/package.json +80 -0
  352. package/src/bibliography/tools.js +58 -0
  353. package/src/citations/index.js +443 -0
  354. package/src/citations/render.js +68 -0
  355. package/src/clipboard/copy/index.js +1 -0
  356. package/src/clipboard/copy/schema.js +62 -0
  357. package/src/clipboard/copy/serializers.js +179 -0
  358. package/src/clipboard/paste/fidus_writer.js +55 -0
  359. package/src/clipboard/paste/general.js +80 -0
  360. package/src/clipboard/paste/google_docs.js +26 -0
  361. package/src/clipboard/paste/html.js +76 -0
  362. package/src/clipboard/paste/index.js +2 -0
  363. package/src/clipboard/paste/libreoffice_writer.js +32 -0
  364. package/src/clipboard/paste/microsoft_word.js +87 -0
  365. package/src/clipboard/paste/text.js +58 -0
  366. package/src/collab/chat.js +195 -0
  367. package/src/collab/colors.js +67 -0
  368. package/src/collab/doc.js +1436 -0
  369. package/src/collab/index.js +81 -0
  370. package/src/collab/merge/changeset.js +103 -0
  371. package/src/collab/merge/editor.js +1065 -0
  372. package/src/collab/merge/footnotes.js +175 -0
  373. package/src/collab/merge/index.js +401 -0
  374. package/src/collab/merge/recreate_transform.js +326 -0
  375. package/src/collab/merge/schema.js +135 -0
  376. package/src/collab/merge/state_plugins/action.js +171 -0
  377. package/src/collab/merge/state_plugins/clipboard.js +32 -0
  378. package/src/collab/merge/state_plugins/diff.js +780 -0
  379. package/src/collab/merge/state_plugins/index.js +2 -0
  380. package/src/collab/merge/tools.js +217 -0
  381. package/src/collab/templates.js +10 -0
  382. package/src/comments/comment.js +29 -0
  383. package/src/comments/editors/answer.js +55 -0
  384. package/src/comments/editors/comment.js +318 -0
  385. package/src/comments/editors/index.js +4 -0
  386. package/src/comments/editors/notify.js +13 -0
  387. package/src/comments/editors/schema.js +65 -0
  388. package/src/comments/index.js +11 -0
  389. package/src/comments/interactions.js +465 -0
  390. package/src/comments/store.js +513 -0
  391. package/src/contacts/add_dialog.js +110 -0
  392. package/src/contacts/templates.js +28 -0
  393. package/src/copyright_dialog/index.js +179 -0
  394. package/src/copyright_dialog/templates.js +56 -0
  395. package/src/databases/bibliography.js +179 -0
  396. package/src/databases/images.js +320 -0
  397. package/src/databases/index.js +69 -0
  398. package/src/dialogs/citation.js +428 -0
  399. package/src/dialogs/code_block.js +242 -0
  400. package/src/dialogs/contributor.js +173 -0
  401. package/src/dialogs/figure.js +479 -0
  402. package/src/dialogs/index.js +10 -0
  403. package/src/dialogs/language.js +58 -0
  404. package/src/dialogs/link.js +358 -0
  405. package/src/dialogs/math.js +148 -0
  406. package/src/dialogs/ordered_list.js +84 -0
  407. package/src/dialogs/revision.js +43 -0
  408. package/src/dialogs/subsup.js +116 -0
  409. package/src/dialogs/table.js +258 -0
  410. package/src/dialogs/templates.js +426 -0
  411. package/src/document_template/exporter.js +97 -0
  412. package/src/document_template/index.js +483 -0
  413. package/src/documents/access_rights/index.js +693 -0
  414. package/src/documents/access_rights/templates.js +178 -0
  415. package/src/e2ee/encryptor.js +228 -0
  416. package/src/e2ee/key-manager.js +202 -0
  417. package/src/e2ee/passphrase-crypto.js +606 -0
  418. package/src/e2ee/passphrase-dialog.js +655 -0
  419. package/src/e2ee/passphrase-manager.js +502 -0
  420. package/src/e2ee/password-dialog.js +652 -0
  421. package/src/e2ee/snapshot-manager.js +305 -0
  422. package/src/exporter/native/copy.js +54 -0
  423. package/src/exporter/native/file.js +37 -0
  424. package/src/exporter/native/index.js +3 -0
  425. package/src/exporter/native/revision.js +86 -0
  426. package/src/exporter/native/zip.js +1 -0
  427. package/src/footnotes/editor.js +227 -0
  428. package/src/footnotes/index.js +16 -0
  429. package/src/footnotes/layout.js +190 -0
  430. package/src/footnotes/state_plugins/access_rights.js +39 -0
  431. package/src/footnotes/state_plugins/index.js +1 -0
  432. package/src/images/edit_dialog/edit_dialog/index.js +222 -0
  433. package/src/images/edit_dialog/edit_dialog/model.js +183 -0
  434. package/src/images/edit_dialog/edit_dialog/templates.js +41 -0
  435. package/src/images/edit_dialog/model.js +5 -0
  436. package/src/images/selection_dialog/index.js +176 -0
  437. package/src/index.js +1252 -0
  438. package/src/keymap.js +102 -0
  439. package/src/marginboxes/index.js +946 -0
  440. package/src/marginboxes/templates.js +573 -0
  441. package/src/menus/code_block/index.js +1 -0
  442. package/src/menus/code_block/model.js +52 -0
  443. package/src/menus/figure/index.js +2 -0
  444. package/src/menus/figure/model.js +91 -0
  445. package/src/menus/figure/utils.js +5 -0
  446. package/src/menus/headerbar/index.js +2 -0
  447. package/src/menus/headerbar/model.js +1077 -0
  448. package/src/menus/headerbar/view.js +675 -0
  449. package/src/menus/image/index.js +1 -0
  450. package/src/menus/image/model.js +32 -0
  451. package/src/menus/index.js +9 -0
  452. package/src/menus/navigator/index.js +1 -0
  453. package/src/menus/navigator/model.js +28 -0
  454. package/src/menus/ordered_list/index.js +1 -0
  455. package/src/menus/ordered_list/model.js +19 -0
  456. package/src/menus/selection/index.js +2 -0
  457. package/src/menus/selection/model.js +134 -0
  458. package/src/menus/selection/view.js +154 -0
  459. package/src/menus/table/index.js +1 -0
  460. package/src/menus/table/model.js +414 -0
  461. package/src/menus/toolbar/index.js +2 -0
  462. package/src/menus/toolbar/model.js +920 -0
  463. package/src/menus/toolbar/view.js +354 -0
  464. package/src/navigator/index.js +515 -0
  465. package/src/no_collab_save/index.js +209 -0
  466. package/src/plugins/citation_dialog/index.js +1 -0
  467. package/src/plugins/editor/index.js +1 -0
  468. package/src/state_plugins/access_rights.js +47 -0
  469. package/src/state_plugins/citation_render.js +88 -0
  470. package/src/state_plugins/clipboard.js +289 -0
  471. package/src/state_plugins/code_block.js +201 -0
  472. package/src/state_plugins/collab_carets.js +152 -0
  473. package/src/state_plugins/comments.js +259 -0
  474. package/src/state_plugins/contributor_input/add_button.js +134 -0
  475. package/src/state_plugins/contributor_input/dropup.js +318 -0
  476. package/src/state_plugins/contributor_input/helpers.js +24 -0
  477. package/src/state_plugins/contributor_input/index.js +2 -0
  478. package/src/state_plugins/contributor_input/node_view.js +86 -0
  479. package/src/state_plugins/contributor_input/plugin.js +318 -0
  480. package/src/state_plugins/document_template.js +315 -0
  481. package/src/state_plugins/figure.js +108 -0
  482. package/src/state_plugins/footnote_markers.js +238 -0
  483. package/src/state_plugins/headerbar.js +12 -0
  484. package/src/state_plugins/index.js +73 -0
  485. package/src/state_plugins/inline_math.js +918 -0
  486. package/src/state_plugins/inline_reference/dropup.js +208 -0
  487. package/src/state_plugins/inline_reference/index.js +5 -0
  488. package/src/state_plugins/inline_reference/plugin.js +1429 -0
  489. package/src/state_plugins/jump_hidden_nodes.js +58 -0
  490. package/src/state_plugins/links.js +830 -0
  491. package/src/state_plugins/marginboxes.js +14 -0
  492. package/src/state_plugins/ordered_list_menu.js +84 -0
  493. package/src/state_plugins/placeholders.js +123 -0
  494. package/src/state_plugins/search.js +251 -0
  495. package/src/state_plugins/selection_menu.js +12 -0
  496. package/src/state_plugins/settings.js +223 -0
  497. package/src/state_plugins/table.js +127 -0
  498. package/src/state_plugins/tag_input/helpers.js +69 -0
  499. package/src/state_plugins/tag_input/index.js +2 -0
  500. package/src/state_plugins/tag_input/node_view.js +85 -0
  501. package/src/state_plugins/tag_input/plugin.js +278 -0
  502. package/src/state_plugins/tag_input/tag_editor.js +202 -0
  503. package/src/state_plugins/tag_input/tag_editor_plugins.js +74 -0
  504. package/src/state_plugins/toc_render.js +160 -0
  505. package/src/state_plugins/toolbar.js +12 -0
  506. package/src/state_plugins/track/find_selected_changes.js +107 -0
  507. package/src/state_plugins/track/helpers.js +108 -0
  508. package/src/state_plugins/track/index.js +6 -0
  509. package/src/state_plugins/track/plugin.js +140 -0
  510. package/src/tools/index.js +3 -0
  511. package/src/tools/key_bindings/index.js +21 -0
  512. package/src/tools/key_bindings/templates.js +68 -0
  513. package/src/tools/search_replace/index.js +421 -0
  514. package/src/tools/search_replace/templates.js +19 -0
  515. package/src/tools/word_count/index.js +59 -0
  516. package/src/tools/word_count/templates.js +34 -0
  517. package/src/track/accept.js +102 -0
  518. package/src/track/accept_all.js +103 -0
  519. package/src/track/accept_all_no_insertions.js +102 -0
  520. package/src/track/amend_transaction.js +562 -0
  521. package/src/track/delete.js +92 -0
  522. package/src/track/index.js +8 -0
  523. package/src/track/mod.js +116 -0
  524. package/src/track/reject.js +116 -0
  525. package/src/track/reject_all.js +121 -0
package/dist/index.js ADDED
@@ -0,0 +1,1002 @@
1
+ import { Dialog, WebSocketConnector, activateWait, addAlert, deactivateWait, ensureCSS, postJson, showSystemMessage, whenReady } from "fwtoolkit";
2
+ import { collab, sendableSteps } from "prosemirror-collab";
3
+ import { baseKeymap } from "prosemirror-commands";
4
+ import { dropCursor } from "prosemirror-dropcursor";
5
+ import { buildKeymap } from "prosemirror-example-setup";
6
+ import { gapCursor } from "prosemirror-gapcursor";
7
+ import { history } from "prosemirror-history";
8
+ import { keymap } from "prosemirror-keymap";
9
+ import { EditorState, TextSelection } from "prosemirror-state";
10
+ import { tableEditing } from "prosemirror-tables";
11
+ import { EditorView } from "prosemirror-view";
12
+ import { FeedbackTab } from "@fiduswriter/common";
13
+ import { E2EEEncryptor } from "./e2ee/encryptor";
14
+ import { E2EEKeyManager } from "./e2ee/key-manager";
15
+ import { enterPassphraseDialog, setupPassphraseDialog, showRecoveryKeyDialog } from "./e2ee/passphrase-dialog";
16
+ import { PassphraseManager } from "./e2ee/passphrase-manager";
17
+ import { changePasswordDialog, createPasswordDialog } from "./e2ee/password-dialog";
18
+ import { E2EESnapshotManager } from "./e2ee/snapshot-manager";
19
+ import { getSettings } from "@fiduswriter/document/schema/convert";
20
+ import { docSchema } from "@fiduswriter/document/schema/document/index";
21
+ import { plugins } from "../../plugins/editor";
22
+ import { ModCitations } from "./citations";
23
+ import { ModCollab } from "./collab";
24
+ import { ModComments } from "./comments";
25
+ import { ModDB } from "./databases";
26
+ import { ModDocumentTemplate } from "./document_template";
27
+ import { ModFootnotes } from "./footnotes";
28
+ import { ModMarginboxes } from "./marginboxes";
29
+ import { codeBlockMenuModel, figureMenuModel, figureWidthMenuModel, headerbarModel, imageMenuModel, navigatorFilterModel, orderedListMenuModel, selectionMenuModel, tableMenuModel, toolbarModel } from "./menus";
30
+ import { ModNavigator } from "./navigator";
31
+ import { NoCollabSave } from "./no_collab_save";
32
+ import { ModTrack, acceptAllNoInsertions, amendTransaction } from "./track";
33
+ import { ExportFidusFile } from "../exporter/native/file";
34
+ import { imageEditModel } from "../images/edit_dialog/model";
35
+ import { buildEditorKeymap } from "./keymap";
36
+ import { accessRightsPlugin, citationRenderPlugin, clipboardPlugin, codeBlockPlugin, collabCaretsPlugin, commentsPlugin, contributorInputPlugin, documentTemplatePlugin, figurePlugin, footnoteMarkersPlugin, headerbarPlugin, inlineMathPlugin, inlineReferencePlugin, jumpHiddenNodesPlugin, linksPlugin, marginboxesPlugin, orderedListMenuPlugin, placeholdersPlugin, searchPlugin, selectionMenuPlugin, settingsPlugin, tablePlugin, tagInputPlugin, tocRenderPlugin, toolbarPlugin, trackPlugin } from "./state_plugins";
37
+ // UUID v4 pattern for share tokens
38
+ const uuid4Pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
39
+ export const COMMENT_ONLY_ROLES = ["review", "comment"];
40
+ export const READ_ONLY_ROLES = ["read", "read-without-comments"];
41
+ export const REVIEW_ROLES = ["review", "review-tracked"];
42
+ export const WRITE_ROLES = ["write", "write-tracked", "review-tracked"];
43
+ export class Editor {
44
+ // A class that contains everything that happens on the editor page.
45
+ // It is currently not possible to initialize more than one editor class, as it
46
+ // contains bindings to menu items, etc. that are uniquely defined.
47
+ constructor({ app, user }, path, idString) {
48
+ this.app = app;
49
+ // For unauthenticated guests, replace the bare config object with a
50
+ // proper placeholder that has all the fields templates expect.
51
+ this.user = user.is_authenticated
52
+ ? user
53
+ : { id: undefined, username: "", name: "", is_authenticated: false };
54
+ this.mod = {};
55
+ // Whether the editor is currently waiting for a document update. Set to true
56
+ // initially so that diffs that arrive before document has been loaded are not
57
+ // dealt with.
58
+ this.waitingForDocument = true;
59
+ this.docInfo = {
60
+ rights: "",
61
+ owner: undefined,
62
+ is_owner: false,
63
+ confirmedDoc: false, // The latest doc as confirmed by the server.
64
+ updated: false, // Latest update time stamp
65
+ dir: "ltr", // standard direction, used in input fields, etc.
66
+ path // Default doc path.
67
+ };
68
+ if (uuid4Pattern.test(idString)) {
69
+ // Share token — store the raw UUID; init() will resolve it to a real doc id
70
+ this.docInfo.id = idString;
71
+ this.docInfo.token = idString;
72
+ }
73
+ else {
74
+ let id = Number.parseInt(idString);
75
+ if (isNaN(id)) {
76
+ id = 0;
77
+ let templateId = Number.parseInt(idString.slice(1));
78
+ if (isNaN(templateId)) {
79
+ templateId = 1;
80
+ }
81
+ this.docInfo.templateId = templateId;
82
+ }
83
+ this.docInfo.id = id;
84
+ }
85
+ this.schema = docSchema;
86
+ // Provide a handler for decrypting end-to-end encrypted images. The
87
+ // reusable document schema no longer includes E2EE code, so the main
88
+ // app injects the decryption logic here after loading the schema.
89
+ this.schema.cached.decryptImage = (imageEntry, _dom) => {
90
+ const key = this.e2ee?.key;
91
+ if (!key) {
92
+ return Promise.reject(new Error("No E2EE key available"));
93
+ }
94
+ return E2EEEncryptor.decryptImageToUrl(imageEntry.image, key, imageEntry.original_file_type || "image/png");
95
+ };
96
+ this.menu = {
97
+ headerbarModel: headerbarModel(),
98
+ imageMenuModel: imageMenuModel(),
99
+ navigatorFilterModel: navigatorFilterModel(),
100
+ orderedListMenuModel: orderedListMenuModel(),
101
+ selectionMenuModel: selectionMenuModel(),
102
+ imageEditModel: imageEditModel(),
103
+ tableMenuModel: tableMenuModel(),
104
+ figureMenuModel: figureMenuModel(),
105
+ toolbarModel: toolbarModel(),
106
+ figureWidthMenuModel: figureWidthMenuModel(),
107
+ codeBlockMenuModel: codeBlockMenuModel()
108
+ };
109
+ this.client_id = Math.floor(Math.random() * 0xffffffff);
110
+ this.clientTimeAdjustment = 0;
111
+ // E2EE state: holds the encryption key and snapshot manager
112
+ // for end-to-end encrypted documents. Set to an object with
113
+ // { key, snapshotManager, encrypted, encryptionSalt, encryptionIterations }
114
+ // when the document is encrypted and the user has entered the password.
115
+ this.e2ee = null;
116
+ this.pathEditable = true; // Set to false through plugin to disable path editing.
117
+ this.statePlugins = [
118
+ [inlineMathPlugin, () => ({ editor: this })],
119
+ [inlineReferencePlugin, () => ({ editor: this })],
120
+ [linksPlugin, () => ({ editor: this })],
121
+ [keymap, () => buildEditorKeymap(this.schema)],
122
+ [keymap, () => buildKeymap(this.schema)],
123
+ [keymap, () => baseKeymap],
124
+ [collab, () => ({ clientID: this.client_id })],
125
+ [history],
126
+ [dropCursor],
127
+ [gapCursor],
128
+ [tableEditing],
129
+ [jumpHiddenNodesPlugin],
130
+ [placeholdersPlugin, () => ({ editor: this })],
131
+ [citationRenderPlugin, () => ({ editor: this })],
132
+ [headerbarPlugin, () => ({ editor: this })],
133
+ [toolbarPlugin, () => ({ editor: this })],
134
+ [selectionMenuPlugin, () => ({ editor: this })],
135
+ [collabCaretsPlugin, () => ({ editor: this })],
136
+ [footnoteMarkersPlugin, () => ({ editor: this })],
137
+ [commentsPlugin, () => ({ editor: this })],
138
+ [marginboxesPlugin, () => ({ editor: this })],
139
+ [tagInputPlugin, () => ({ editor: this })],
140
+ [contributorInputPlugin, () => ({ editor: this })],
141
+ [clipboardPlugin, () => ({ editor: this, viewType: "main" })],
142
+ [accessRightsPlugin, () => ({ editor: this })],
143
+ [settingsPlugin, () => ({ editor: this })],
144
+ [documentTemplatePlugin, () => ({ editor: this })],
145
+ [trackPlugin, () => ({ editor: this })],
146
+ [tablePlugin, () => ({ editor: this })],
147
+ [orderedListMenuPlugin, () => ({ editor: this })],
148
+ [figurePlugin, () => ({ editor: this })],
149
+ [codeBlockPlugin, () => ({ editor: this })],
150
+ [tocRenderPlugin, () => ({ editor: this })],
151
+ [searchPlugin]
152
+ ];
153
+ }
154
+ init() {
155
+ ensureCSS([
156
+ staticUrl("css/libs/mathlive/mathlive.css"),
157
+ staticUrl("css/editor.css"),
158
+ staticUrl("css/tags.css"),
159
+ staticUrl("css/contributors.css"),
160
+ staticUrl("css/document.css"),
161
+ staticUrl("css/carets.css"),
162
+ staticUrl("css/tracking.css"),
163
+ staticUrl("css/margin_boxes.css"),
164
+ staticUrl("css/prosemirror.css"),
165
+ staticUrl("css/footnotes.css"),
166
+ staticUrl("css/chat.css"),
167
+ staticUrl("css/access_rights_dialog.css"),
168
+ staticUrl("css/citation_dialog.css"),
169
+ staticUrl("css/review.css"),
170
+ staticUrl("css/add_remove_dialog.css"),
171
+ staticUrl("css/bibliography.css"),
172
+ staticUrl("css/dot_menu.css"),
173
+ staticUrl("css/cropper.min.css"),
174
+ staticUrl("css/inline_tools.css"),
175
+ staticUrl("css/e2ee.css")
176
+ ]);
177
+ new ModDocumentTemplate(this);
178
+ const initPromises = [
179
+ whenReady(),
180
+ this.mod.documentTemplate.getCitationStyles()
181
+ ];
182
+ if (this.docInfo.hasOwnProperty("templateId")) {
183
+ // Check if this should be an E2EE document.
184
+ // The E2EE mode can be indicated via:
185
+ // 1. URL query parameter ?e2ee=true (from overview page)
186
+ // 2. Global E2EE_MODE variable set by the settings
187
+ const e2eeParam = new URLSearchParams(window.location.search).get("e2ee");
188
+ const isE2EERequired = this.app.settings.E2EE_MODE === "required";
189
+ const isE2EERequested = e2eeParam === "true" || e2eeParam === "1";
190
+ const shouldEncrypt = isE2EERequired || isE2EERequested;
191
+ if (shouldEncrypt) {
192
+ initPromises.push(this._createE2EEDocument());
193
+ }
194
+ else {
195
+ initPromises.push(postJson("/api/document/create_doc/", {
196
+ template_id: this.docInfo.templateId,
197
+ path: this.docInfo.path
198
+ }).then(({ json }) => {
199
+ this.docInfo.id = json.id;
200
+ window.history.replaceState("", "", `/document/${this.docInfo.id}/`);
201
+ delete this.docInfo.templateId;
202
+ return Promise.resolve();
203
+ }));
204
+ }
205
+ }
206
+ // Handle token-based access (share links)
207
+ if (uuid4Pattern.test(this.docInfo.id)) {
208
+ this.docInfo.token = this.docInfo.id;
209
+ initPromises.push(postJson(`/api/document/share_token/validate/${this.docInfo.token}/`).then(({ json, status }) => {
210
+ if (status === 200 && json.document_id) {
211
+ this.docInfo.id = json.document_id;
212
+ this.docInfo.access_rights = json.rights;
213
+ this.docInfo.wsBase = json.ws_base;
214
+ return Promise.resolve();
215
+ }
216
+ else {
217
+ // Token is invalid or expired
218
+ return Promise.reject(new Error("Invalid or expired share link"));
219
+ }
220
+ }));
221
+ }
222
+ return Promise.all(initPromises)
223
+ .then(() => {
224
+ new ModCitations(this);
225
+ new ModFootnotes(this);
226
+ return this.activateFidusPlugins();
227
+ })
228
+ .then(() => {
229
+ activateWait(true);
230
+ const stylesPayload = { id: this.docInfo.id };
231
+ if (this.docInfo.token) {
232
+ stylesPayload.token = this.docInfo.token;
233
+ }
234
+ const editorSaveMode = this.app.settings.EDITOR_SAVE_MODE;
235
+ const collaborativeEditing = editorSaveMode === "collaborative";
236
+ const wsBasePromise = collaborativeEditing
237
+ ? this.docInfo.wsBase
238
+ ? Promise.resolve({
239
+ json: { ws_base: this.docInfo.wsBase }
240
+ })
241
+ : postJson("/api/document/get_ws_base/", {
242
+ id: this.docInfo.id
243
+ })
244
+ : Promise.resolve({ json: { ws_base: "" } });
245
+ const stylesPromise = postJson("/api/document/get_doc_styles/", stylesPayload);
246
+ const docDataPromise = postJson("/api/document/get_doc_data/", stylesPayload);
247
+ return Promise.all([
248
+ wsBasePromise,
249
+ stylesPromise,
250
+ docDataPromise
251
+ ]);
252
+ })
253
+ .catch(error => {
254
+ // Only show "Invalid Share Link" for token validation errors.
255
+ // Other errors (REST failures, etc.) should not show this dialog.
256
+ if (error.message === "Invalid or expired share link") {
257
+ deactivateWait();
258
+ const errorDialog = new Dialog({
259
+ title: gettext("Invalid Share Link"),
260
+ id: "invalid_share_link_dialog",
261
+ body: gettext("This share link has expired or is invalid. Please ask the document owner for a new link."),
262
+ buttons: [
263
+ {
264
+ text: gettext("OK"),
265
+ classes: "fw-dark",
266
+ click: () => {
267
+ window.location.href = "/";
268
+ }
269
+ }
270
+ ],
271
+ canClose: false
272
+ });
273
+ errorDialog.open();
274
+ }
275
+ else {
276
+ deactivateWait();
277
+ console.error("Editor initialization failed:", error);
278
+ }
279
+ return Promise.reject(error);
280
+ })
281
+ .then(([wsResult, stylesResult, docResult]) => {
282
+ let resubScribed = false;
283
+ this.render();
284
+ this.initEditor();
285
+ const editorSaveMode = this.app.settings.EDITOR_SAVE_MODE;
286
+ const collaborativeEditing = editorSaveMode === "collaborative";
287
+ if (collaborativeEditing) {
288
+ // Include token in WebSocket path if present
289
+ let wsPath = `/document/${this.docInfo.id}/`;
290
+ if (this.docInfo.token) {
291
+ wsPath += `?token=${this.docInfo.token}`;
292
+ }
293
+ this.ws = new WebSocketConnector({
294
+ base: wsResult.json.ws_base,
295
+ path: wsPath,
296
+ appLoaded: () => this.view.state.plugins.length,
297
+ anythingToSend: () => sendableSteps(this.view.state),
298
+ initialMessage: () => {
299
+ const message = {
300
+ type: "subscribe",
301
+ v: this.docInfo.version
302
+ };
303
+ if (this.ws.connectionCount) {
304
+ message.connection = this.ws.connectionCount;
305
+ }
306
+ return message;
307
+ },
308
+ resubScribed: () => {
309
+ if (sendableSteps(this.mod.footnotes.fnEditor.view.state)) {
310
+ this.mod.collab.doc.footnoteRender = true;
311
+ }
312
+ resubScribed = true;
313
+ this.mod.footnotes.fnEditor.renderAllFootnotes();
314
+ this.mod.collab.doc.awaitingDiffResponse = true; // wait sending diffs till the version is confirmed
315
+ },
316
+ restartMessage: () => ({
317
+ type: "check_version",
318
+ v: this.docInfo.version
319
+ }),
320
+ messagesElement: () => this.dom.querySelector("#unobtrusive-messages"),
321
+ warningNotAllSent: gettext("Warning! Not all your changes have been saved! You could suffer data loss. Attempting to reconnect..."),
322
+ infoDisconnected: gettext("Disconnected. Attempting to reconnect..."),
323
+ receiveData: data => {
324
+ if (document.body !== this.dom) {
325
+ return; // user navigated away.
326
+ }
327
+ switch (data.type) {
328
+ case "chat":
329
+ this.mod.collab.chat.newMessage(data);
330
+ break;
331
+ case "connections":
332
+ this.mod.collab.updateParticipantList(data.participant_list);
333
+ if (resubScribed) {
334
+ // Reconnecting after offline with
335
+ // local edits: ask the server to
336
+ // save first so that the REST
337
+ // refetch reflects the definitive
338
+ // server state and adjustDocument
339
+ // can run the tracked-changes merge.
340
+ const missingSteps = sendableSteps(this.view.state);
341
+ this.mod.collab.doc.checkVersion(missingSteps);
342
+ resubScribed = false;
343
+ }
344
+ break;
345
+ case "session_info":
346
+ this.docInfo.session_id = data.session_id;
347
+ // Update access rights from server
348
+ if (data.access_right) {
349
+ this.docInfo.access_rights =
350
+ data.access_right;
351
+ }
352
+ // Update guest user identity with session_id
353
+ if (!this.user.is_authenticated) {
354
+ this.user = {
355
+ id: this.docInfo.token,
356
+ username: `guest${data.session_id}`,
357
+ name: `Guest ${data.session_id}`,
358
+ is_authenticated: false
359
+ };
360
+ }
361
+ // Handle E2EE encryption parameters from server
362
+ if (data.e2ee) {
363
+ if (!this.e2ee) {
364
+ this.e2ee = {
365
+ encrypted: true,
366
+ encryptionSalt: data.e2ee_salt,
367
+ encryptionIterations: data.e2ee_iterations,
368
+ key: null,
369
+ snapshotManager: new E2EESnapshotManager(this)
370
+ };
371
+ }
372
+ else {
373
+ this.e2ee.encrypted = true;
374
+ this.e2ee.encryptionSalt =
375
+ data.e2ee_salt;
376
+ this.e2ee.encryptionIterations =
377
+ data.e2ee_iterations;
378
+ }
379
+ }
380
+ break;
381
+ case "refetch_doc":
382
+ // The server has forced a DB save before
383
+ // sending this message, so the REST response
384
+ // will be at the current server version. Passing
385
+ // our version (v) lets the endpoint include the
386
+ // covering diffs as `m` so adjustDocument can
387
+ // do a precise merge.
388
+ postJson("/api/document/get_doc_data/", {
389
+ id: this.docInfo.id,
390
+ token: this.docInfo.token,
391
+ v: this.docInfo.version
392
+ }).then(({ json }) => {
393
+ this.mod.collab.doc.receiveDocument(json);
394
+ });
395
+ break;
396
+ case "confirm_version":
397
+ this.mod.collab.doc.cancelCurrentlyCheckingVersion();
398
+ if (data["v"] !== this.docInfo.version) {
399
+ this.mod.collab.doc.checkVersion();
400
+ return;
401
+ }
402
+ this.mod.collab.doc.confirmVersion(data["v"]);
403
+ this.mod.collab.doc.enableDiffSending();
404
+ break;
405
+ case "selection_change":
406
+ this.mod.collab.doc.cancelCurrentlyCheckingVersion();
407
+ if (data["v"] !== this.docInfo.version) {
408
+ this.mod.collab.doc.checkVersion();
409
+ return;
410
+ }
411
+ this.mod.collab.doc.receiveSelectionChange(data);
412
+ break;
413
+ case "path_change":
414
+ this.docInfo.path = data["path"];
415
+ this.menu.headerView.update();
416
+ break;
417
+ case "diff":
418
+ if (data["cid"] === this.client_id) {
419
+ // The diff origins from the local user.
420
+ this.mod.collab.doc.confirmDiff(data["rid"]);
421
+ return;
422
+ }
423
+ if (data["v"] !== this.docInfo.version) {
424
+ this.mod.collab.doc.checkVersion();
425
+ return;
426
+ }
427
+ this.mod.collab.doc.receiveDiff(data);
428
+ break;
429
+ case "confirm_diff":
430
+ this.mod.collab.doc.confirmDiff(data["rid"]);
431
+ break;
432
+ case "reject_diff":
433
+ this.mod.collab.doc.rejectDiff(data["rid"]);
434
+ break;
435
+ case "patch_error":
436
+ showSystemMessage(gettext("Your document was out of sync and has been reset."));
437
+ break;
438
+ case "access_right":
439
+ if (data.access_right !==
440
+ this.docInfo.access_rights) {
441
+ if (sendableSteps(this.view.state) &&
442
+ !WRITE_ROLES.includes(data.access_right)) {
443
+ // If the user's new rights does not allow him to update document , then download a copy of the
444
+ // same and ask him to re-open the document.
445
+ this.handleAccessRightModification();
446
+ }
447
+ else {
448
+ addAlert("info", interpolate(gettext("Your Access rights have been modified. You now have %(accessRight)s access to this document."), {
449
+ accessRight: data.access_right
450
+ }, true));
451
+ this.docInfo.access_rights =
452
+ data.access_right;
453
+ }
454
+ }
455
+ break;
456
+ case "request_snapshot":
457
+ // Server is asking a write-capable client to
458
+ // send an encrypted snapshot for persistence.
459
+ if (this.e2ee &&
460
+ this.e2ee.snapshotManager) {
461
+ this.e2ee.snapshotManager.handleRequestSnapshot(data);
462
+ }
463
+ break;
464
+ case "e2ee_snapshot_received":
465
+ // Server confirmed that an encrypted snapshot
466
+ // was saved. Informational — no action needed.
467
+ if (this.e2ee &&
468
+ this.e2ee.snapshotManager) {
469
+ this.e2ee.snapshotManager.handleSnapshotReceived(data);
470
+ }
471
+ break;
472
+ default:
473
+ break;
474
+ }
475
+ },
476
+ failedAuth: () => {
477
+ // Clear all E2EE keys on session expiration
478
+ E2EEKeyManager.clearAllKeysFromSession();
479
+ PassphraseManager.clearKeysFromSession();
480
+ if (this.docInfo.token) {
481
+ // Token-based access failed
482
+ const tokenDialog = new Dialog({
483
+ title: gettext("Invalid Share Link"),
484
+ id: "invalid_token_dialog",
485
+ body: gettext("This share link has expired or is invalid. Please ask the document owner for a new link."),
486
+ buttons: [
487
+ {
488
+ text: gettext("OK"),
489
+ classes: "fw-dark",
490
+ click: () => {
491
+ window.location.href = "/";
492
+ }
493
+ }
494
+ ],
495
+ canClose: false
496
+ });
497
+ tokenDialog.open();
498
+ return;
499
+ }
500
+ if (this.view.state.plugins.length &&
501
+ sendableSteps(this.view.state) &&
502
+ this.ws.connectionCount > 0) {
503
+ this.ws.online = false; // To avoid Websocket trying to reconnect.
504
+ const sessionDialog = new Dialog({
505
+ title: gettext("Session Expired"),
506
+ id: "session_expiration_dialog",
507
+ body: gettext("Your session expired while you were offline, so we cannot save your work to the server any longer, but you can download it to your computer instead. Please consider importing it as a new document after logging in."),
508
+ buttons: [
509
+ {
510
+ text: gettext("Download Document"),
511
+ click: () => {
512
+ const doc = this.getDoc({
513
+ use_current_view: true
514
+ });
515
+ new ExportFidusFile(doc, this.mod.db.bibDB, this.mod.db.imageDB, false);
516
+ }
517
+ },
518
+ {
519
+ text: gettext("Proceed to Login page"),
520
+ classes: "fw-dark",
521
+ click: () => {
522
+ window.location.href = "/";
523
+ }
524
+ }
525
+ ],
526
+ canClose: false
527
+ });
528
+ sessionDialog.open();
529
+ }
530
+ else {
531
+ window.location.href = "/";
532
+ }
533
+ }
534
+ });
535
+ }
536
+ this.mod.documentTemplate.setStyles(stylesResult.json);
537
+ const docIsE2EE = docResult.json.doc_info?.e2ee === true;
538
+ this.mod.collab.doc.receiveDocument(docResult.json);
539
+ // For E2EE documents, delay the WebSocket connection until
540
+ // after the password dialog and decryption are complete (see
541
+ // _decryptAndLoadDoc). This ensures the key is available when
542
+ // catch-up diffs arrive from the server.
543
+ if (collaborativeEditing && !docIsE2EE) {
544
+ this.startWebSocket();
545
+ }
546
+ else if (!docIsE2EE) {
547
+ // In non-collaborative modes there is no server
548
+ // confirm_version message, so mark it locally.
549
+ this.mod.collab.doc.confirmVersion(this.docInfo.version);
550
+ }
551
+ if (editorSaveMode === "direct") {
552
+ if (!this.noCollabSave) {
553
+ this.noCollabSave = new NoCollabSave(this);
554
+ }
555
+ this.noCollabSave.start();
556
+ }
557
+ });
558
+ }
559
+ /**
560
+ * Open the WebSocket connection and attach the close listener.
561
+ * Called immediately for non-E2EE documents and deferred until after
562
+ * password entry + decryption for E2EE documents.
563
+ */
564
+ startWebSocket() {
565
+ this.ws.init();
566
+ this.ws.ws.addEventListener("close", () => {
567
+ if (this.menu.toolbarViews) {
568
+ this.menu.toolbarViews.forEach(view => view.update());
569
+ }
570
+ if (this.menu.headerView) {
571
+ this.menu.headerView.update();
572
+ }
573
+ });
574
+ }
575
+ handleAccessRightModification() {
576
+ // This function when invoked creates a copy of document in FW format and closes editor operation.
577
+ new ExportFidusFile(this.getDoc({ use_current_view: true }), this.mod.db.bibDB, this.mod.db.imageDB);
578
+ const accessRightModifiedDialog = new Dialog({
579
+ title: gettext("Access rights modified"),
580
+ id: "access_rights_modified",
581
+ body: gettext("Your access rights were modified while you were offline, so we cannot save your work to the server any longer, and it is downloaded to your computer instead. Please consider importing it into a new document."),
582
+ buttons: [
583
+ {
584
+ text: gettext("Leave editor"),
585
+ classes: "fw-dark",
586
+ click: () => {
587
+ window.location.href = "/";
588
+ }
589
+ }
590
+ ],
591
+ canClose: false
592
+ });
593
+ accessRightModifiedDialog.open();
594
+ this.close(); // Close the editor operations.
595
+ }
596
+ /**
597
+ * Create a new E2EE document.
598
+ *
599
+ * If the user has set up a personal passphrase, generates a random
600
+ * document encryption key (DEK) and encrypts it with the master key.
601
+ * Otherwise, prompts for a per-document password.
602
+ *
603
+ * @returns {Promise<void>}
604
+ * @private
605
+ */
606
+ async _createE2EEDocument() {
607
+ let password, key, saltBase64, iterations;
608
+ // Check if the user has a personal passphrase set up
609
+ const hasPassphraseKeys = await PassphraseManager.hasEncryptionKeys();
610
+ if (hasPassphraseKeys) {
611
+ // Check if keys are unlocked in sessionStorage
612
+ if (!PassphraseManager.hasKeysInSession()) {
613
+ // Prompt for passphrase to unlock
614
+ const result = await new Promise(resolve => {
615
+ enterPassphraseDialog(pwd => resolve({ action: "unlock", passphrase: pwd }), () => resolve({ action: "recover" }));
616
+ });
617
+ if (result.action === "unlock" && result.passphrase) {
618
+ try {
619
+ await PassphraseManager.unlockWithPassphrase(result.passphrase);
620
+ }
621
+ catch (_e) {
622
+ addAlert("error", gettext("Incorrect passphrase."));
623
+ }
624
+ }
625
+ else if (result.action === "recover") {
626
+ // Recovery flow
627
+ const { recoverWithKeyDialog } = await import("./e2ee/passphrase-dialog.js");
628
+ const recoverResult = await new Promise(resolve => {
629
+ recoverWithKeyDialog(resolve);
630
+ });
631
+ if (recoverResult) {
632
+ try {
633
+ const { newRecoveryKey } = await PassphraseManager.recoverWithRecoveryKey(recoverResult.recoveryKey, recoverResult.newPassphrase);
634
+ const { showRecoveryKeyDialog } = await import("./e2ee/passphrase-dialog.js");
635
+ await new Promise(resolve => {
636
+ showRecoveryKeyDialog(newRecoveryKey, resolve);
637
+ });
638
+ }
639
+ catch (e) {
640
+ addAlert("error", gettext("Recovery failed: ") + e.message);
641
+ }
642
+ }
643
+ }
644
+ }
645
+ if (PassphraseManager.hasKeysInSession()) {
646
+ // Generate a random document password that is itself a raw DEK
647
+ password = await PassphraseManager.generateDocumentPassword();
648
+ }
649
+ // Fall through to per-document password if passphrase unlock failed
650
+ }
651
+ if (!password) {
652
+ // Prompt for per-document password
653
+ password = await new Promise(resolve => {
654
+ createPasswordDialog(pwd => resolve(pwd));
655
+ });
656
+ }
657
+ // Generate a random 16-byte salt
658
+ const salt = E2EEKeyManager.generateSalt();
659
+ saltBase64 = btoa(String.fromCharCode(...salt));
660
+ iterations = 600000;
661
+ // Resolve password to encryption key
662
+ key = await PassphraseManager.resolvePasswordToKey(password, salt, iterations);
663
+ // Create the document on the server with E2EE parameters
664
+ const { json, status } = await postJson("/api/document/create_doc/", {
665
+ template_id: this.docInfo.templateId,
666
+ path: this.docInfo.path,
667
+ e2ee: true,
668
+ e2ee_salt: saltBase64,
669
+ e2ee_iterations: iterations
670
+ });
671
+ if (status === 403) {
672
+ addAlert("error", json.error || gettext("E2EE is not enabled on this server."));
673
+ window.location.href = "/";
674
+ return;
675
+ }
676
+ this.docInfo.id = json.id;
677
+ window.history.replaceState("", "", `/document/${this.docInfo.id}/`);
678
+ delete this.docInfo.templateId;
679
+ // Store password in sessionStorage
680
+ E2EEKeyManager.storePasswordInSession(this.docInfo.id, password);
681
+ // If passphrase user, also save encrypted password on server
682
+ if (hasPassphraseKeys && PassphraseManager.hasKeysInSession()) {
683
+ try {
684
+ await PassphraseManager.saveDocumentPassword(this.docInfo.id, password, null, "user", true);
685
+ }
686
+ catch (_e) {
687
+ console.error("Failed to save document password:", _e);
688
+ }
689
+ }
690
+ else {
691
+ // Non-passphrase user: cache the key in sessionStorage
692
+ await E2EEKeyManager.storeKeyInSession(this.docInfo.id, key);
693
+ }
694
+ // Set up E2EE state on the editor
695
+ this.e2ee = {
696
+ encrypted: true,
697
+ encryptionSalt: saltBase64,
698
+ encryptionIterations: iterations,
699
+ key: key,
700
+ password: password,
701
+ snapshotManager: new E2EESnapshotManager(this),
702
+ usesPassphrase: hasPassphraseKeys && PassphraseManager.hasKeysInSession()
703
+ };
704
+ this.e2ee.snapshotManager.setKey(key);
705
+ }
706
+ close() {
707
+ if (this.menu.toolbarViews) {
708
+ this.menu.toolbarViews.forEach(view => view.destroy());
709
+ }
710
+ if (this.menu.selectionMenuViews) {
711
+ this.menu.selectionMenuViews.forEach(view => view.destroy());
712
+ }
713
+ if (this.menu.headerView) {
714
+ this.menu.headerView.destroy();
715
+ }
716
+ // For E2EE documents, update the sessionStorage title cache with the
717
+ // current ProseMirror title so the overview shows the correct title
718
+ // immediately, even before the encrypted snapshot has been saved to
719
+ // the DB.
720
+ if (this.e2ee && this.view) {
721
+ const titleNode = this.view.state.doc.firstChild;
722
+ if (titleNode) {
723
+ let title = "";
724
+ titleNode.forEach(child => {
725
+ if (!child.marks.find(mark => mark.type.name === "deletion")) {
726
+ title += child.textContent;
727
+ }
728
+ });
729
+ sessionStorage.setItem(`e2ee_title_${this.docInfo.id}`, title);
730
+ }
731
+ }
732
+ if (this.noCollabSave) {
733
+ this.noCollabSave.stop();
734
+ if (this.docInfo.access_rights === "write") {
735
+ // Fire a final save before closing. For SPA navigation the
736
+ // promise usually has enough time to complete; for actual page
737
+ // unload the beforeunload/pagehide handler covers us.
738
+ this.noCollabSave.save();
739
+ }
740
+ }
741
+ if (this.ws) {
742
+ // For E2EE documents, try to send one last encrypted snapshot before
743
+ // closing so the title (and any last-minute edits) are persisted.
744
+ // The WebSocket connector has an 80ms throttle between messages, so
745
+ // we delay ws.close() to give the snapshot time to be queued and
746
+ // sent. For SPA navigation 300ms is usually enough; for tab-close
747
+ // the browser may still kill us early.
748
+ // For non-E2EE documents we close immediately so the server's
749
+ // disconnect handler saves the document to the DB before the
750
+ // overview page loads its data (avoiding a race where the overview
751
+ // would still see the old/empty title).
752
+ if (this.e2ee &&
753
+ this.e2ee.key &&
754
+ this.e2ee.snapshotManager &&
755
+ this.docInfo.access_rights === "write") {
756
+ this.e2ee.snapshotManager.handleRequestSnapshot({});
757
+ window.setTimeout(() => this.ws.close(), 300);
758
+ }
759
+ else {
760
+ this.ws.close();
761
+ }
762
+ }
763
+ }
764
+ render() {
765
+ this.dom = document.createElement("body");
766
+ document.body = this.dom;
767
+ this.dom.classList.add("editor");
768
+ this.dom.classList.add("fw-scrollable");
769
+ this.dom.innerHTML = `<div id="editor">
770
+ <div id="fw-wait"><i class="fa-solid fa-spinner fa-pulse"></i></div>
771
+ <header>
772
+ <nav id="headerbar"><div></div></nav>
773
+ <nav id="toolbar"><div></div></nav>
774
+ </header>
775
+ <div id="navigator"></div>
776
+ <div id="editor-content">
777
+ <div id="flow">
778
+ <div id="paper-editable">
779
+ <div id="document-editable" class="user-contents"></div>
780
+ <div id="footnote-box-container" class="user-contents">
781
+ <div id="citation-footnote-box-container"></div>
782
+ </div>
783
+ </div>
784
+ <div id="bibliography" class="doc-bibliography user-contents"></div>
785
+ <div id="global-comments">
786
+ <div id="global-comment-container"><div></div></div>
787
+ <button id="new-global-comment" class="fw-button fw-light" type="button">
788
+ ${gettext("Add document comment")}
789
+ </button>
790
+ </div>
791
+ </div>
792
+ <nav id="selection-menu"><div></div></nav>
793
+ <div id="margin-box-column">
794
+ <div id="margin-box-filter"></div>
795
+ <div id="margin-box-container"><div></div></div>
796
+ </div>
797
+ </div>
798
+ <div id="chat">
799
+ <i class="resize-button fa-solid fa-angle-double-down"></i>
800
+ <div id="chat-container"></div>
801
+ <div id="messageform" contentEditable="true" class="empty"></div>
802
+ <audio id="chat-notification">
803
+ <source src="${staticUrl("ogg/chat_notification.ogg")}" type="audio/ogg">
804
+ </audio>
805
+ </div>
806
+ </div>
807
+ <div id="unobtrusive-messages"></div>`;
808
+ const feedbackTab = new FeedbackTab();
809
+ feedbackTab.init();
810
+ }
811
+ onResize() {
812
+ if (!this.view || !this.mod.marginboxes) {
813
+ // Editor not yet set up
814
+ return;
815
+ }
816
+ this.mod.marginboxes.updateDOM();
817
+ this.mod.footnotes.layout.updateDOM();
818
+ this.menu.toolbarViews.forEach(view => view.onResize());
819
+ }
820
+ onBeforeUnload() {
821
+ if (this.app.isOffline()) {
822
+ showSystemMessage(gettext("Changes you made to the document since going offline will be lost, if you choose to close/refresh the tab or close the browser."));
823
+ return true;
824
+ }
825
+ this.close();
826
+ }
827
+ initEditor() {
828
+ let setFocus = false;
829
+ this.view = new EditorView(this.dom.querySelector("#document-editable"), {
830
+ state: EditorState.create({
831
+ schema: this.schema
832
+ }),
833
+ handleDOMEvents: {
834
+ mousedown: (view, _event) => {
835
+ this.currentView = this.view;
836
+ view.focus();
837
+ return false;
838
+ },
839
+ focus: (view, _event) => {
840
+ if (!setFocus) {
841
+ this.currentView = this.view;
842
+ // We focus once more, as focus may have disappeared due to
843
+ // disappearing placeholders.
844
+ setFocus = true;
845
+ view.focus();
846
+ setFocus = false;
847
+ }
848
+ }
849
+ },
850
+ dispatchTransaction: tr => {
851
+ const trackedTr = amendTransaction(tr, this.view.state, this);
852
+ const { state: newState, transactions } = this.view.state.applyTransaction(trackedTr);
853
+ this.view.updateState(newState);
854
+ transactions.forEach(subTr => {
855
+ const footTr = subTr.getMeta("footTr");
856
+ if (footTr && footTr.steps.length) {
857
+ this.mod.footnotes.fnEditor.view.dispatch(footTr);
858
+ }
859
+ });
860
+ if (tr.steps.length) {
861
+ this.docInfo.updated = new Date();
862
+ }
863
+ // Update the header bar to reflect any title changes
864
+ if (this.menu.headerView) {
865
+ this.menu.headerView.update();
866
+ }
867
+ this.mod.collab.doc.sendToCollaborators();
868
+ }
869
+ });
870
+ // The editor that is currently being edited in -- main or footnote editor
871
+ this.currentView = this.view;
872
+ this.mod.citations.init();
873
+ this.mod.footnotes.init();
874
+ new ModDB(this);
875
+ new ModCollab(this);
876
+ new ModTrack(this);
877
+ new ModMarginboxes(this);
878
+ this.mod.marginboxes.init();
879
+ new ModComments(this);
880
+ new ModNavigator(this);
881
+ this.mod.navigator.init();
882
+ }
883
+ activateFidusPlugins() {
884
+ if (this.plugins) {
885
+ // Plugins have been activated already
886
+ return;
887
+ }
888
+ // Add plugins.
889
+ this.plugins = {};
890
+ return Promise.all(plugins.map(([app, plugin]) => {
891
+ if (!this.app.settings.APPS.includes(app)) {
892
+ return Promise.resolve();
893
+ }
894
+ return Promise.all(Object.values(plugin).map(pluginExport => {
895
+ if (typeof pluginExport === "function") {
896
+ this.plugins[pluginExport.name] = new pluginExport(this);
897
+ return (this.plugins[pluginExport.name].init() ||
898
+ Promise.resolve());
899
+ }
900
+ return Promise.resolve();
901
+ }));
902
+ }));
903
+ }
904
+ // Collect all components of the current doc. Needed for saving and export
905
+ // filters
906
+ getDoc(options = {}) {
907
+ const doc = this.app.isOffline() || Boolean(options.use_current_view)
908
+ ? this.view.docView.node
909
+ : this.docInfo.confirmedDoc;
910
+ const pmDoc = options.changes === "acceptAllNoInsertions"
911
+ ? acceptAllNoInsertions(doc)
912
+ : doc;
913
+ let title = "";
914
+ pmDoc.firstChild.forEach(child => {
915
+ if (!child.marks.find(mark => mark.type.name === "deletion")) {
916
+ title += child.textContent;
917
+ }
918
+ });
919
+ return {
920
+ content: pmDoc.toJSON(),
921
+ settings: getSettings(pmDoc),
922
+ title: title.substring(0, 255),
923
+ path: this.docInfo.path,
924
+ version: this.docInfo.version,
925
+ comments: this.mod.comments.store.comments,
926
+ id: this.docInfo.id,
927
+ updated: this.docInfo.updated
928
+ };
929
+ }
930
+ // Use PMs scrollIntoView function and adjust for top menu
931
+ scrollIdIntoView(id) {
932
+ let foundPos = false, view;
933
+ this.view.state.doc.descendants((node, pos) => {
934
+ if (foundPos) {
935
+ return;
936
+ }
937
+ else if ((node.type.groups.includes("heading") ||
938
+ node.type.name === "figure") &&
939
+ node.attrs.id === id) {
940
+ foundPos = node.type.name === "figure" ? pos : pos + 1;
941
+ view = this.view;
942
+ }
943
+ else {
944
+ const anchorMark = node.marks.find(mark => mark.type.name === "anchor");
945
+ if (anchorMark?.attrs.id === id) {
946
+ foundPos = pos + 1;
947
+ view = this.view;
948
+ }
949
+ }
950
+ });
951
+ if (!foundPos) {
952
+ this.mod.footnotes.fnEditor.view.state.doc.descendants((node, pos) => {
953
+ if (foundPos) {
954
+ return;
955
+ }
956
+ else if ((node.type.groups.includes("heading") ||
957
+ node.type.name === "figure") &&
958
+ node.attrs.id === id) {
959
+ foundPos = node.type.name === "figure" ? pos : pos + 1;
960
+ view = this.mod.footnotes.fnEditor.view;
961
+ }
962
+ else {
963
+ const anchorMark = node.marks.find(mark => mark.type.name === "anchor");
964
+ if (anchorMark?.attrs.id === id) {
965
+ foundPos = pos + 1;
966
+ view = this.mod.footnotes.fnEditor.view;
967
+ }
968
+ }
969
+ });
970
+ }
971
+ if (foundPos) {
972
+ this.scrollPosIntoView(foundPos, view);
973
+ }
974
+ }
975
+ scrollPosIntoView(pos, view) {
976
+ const topMenuHeight = this.dom.querySelector("header").offsetHeight + 10;
977
+ const $pos = view.state.doc.resolve(pos);
978
+ view.dispatch(view.state.tr.setSelection(new TextSelection($pos, $pos)));
979
+ view.focus();
980
+ const distanceFromTop = view.coordsAtPos(pos).top - topMenuHeight;
981
+ window.scrollBy({
982
+ left: 0,
983
+ top: distanceFromTop,
984
+ behavior: "smooth",
985
+ block: "center"
986
+ });
987
+ return;
988
+ }
989
+ scrollBibliographyIntoView() {
990
+ const topMenuHeight = this.dom.querySelector("header").offsetHeight + 10;
991
+ const bibliographyHeaderEl = document.querySelector("h1.doc-bibliography-header");
992
+ const distanceFromTop = bibliographyHeaderEl.getBoundingClientRect().top - topMenuHeight;
993
+ window.scrollBy({
994
+ left: 0,
995
+ top: distanceFromTop,
996
+ behavior: "smooth",
997
+ block: "center"
998
+ });
999
+ return;
1000
+ }
1001
+ }
1002
+ //# sourceMappingURL=index.js.map