@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.
- package/LICENSE +661 -0
- package/README.md +22 -0
- package/dist/bibliography/tools.js +53 -0
- package/dist/bibliography/tools.js.map +1 -0
- package/dist/citations/index.js +339 -0
- package/dist/citations/index.js.map +1 -0
- package/dist/citations/render.js +47 -0
- package/dist/citations/render.js.map +1 -0
- package/dist/clipboard/copy/index.js +2 -0
- package/dist/clipboard/copy/index.js.map +1 -0
- package/dist/clipboard/copy/schema.js +54 -0
- package/dist/clipboard/copy/schema.js.map +1 -0
- package/dist/clipboard/copy/serializers.js +132 -0
- package/dist/clipboard/copy/serializers.js.map +1 -0
- package/dist/clipboard/paste/fidus_writer.js +45 -0
- package/dist/clipboard/paste/fidus_writer.js.map +1 -0
- package/dist/clipboard/paste/general.js +76 -0
- package/dist/clipboard/paste/general.js.map +1 -0
- package/dist/clipboard/paste/google_docs.js +24 -0
- package/dist/clipboard/paste/google_docs.js.map +1 -0
- package/dist/clipboard/paste/html.js +68 -0
- package/dist/clipboard/paste/html.js.map +1 -0
- package/dist/clipboard/paste/index.js +3 -0
- package/dist/clipboard/paste/index.js.map +1 -0
- package/dist/clipboard/paste/libreoffice_writer.js +27 -0
- package/dist/clipboard/paste/libreoffice_writer.js.map +1 -0
- package/dist/clipboard/paste/microsoft_word.js +79 -0
- package/dist/clipboard/paste/microsoft_word.js.map +1 -0
- package/dist/clipboard/paste/text.js +41 -0
- package/dist/clipboard/paste/text.js.map +1 -0
- package/dist/collab/chat.js +154 -0
- package/dist/collab/chat.js.map +1 -0
- package/dist/collab/colors.js +62 -0
- package/dist/collab/colors.js.map +1 -0
- package/dist/collab/doc.js +1090 -0
- package/dist/collab/doc.js.map +1 -0
- package/dist/collab/index.js +75 -0
- package/dist/collab/index.js.map +1 -0
- package/dist/collab/merge/changeset.js +90 -0
- package/dist/collab/merge/changeset.js.map +1 -0
- package/dist/collab/merge/editor.js +708 -0
- package/dist/collab/merge/editor.js.map +1 -0
- package/dist/collab/merge/footnotes.js +149 -0
- package/dist/collab/merge/footnotes.js.map +1 -0
- package/dist/collab/merge/index.js +270 -0
- package/dist/collab/merge/index.js.map +1 -0
- package/dist/collab/merge/recreate_transform.js +259 -0
- package/dist/collab/merge/recreate_transform.js.map +1 -0
- package/dist/collab/merge/schema.js +117 -0
- package/dist/collab/merge/schema.js.map +1 -0
- package/dist/collab/merge/state_plugins/action.js +138 -0
- package/dist/collab/merge/state_plugins/action.js.map +1 -0
- package/dist/collab/merge/state_plugins/clipboard.js +32 -0
- package/dist/collab/merge/state_plugins/clipboard.js.map +1 -0
- package/dist/collab/merge/state_plugins/diff.js +541 -0
- package/dist/collab/merge/state_plugins/diff.js.map +1 -0
- package/dist/collab/merge/state_plugins/index.js +3 -0
- package/dist/collab/merge/state_plugins/index.js.map +1 -0
- package/dist/collab/merge/tools.js +175 -0
- package/dist/collab/merge/tools.js.map +1 -0
- package/dist/collab/templates.js +10 -0
- package/dist/collab/templates.js.map +1 -0
- package/dist/comments/comment.js +17 -0
- package/dist/comments/comment.js.map +1 -0
- package/dist/comments/editors/answer.js +43 -0
- package/dist/comments/editors/answer.js.map +1 -0
- package/dist/comments/editors/comment.js +278 -0
- package/dist/comments/editors/comment.js.map +1 -0
- package/dist/comments/editors/index.js +4 -0
- package/dist/comments/editors/index.js.map +1 -0
- package/dist/comments/editors/notify.js +13 -0
- package/dist/comments/editors/notify.js.map +1 -0
- package/dist/comments/editors/schema.js +60 -0
- package/dist/comments/editors/schema.js.map +1 -0
- package/dist/comments/index.js +11 -0
- package/dist/comments/index.js.map +1 -0
- package/dist/comments/interactions.js +379 -0
- package/dist/comments/interactions.js.map +1 -0
- package/dist/comments/store.js +438 -0
- package/dist/comments/store.js.map +1 -0
- package/dist/contacts/add_dialog.js +88 -0
- package/dist/contacts/add_dialog.js.map +1 -0
- package/dist/contacts/templates.js +23 -0
- package/dist/contacts/templates.js.map +1 -0
- package/dist/copyright_dialog/index.js +148 -0
- package/dist/copyright_dialog/index.js.map +1 -0
- package/dist/copyright_dialog/templates.js +48 -0
- package/dist/copyright_dialog/templates.js.map +1 -0
- package/dist/databases/bibliography.js +162 -0
- package/dist/databases/bibliography.js.map +1 -0
- package/dist/databases/images.js +260 -0
- package/dist/databases/images.js.map +1 -0
- package/dist/databases/index.js +55 -0
- package/dist/databases/index.js.map +1 -0
- package/dist/dialogs/citation.js +321 -0
- package/dist/dialogs/citation.js.map +1 -0
- package/dist/dialogs/code_block.js +192 -0
- package/dist/dialogs/code_block.js.map +1 -0
- package/dist/dialogs/contributor.js +132 -0
- package/dist/dialogs/contributor.js.map +1 -0
- package/dist/dialogs/figure.js +378 -0
- package/dist/dialogs/figure.js.map +1 -0
- package/dist/dialogs/index.js +11 -0
- package/dist/dialogs/index.js.map +1 -0
- package/dist/dialogs/language.js +47 -0
- package/dist/dialogs/language.js.map +1 -0
- package/dist/dialogs/link.js +243 -0
- package/dist/dialogs/link.js.map +1 -0
- package/dist/dialogs/math.js +121 -0
- package/dist/dialogs/math.js.map +1 -0
- package/dist/dialogs/ordered_list.js +67 -0
- package/dist/dialogs/ordered_list.js.map +1 -0
- package/dist/dialogs/revision.js +36 -0
- package/dist/dialogs/revision.js.map +1 -0
- package/dist/dialogs/subsup.js +109 -0
- package/dist/dialogs/subsup.js.map +1 -0
- package/dist/dialogs/table.js +217 -0
- package/dist/dialogs/table.js.map +1 -0
- package/dist/dialogs/templates.js +366 -0
- package/dist/dialogs/templates.js.map +1 -0
- package/dist/document_template/exporter.js +83 -0
- package/dist/document_template/exporter.js.map +1 -0
- package/dist/document_template/index.js +327 -0
- package/dist/document_template/index.js.map +1 -0
- package/dist/documents/access_rights/index.js +532 -0
- package/dist/documents/access_rights/index.js.map +1 -0
- package/dist/documents/access_rights/templates.js +150 -0
- package/dist/documents/access_rights/templates.js.map +1 -0
- package/dist/e2ee/encryptor.js +201 -0
- package/dist/e2ee/encryptor.js.map +1 -0
- package/dist/e2ee/key-manager.js +171 -0
- package/dist/e2ee/key-manager.js.map +1 -0
- package/dist/e2ee/passphrase-crypto.js +365 -0
- package/dist/e2ee/passphrase-crypto.js.map +1 -0
- package/dist/e2ee/passphrase-dialog.js +548 -0
- package/dist/e2ee/passphrase-dialog.js.map +1 -0
- package/dist/e2ee/passphrase-manager.js +350 -0
- package/dist/e2ee/passphrase-manager.js.map +1 -0
- package/dist/e2ee/password-dialog.js +552 -0
- package/dist/e2ee/password-dialog.js.map +1 -0
- package/dist/e2ee/snapshot-manager.js +238 -0
- package/dist/e2ee/snapshot-manager.js.map +1 -0
- package/dist/exporter/native/copy.js +29 -0
- package/dist/exporter/native/copy.js.map +1 -0
- package/dist/exporter/native/file.js +19 -0
- package/dist/exporter/native/file.js.map +1 -0
- package/dist/exporter/native/index.js +4 -0
- package/dist/exporter/native/index.js.map +1 -0
- package/dist/exporter/native/revision.js +54 -0
- package/dist/exporter/native/revision.js.map +1 -0
- package/dist/exporter/native/zip.js +2 -0
- package/dist/exporter/native/zip.js.map +1 -0
- package/dist/footnotes/editor.js +180 -0
- package/dist/footnotes/editor.js.map +1 -0
- package/dist/footnotes/index.js +15 -0
- package/dist/footnotes/index.js.map +1 -0
- package/dist/footnotes/layout.js +125 -0
- package/dist/footnotes/layout.js.map +1 -0
- package/dist/footnotes/state_plugins/access_rights.js +25 -0
- package/dist/footnotes/state_plugins/access_rights.js.map +1 -0
- package/dist/footnotes/state_plugins/index.js +2 -0
- package/dist/footnotes/state_plugins/index.js.map +1 -0
- package/dist/images/edit_dialog/edit_dialog/index.js +186 -0
- package/dist/images/edit_dialog/edit_dialog/index.js.map +1 -0
- package/dist/images/edit_dialog/edit_dialog/model.js +152 -0
- package/dist/images/edit_dialog/edit_dialog/model.js.map +1 -0
- package/dist/images/edit_dialog/edit_dialog/templates.js +33 -0
- package/dist/images/edit_dialog/edit_dialog/templates.js.map +1 -0
- package/dist/images/edit_dialog/model.js +6 -0
- package/dist/images/edit_dialog/model.js.map +1 -0
- package/dist/images/selection_dialog/index.js +149 -0
- package/dist/images/selection_dialog/index.js.map +1 -0
- package/dist/index.js +1002 -0
- package/dist/index.js.map +1 -0
- package/dist/keymap.js +73 -0
- package/dist/keymap.js.map +1 -0
- package/dist/marginboxes/index.js +674 -0
- package/dist/marginboxes/index.js.map +1 -0
- package/dist/marginboxes/templates.js +447 -0
- package/dist/marginboxes/templates.js.map +1 -0
- package/dist/menus/code_block/index.js +2 -0
- package/dist/menus/code_block/index.js.map +1 -0
- package/dist/menus/code_block/model.js +49 -0
- package/dist/menus/code_block/model.js.map +1 -0
- package/dist/menus/figure/index.js +3 -0
- package/dist/menus/figure/index.js.map +1 -0
- package/dist/menus/figure/model.js +80 -0
- package/dist/menus/figure/model.js.map +1 -0
- package/dist/menus/figure/utils.js +6 -0
- package/dist/menus/figure/utils.js.map +1 -0
- package/dist/menus/headerbar/index.js +3 -0
- package/dist/menus/headerbar/index.js.map +1 -0
- package/dist/menus/headerbar/model.js +743 -0
- package/dist/menus/headerbar/model.js.map +1 -0
- package/dist/menus/headerbar/view.js +579 -0
- package/dist/menus/headerbar/view.js.map +1 -0
- package/dist/menus/image/index.js +2 -0
- package/dist/menus/image/index.js.map +1 -0
- package/dist/menus/image/model.js +32 -0
- package/dist/menus/image/model.js.map +1 -0
- package/dist/menus/index.js +10 -0
- package/dist/menus/index.js.map +1 -0
- package/dist/menus/navigator/index.js +2 -0
- package/dist/menus/navigator/index.js.map +1 -0
- package/dist/menus/navigator/model.js +29 -0
- package/dist/menus/navigator/model.js.map +1 -0
- package/dist/menus/ordered_list/index.js +2 -0
- package/dist/menus/ordered_list/index.js.map +1 -0
- package/dist/menus/ordered_list/model.js +17 -0
- package/dist/menus/ordered_list/model.js.map +1 -0
- package/dist/menus/selection/index.js +3 -0
- package/dist/menus/selection/index.js.map +1 -0
- package/dist/menus/selection/model.js +108 -0
- package/dist/menus/selection/model.js.map +1 -0
- package/dist/menus/selection/view.js +125 -0
- package/dist/menus/selection/view.js.map +1 -0
- package/dist/menus/table/index.js +2 -0
- package/dist/menus/table/index.js.map +1 -0
- package/dist/menus/table/model.js +319 -0
- package/dist/menus/table/model.js.map +1 -0
- package/dist/menus/toolbar/index.js +3 -0
- package/dist/menus/toolbar/index.js.map +1 -0
- package/dist/menus/toolbar/model.js +747 -0
- package/dist/menus/toolbar/model.js.map +1 -0
- package/dist/menus/toolbar/view.js +314 -0
- package/dist/menus/toolbar/view.js.map +1 -0
- package/dist/navigator/index.js +435 -0
- package/dist/navigator/index.js.map +1 -0
- package/dist/no_collab_save/index.js +169 -0
- package/dist/no_collab_save/index.js.map +1 -0
- package/dist/plugins/citation_dialog/index.js +2 -0
- package/dist/plugins/citation_dialog/index.js.map +1 -0
- package/dist/plugins/editor/index.js +2 -0
- package/dist/plugins/editor/index.js.map +1 -0
- package/dist/state_plugins/access_rights.js +30 -0
- package/dist/state_plugins/access_rights.js.map +1 -0
- package/dist/state_plugins/citation_render.js +78 -0
- package/dist/state_plugins/citation_render.js.map +1 -0
- package/dist/state_plugins/clipboard.js +217 -0
- package/dist/state_plugins/clipboard.js.map +1 -0
- package/dist/state_plugins/code_block.js +165 -0
- package/dist/state_plugins/code_block.js.map +1 -0
- package/dist/state_plugins/collab_carets.js +115 -0
- package/dist/state_plugins/collab_carets.js.map +1 -0
- package/dist/state_plugins/comments.js +197 -0
- package/dist/state_plugins/comments.js.map +1 -0
- package/dist/state_plugins/contributor_input/add_button.js +101 -0
- package/dist/state_plugins/contributor_input/add_button.js.map +1 -0
- package/dist/state_plugins/contributor_input/dropup.js +273 -0
- package/dist/state_plugins/contributor_input/dropup.js.map +1 -0
- package/dist/state_plugins/contributor_input/helpers.js +23 -0
- package/dist/state_plugins/contributor_input/helpers.js.map +1 -0
- package/dist/state_plugins/contributor_input/index.js +3 -0
- package/dist/state_plugins/contributor_input/index.js.map +1 -0
- package/dist/state_plugins/contributor_input/node_view.js +76 -0
- package/dist/state_plugins/contributor_input/node_view.js.map +1 -0
- package/dist/state_plugins/contributor_input/plugin.js +236 -0
- package/dist/state_plugins/contributor_input/plugin.js.map +1 -0
- package/dist/state_plugins/document_template.js +213 -0
- package/dist/state_plugins/document_template.js.map +1 -0
- package/dist/state_plugins/figure.js +90 -0
- package/dist/state_plugins/figure.js.map +1 -0
- package/dist/state_plugins/footnote_markers.js +187 -0
- package/dist/state_plugins/footnote_markers.js.map +1 -0
- package/dist/state_plugins/headerbar.js +10 -0
- package/dist/state_plugins/headerbar.js.map +1 -0
- package/dist/state_plugins/index.js +27 -0
- package/dist/state_plugins/index.js.map +1 -0
- package/dist/state_plugins/inline_math.js +719 -0
- package/dist/state_plugins/inline_math.js.map +1 -0
- package/dist/state_plugins/inline_reference/dropup.js +182 -0
- package/dist/state_plugins/inline_reference/dropup.js.map +1 -0
- package/dist/state_plugins/inline_reference/index.js +2 -0
- package/dist/state_plugins/inline_reference/index.js.map +1 -0
- package/dist/state_plugins/inline_reference/plugin.js +1134 -0
- package/dist/state_plugins/inline_reference/plugin.js.map +1 -0
- package/dist/state_plugins/jump_hidden_nodes.js +47 -0
- package/dist/state_plugins/jump_hidden_nodes.js.map +1 -0
- package/dist/state_plugins/links.js +622 -0
- package/dist/state_plugins/links.js.map +1 -0
- package/dist/state_plugins/marginboxes.js +13 -0
- package/dist/state_plugins/marginboxes.js.map +1 -0
- package/dist/state_plugins/ordered_list_menu.js +72 -0
- package/dist/state_plugins/ordered_list_menu.js.map +1 -0
- package/dist/state_plugins/placeholders.js +98 -0
- package/dist/state_plugins/placeholders.js.map +1 -0
- package/dist/state_plugins/search.js +212 -0
- package/dist/state_plugins/search.js.map +1 -0
- package/dist/state_plugins/selection_menu.js +10 -0
- package/dist/state_plugins/selection_menu.js.map +1 -0
- package/dist/state_plugins/settings.js +191 -0
- package/dist/state_plugins/settings.js.map +1 -0
- package/dist/state_plugins/table.js +104 -0
- package/dist/state_plugins/table.js.map +1 -0
- package/dist/state_plugins/tag_input/helpers.js +55 -0
- package/dist/state_plugins/tag_input/helpers.js.map +1 -0
- package/dist/state_plugins/tag_input/index.js +3 -0
- package/dist/state_plugins/tag_input/index.js.map +1 -0
- package/dist/state_plugins/tag_input/node_view.js +77 -0
- package/dist/state_plugins/tag_input/node_view.js.map +1 -0
- package/dist/state_plugins/tag_input/plugin.js +199 -0
- package/dist/state_plugins/tag_input/plugin.js.map +1 -0
- package/dist/state_plugins/tag_input/tag_editor.js +159 -0
- package/dist/state_plugins/tag_input/tag_editor.js.map +1 -0
- package/dist/state_plugins/tag_input/tag_editor_plugins.js +61 -0
- package/dist/state_plugins/tag_input/tag_editor_plugins.js.map +1 -0
- package/dist/state_plugins/toc_render.js +142 -0
- package/dist/state_plugins/toc_render.js.map +1 -0
- package/dist/state_plugins/toolbar.js +10 -0
- package/dist/state_plugins/toolbar.js.map +1 -0
- package/dist/state_plugins/track/find_selected_changes.js +82 -0
- package/dist/state_plugins/track/find_selected_changes.js.map +1 -0
- package/dist/state_plugins/track/helpers.js +67 -0
- package/dist/state_plugins/track/helpers.js.map +1 -0
- package/dist/state_plugins/track/index.js +3 -0
- package/dist/state_plugins/track/index.js.map +1 -0
- package/dist/state_plugins/track/plugin.js +114 -0
- package/dist/state_plugins/track/plugin.js.map +1 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/key_bindings/index.js +20 -0
- package/dist/tools/key_bindings/index.js.map +1 -0
- package/dist/tools/key_bindings/templates.js +68 -0
- package/dist/tools/key_bindings/templates.js.map +1 -0
- package/dist/tools/search_replace/index.js +275 -0
- package/dist/tools/search_replace/index.js.map +1 -0
- package/dist/tools/search_replace/templates.js +18 -0
- package/dist/tools/search_replace/templates.js.map +1 -0
- package/dist/tools/word_count/index.js +40 -0
- package/dist/tools/word_count/index.js.map +1 -0
- package/dist/tools/word_count/templates.js +27 -0
- package/dist/tools/word_count/templates.js.map +1 -0
- package/dist/track/accept.js +66 -0
- package/dist/track/accept.js.map +1 -0
- package/dist/track/accept_all.js +51 -0
- package/dist/track/accept_all.js.map +1 -0
- package/dist/track/accept_all_no_insertions.js +63 -0
- package/dist/track/accept_all_no_insertions.js.map +1 -0
- package/dist/track/amend_transaction.js +390 -0
- package/dist/track/amend_transaction.js.map +1 -0
- package/dist/track/delete.js +62 -0
- package/dist/track/delete.js.map +1 -0
- package/dist/track/index.js +6 -0
- package/dist/track/index.js.map +1 -0
- package/dist/track/mod.js +86 -0
- package/dist/track/mod.js.map +1 -0
- package/dist/track/reject.js +67 -0
- package/dist/track/reject.js.map +1 -0
- package/dist/track/reject_all.js +56 -0
- package/dist/track/reject_all.js.map +1 -0
- package/package.json +80 -0
- package/src/bibliography/tools.js +58 -0
- package/src/citations/index.js +443 -0
- package/src/citations/render.js +68 -0
- package/src/clipboard/copy/index.js +1 -0
- package/src/clipboard/copy/schema.js +62 -0
- package/src/clipboard/copy/serializers.js +179 -0
- package/src/clipboard/paste/fidus_writer.js +55 -0
- package/src/clipboard/paste/general.js +80 -0
- package/src/clipboard/paste/google_docs.js +26 -0
- package/src/clipboard/paste/html.js +76 -0
- package/src/clipboard/paste/index.js +2 -0
- package/src/clipboard/paste/libreoffice_writer.js +32 -0
- package/src/clipboard/paste/microsoft_word.js +87 -0
- package/src/clipboard/paste/text.js +58 -0
- package/src/collab/chat.js +195 -0
- package/src/collab/colors.js +67 -0
- package/src/collab/doc.js +1436 -0
- package/src/collab/index.js +81 -0
- package/src/collab/merge/changeset.js +103 -0
- package/src/collab/merge/editor.js +1065 -0
- package/src/collab/merge/footnotes.js +175 -0
- package/src/collab/merge/index.js +401 -0
- package/src/collab/merge/recreate_transform.js +326 -0
- package/src/collab/merge/schema.js +135 -0
- package/src/collab/merge/state_plugins/action.js +171 -0
- package/src/collab/merge/state_plugins/clipboard.js +32 -0
- package/src/collab/merge/state_plugins/diff.js +780 -0
- package/src/collab/merge/state_plugins/index.js +2 -0
- package/src/collab/merge/tools.js +217 -0
- package/src/collab/templates.js +10 -0
- package/src/comments/comment.js +29 -0
- package/src/comments/editors/answer.js +55 -0
- package/src/comments/editors/comment.js +318 -0
- package/src/comments/editors/index.js +4 -0
- package/src/comments/editors/notify.js +13 -0
- package/src/comments/editors/schema.js +65 -0
- package/src/comments/index.js +11 -0
- package/src/comments/interactions.js +465 -0
- package/src/comments/store.js +513 -0
- package/src/contacts/add_dialog.js +110 -0
- package/src/contacts/templates.js +28 -0
- package/src/copyright_dialog/index.js +179 -0
- package/src/copyright_dialog/templates.js +56 -0
- package/src/databases/bibliography.js +179 -0
- package/src/databases/images.js +320 -0
- package/src/databases/index.js +69 -0
- package/src/dialogs/citation.js +428 -0
- package/src/dialogs/code_block.js +242 -0
- package/src/dialogs/contributor.js +173 -0
- package/src/dialogs/figure.js +479 -0
- package/src/dialogs/index.js +10 -0
- package/src/dialogs/language.js +58 -0
- package/src/dialogs/link.js +358 -0
- package/src/dialogs/math.js +148 -0
- package/src/dialogs/ordered_list.js +84 -0
- package/src/dialogs/revision.js +43 -0
- package/src/dialogs/subsup.js +116 -0
- package/src/dialogs/table.js +258 -0
- package/src/dialogs/templates.js +426 -0
- package/src/document_template/exporter.js +97 -0
- package/src/document_template/index.js +483 -0
- package/src/documents/access_rights/index.js +693 -0
- package/src/documents/access_rights/templates.js +178 -0
- package/src/e2ee/encryptor.js +228 -0
- package/src/e2ee/key-manager.js +202 -0
- package/src/e2ee/passphrase-crypto.js +606 -0
- package/src/e2ee/passphrase-dialog.js +655 -0
- package/src/e2ee/passphrase-manager.js +502 -0
- package/src/e2ee/password-dialog.js +652 -0
- package/src/e2ee/snapshot-manager.js +305 -0
- package/src/exporter/native/copy.js +54 -0
- package/src/exporter/native/file.js +37 -0
- package/src/exporter/native/index.js +3 -0
- package/src/exporter/native/revision.js +86 -0
- package/src/exporter/native/zip.js +1 -0
- package/src/footnotes/editor.js +227 -0
- package/src/footnotes/index.js +16 -0
- package/src/footnotes/layout.js +190 -0
- package/src/footnotes/state_plugins/access_rights.js +39 -0
- package/src/footnotes/state_plugins/index.js +1 -0
- package/src/images/edit_dialog/edit_dialog/index.js +222 -0
- package/src/images/edit_dialog/edit_dialog/model.js +183 -0
- package/src/images/edit_dialog/edit_dialog/templates.js +41 -0
- package/src/images/edit_dialog/model.js +5 -0
- package/src/images/selection_dialog/index.js +176 -0
- package/src/index.js +1252 -0
- package/src/keymap.js +102 -0
- package/src/marginboxes/index.js +946 -0
- package/src/marginboxes/templates.js +573 -0
- package/src/menus/code_block/index.js +1 -0
- package/src/menus/code_block/model.js +52 -0
- package/src/menus/figure/index.js +2 -0
- package/src/menus/figure/model.js +91 -0
- package/src/menus/figure/utils.js +5 -0
- package/src/menus/headerbar/index.js +2 -0
- package/src/menus/headerbar/model.js +1077 -0
- package/src/menus/headerbar/view.js +675 -0
- package/src/menus/image/index.js +1 -0
- package/src/menus/image/model.js +32 -0
- package/src/menus/index.js +9 -0
- package/src/menus/navigator/index.js +1 -0
- package/src/menus/navigator/model.js +28 -0
- package/src/menus/ordered_list/index.js +1 -0
- package/src/menus/ordered_list/model.js +19 -0
- package/src/menus/selection/index.js +2 -0
- package/src/menus/selection/model.js +134 -0
- package/src/menus/selection/view.js +154 -0
- package/src/menus/table/index.js +1 -0
- package/src/menus/table/model.js +414 -0
- package/src/menus/toolbar/index.js +2 -0
- package/src/menus/toolbar/model.js +920 -0
- package/src/menus/toolbar/view.js +354 -0
- package/src/navigator/index.js +515 -0
- package/src/no_collab_save/index.js +209 -0
- package/src/plugins/citation_dialog/index.js +1 -0
- package/src/plugins/editor/index.js +1 -0
- package/src/state_plugins/access_rights.js +47 -0
- package/src/state_plugins/citation_render.js +88 -0
- package/src/state_plugins/clipboard.js +289 -0
- package/src/state_plugins/code_block.js +201 -0
- package/src/state_plugins/collab_carets.js +152 -0
- package/src/state_plugins/comments.js +259 -0
- package/src/state_plugins/contributor_input/add_button.js +134 -0
- package/src/state_plugins/contributor_input/dropup.js +318 -0
- package/src/state_plugins/contributor_input/helpers.js +24 -0
- package/src/state_plugins/contributor_input/index.js +2 -0
- package/src/state_plugins/contributor_input/node_view.js +86 -0
- package/src/state_plugins/contributor_input/plugin.js +318 -0
- package/src/state_plugins/document_template.js +315 -0
- package/src/state_plugins/figure.js +108 -0
- package/src/state_plugins/footnote_markers.js +238 -0
- package/src/state_plugins/headerbar.js +12 -0
- package/src/state_plugins/index.js +73 -0
- package/src/state_plugins/inline_math.js +918 -0
- package/src/state_plugins/inline_reference/dropup.js +208 -0
- package/src/state_plugins/inline_reference/index.js +5 -0
- package/src/state_plugins/inline_reference/plugin.js +1429 -0
- package/src/state_plugins/jump_hidden_nodes.js +58 -0
- package/src/state_plugins/links.js +830 -0
- package/src/state_plugins/marginboxes.js +14 -0
- package/src/state_plugins/ordered_list_menu.js +84 -0
- package/src/state_plugins/placeholders.js +123 -0
- package/src/state_plugins/search.js +251 -0
- package/src/state_plugins/selection_menu.js +12 -0
- package/src/state_plugins/settings.js +223 -0
- package/src/state_plugins/table.js +127 -0
- package/src/state_plugins/tag_input/helpers.js +69 -0
- package/src/state_plugins/tag_input/index.js +2 -0
- package/src/state_plugins/tag_input/node_view.js +85 -0
- package/src/state_plugins/tag_input/plugin.js +278 -0
- package/src/state_plugins/tag_input/tag_editor.js +202 -0
- package/src/state_plugins/tag_input/tag_editor_plugins.js +74 -0
- package/src/state_plugins/toc_render.js +160 -0
- package/src/state_plugins/toolbar.js +12 -0
- package/src/state_plugins/track/find_selected_changes.js +107 -0
- package/src/state_plugins/track/helpers.js +108 -0
- package/src/state_plugins/track/index.js +6 -0
- package/src/state_plugins/track/plugin.js +140 -0
- package/src/tools/index.js +3 -0
- package/src/tools/key_bindings/index.js +21 -0
- package/src/tools/key_bindings/templates.js +68 -0
- package/src/tools/search_replace/index.js +421 -0
- package/src/tools/search_replace/templates.js +19 -0
- package/src/tools/word_count/index.js +59 -0
- package/src/tools/word_count/templates.js +34 -0
- package/src/track/accept.js +102 -0
- package/src/track/accept_all.js +103 -0
- package/src/track/accept_all_no_insertions.js +102 -0
- package/src/track/amend_transaction.js +562 -0
- package/src/track/delete.js +92 -0
- package/src/track/index.js +8 -0
- package/src/track/mod.js +116 -0
- package/src/track/reject.js +116 -0
- package/src/track/reject_all.js +121 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2EE Password Dialog - UI for entering, creating, and changing
|
|
3
|
+
* document passwords for end-to-end encrypted documents.
|
|
4
|
+
*
|
|
5
|
+
* Provides three dialog types:
|
|
6
|
+
* 1. Enter password - when opening an encrypted document
|
|
7
|
+
* 2. Create password - when creating a new encrypted document
|
|
8
|
+
* 3. Change password - when changing the password of an existing encrypted document
|
|
9
|
+
*
|
|
10
|
+
* Password requirements:
|
|
11
|
+
* - Minimum 5 characters
|
|
12
|
+
* While the requirements are not satisfactory for security,
|
|
13
|
+
* the user is shown a degree of weakness and is therefore encouraged to use a stronger password.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {Dialog, escapeText} from "fwtoolkit"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validate a password against the minimum requirements.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} password - The password to validate
|
|
22
|
+
* @returns {{valid: boolean, message: string}} Validation result
|
|
23
|
+
*/
|
|
24
|
+
export function validatePassword(password) {
|
|
25
|
+
if (!password || password.length < 5) {
|
|
26
|
+
return {
|
|
27
|
+
valid: false,
|
|
28
|
+
message: gettext("Password must be at least 5 characters long.")
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {valid: true, message: ""}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Calculate a rough password strength score (0-4).
|
|
36
|
+
*
|
|
37
|
+
* Based on length, character variety, and common patterns.
|
|
38
|
+
* This is a simple heuristic — not a substitute for a proper
|
|
39
|
+
* password strength estimator like zxcvbn, but sufficient for
|
|
40
|
+
* a basic strength meter.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} password - The password to evaluate
|
|
43
|
+
* @returns {number} Strength score from 0 (very weak) to 4 (very strong)
|
|
44
|
+
*/
|
|
45
|
+
export function passwordStrength(password) {
|
|
46
|
+
if (!password) {
|
|
47
|
+
return 0
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let score = 0
|
|
51
|
+
|
|
52
|
+
// Length contributions
|
|
53
|
+
if (password.length >= 12) {
|
|
54
|
+
score++
|
|
55
|
+
}
|
|
56
|
+
if (password.length >= 16) {
|
|
57
|
+
score++
|
|
58
|
+
}
|
|
59
|
+
if (password.length >= 20) {
|
|
60
|
+
score++
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Character variety contributions
|
|
64
|
+
const hasLower = /[a-z]/.test(password)
|
|
65
|
+
const hasUpper = /[A-Z]/.test(password)
|
|
66
|
+
const hasDigit = /[0-9]/.test(password)
|
|
67
|
+
const hasSpecial = /[^a-zA-Z0-9]/.test(password)
|
|
68
|
+
|
|
69
|
+
const varietyCount = [hasLower, hasUpper, hasDigit, hasSpecial].filter(
|
|
70
|
+
Boolean
|
|
71
|
+
).length
|
|
72
|
+
if (varietyCount >= 3) {
|
|
73
|
+
score++
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Penalize very common patterns
|
|
77
|
+
const commonPatterns = [
|
|
78
|
+
/^123456/,
|
|
79
|
+
/^123123/,
|
|
80
|
+
/^password/i,
|
|
81
|
+
/^qwerty/i,
|
|
82
|
+
/^abc123/i,
|
|
83
|
+
/(.)\1{3,}/ // Repeated characters (4+ in a row)
|
|
84
|
+
]
|
|
85
|
+
for (const pattern of commonPatterns) {
|
|
86
|
+
if (pattern.test(password)) {
|
|
87
|
+
score = Math.max(0, score - 2)
|
|
88
|
+
break
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return Math.min(4, Math.max(0, score))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get a CSS class and label for a password strength score.
|
|
97
|
+
*
|
|
98
|
+
* @param {number} score - Strength score from passwordStrength()
|
|
99
|
+
* @returns {{cssClass: string, label: string}}
|
|
100
|
+
*/
|
|
101
|
+
export function strengthInfo(score) {
|
|
102
|
+
const levels = [
|
|
103
|
+
{cssClass: "very-weak", label: gettext("Very weak")},
|
|
104
|
+
{cssClass: "weak", label: gettext("Weak")},
|
|
105
|
+
{cssClass: "fair", label: gettext("Fair")},
|
|
106
|
+
{cssClass: "strong", label: gettext("Strong")},
|
|
107
|
+
{cssClass: "very-strong", label: gettext("Very strong")}
|
|
108
|
+
]
|
|
109
|
+
return levels[score] || levels[0]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Show a dialog for entering the password to decrypt an E2EE document.
|
|
114
|
+
*
|
|
115
|
+
* This dialog is shown when a user opens an encrypted document.
|
|
116
|
+
* The user must enter the password to derive the decryption key.
|
|
117
|
+
*
|
|
118
|
+
* If the URL contains a fragment (e.g., #PASSWORD from a share link),
|
|
119
|
+
* the password field is pre-filled and the dialog can be auto-submitted.
|
|
120
|
+
*
|
|
121
|
+
* @param {Function} onPassword - Callback called with the entered password string
|
|
122
|
+
* @param {string} [urlFragment] - Password from URL fragment (share link), if available
|
|
123
|
+
* @returns {Promise<void>}
|
|
124
|
+
*/
|
|
125
|
+
export function enterPasswordDialog(
|
|
126
|
+
onPassword,
|
|
127
|
+
urlFragment = "",
|
|
128
|
+
onCancel = null
|
|
129
|
+
) {
|
|
130
|
+
return new Promise(resolve => {
|
|
131
|
+
const dialogId = "e2ee-enter-password"
|
|
132
|
+
|
|
133
|
+
// If password was provided via URL fragment, use it directly
|
|
134
|
+
if (urlFragment && urlFragment.length > 0) {
|
|
135
|
+
onPassword(urlFragment)
|
|
136
|
+
resolve()
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const body = `
|
|
141
|
+
<div class="e2ee-password-dialog">
|
|
142
|
+
<p>${gettext("This document is end-to-end encrypted. Enter the password to decrypt it.")}</p>
|
|
143
|
+
<div class="e2ee-password-field">
|
|
144
|
+
<label for="e2ee-password-input">${gettext("Password")}</label>
|
|
145
|
+
<input type="password" id="e2ee-password-input" class="e2ee-password-input"
|
|
146
|
+
autocomplete="off" data-1p-ignore data-lp-ignore data-lpignore="true" data-bwignore data-form-type="other" autofocus />
|
|
147
|
+
<button type="button" class="e2ee-toggle-visibility" title="${gettext("Show password")}">
|
|
148
|
+
<i class="fa-solid fa-eye"></i>
|
|
149
|
+
</button>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="e2ee-password-error" id="e2ee-password-error"></div>
|
|
152
|
+
</div>
|
|
153
|
+
`
|
|
154
|
+
|
|
155
|
+
// Declare dialogInstance before buttons so the click handler
|
|
156
|
+
// can call dialogInstance.close().
|
|
157
|
+
let dialogInstance
|
|
158
|
+
|
|
159
|
+
const buttons = [
|
|
160
|
+
{
|
|
161
|
+
text: gettext("Decrypt"),
|
|
162
|
+
classes: "fw-button fw-dark",
|
|
163
|
+
click: () => {
|
|
164
|
+
const input = document.getElementById("e2ee-password-input")
|
|
165
|
+
const password = input ? input.value : ""
|
|
166
|
+
if (password.length === 0) {
|
|
167
|
+
const errorEl = document.getElementById(
|
|
168
|
+
"e2ee-password-error"
|
|
169
|
+
)
|
|
170
|
+
if (errorEl) {
|
|
171
|
+
errorEl.textContent = gettext(
|
|
172
|
+
"Please enter the password."
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
dialogInstance.close()
|
|
178
|
+
onPassword(password)
|
|
179
|
+
resolve()
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
text: gettext("Cancel"),
|
|
184
|
+
classes: "fw-button fw-light",
|
|
185
|
+
click: () => {
|
|
186
|
+
dialogInstance.close()
|
|
187
|
+
if (typeof onCancel === "function") {
|
|
188
|
+
onCancel()
|
|
189
|
+
}
|
|
190
|
+
resolve()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
const dialog = {
|
|
196
|
+
title: gettext("Encrypted Document"),
|
|
197
|
+
id: dialogId,
|
|
198
|
+
body: body,
|
|
199
|
+
buttons: buttons,
|
|
200
|
+
canClose: true
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Use the Fidus Writer dialog system
|
|
204
|
+
dialogInstance = new Dialog(dialog)
|
|
205
|
+
dialogInstance.open()
|
|
206
|
+
|
|
207
|
+
// Set up toggle visibility button
|
|
208
|
+
setTimeout(() => {
|
|
209
|
+
const toggleBtn = document.querySelector(
|
|
210
|
+
`#${dialogId} .e2ee-toggle-visibility`
|
|
211
|
+
)
|
|
212
|
+
const input = document.getElementById("e2ee-password-input")
|
|
213
|
+
if (toggleBtn && input) {
|
|
214
|
+
toggleBtn.addEventListener("click", () => {
|
|
215
|
+
if (input.type === "password") {
|
|
216
|
+
input.type = "text"
|
|
217
|
+
toggleBtn.innerHTML =
|
|
218
|
+
'<i class="fa-solid fa-eye-slash"></i>'
|
|
219
|
+
toggleBtn.title = gettext("Hide password")
|
|
220
|
+
} else {
|
|
221
|
+
input.type = "password"
|
|
222
|
+
toggleBtn.innerHTML = '<i class="fa-solid fa-eye"></i>'
|
|
223
|
+
toggleBtn.title = gettext("Show password")
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Submit on Enter key
|
|
229
|
+
if (input) {
|
|
230
|
+
input.addEventListener("keypress", event => {
|
|
231
|
+
if (event.key === "Enter") {
|
|
232
|
+
event.preventDefault()
|
|
233
|
+
buttons[0].click()
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
input.focus()
|
|
237
|
+
}
|
|
238
|
+
}, 100)
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Show a dialog for creating a password for a new E2EE document.
|
|
244
|
+
*
|
|
245
|
+
* This dialog is shown when a user creates a new encrypted document.
|
|
246
|
+
* The user must enter and confirm a password. The password must meet
|
|
247
|
+
* the minimum requirements (12+ characters, at least one letter and
|
|
248
|
+
* one number).
|
|
249
|
+
*
|
|
250
|
+
* @param {Function} onPassword - Callback called with the entered password string
|
|
251
|
+
* @returns {Promise<void>}
|
|
252
|
+
*/
|
|
253
|
+
export function createPasswordDialog(onPassword) {
|
|
254
|
+
return new Promise(resolve => {
|
|
255
|
+
const dialogId = "e2ee-create-password"
|
|
256
|
+
|
|
257
|
+
const body = `
|
|
258
|
+
<div class="e2ee-password-dialog">
|
|
259
|
+
<p>${gettext("Set a password to encrypt this document. You will need this password to open the document in the future.")}</p>
|
|
260
|
+
<p class="e2ee-password-hint">${gettext('Tip: Use a passphrase with multiple words, e.g. "correct-horse-battery-staple". This is both secure and easy to remember.')}</p>
|
|
261
|
+
<div class="e2ee-password-field">
|
|
262
|
+
<label for="e2ee-new-password-input">${gettext("Password")}</label>
|
|
263
|
+
<input type="password" id="e2ee-new-password-input" class="e2ee-password-input"
|
|
264
|
+
autocomplete="off" data-1p-ignore data-lp-ignore data-lpignore="true" data-bwignore data-form-type="other" autofocus />
|
|
265
|
+
<button type="button" class="e2ee-toggle-visibility" title="${gettext("Show password")}">
|
|
266
|
+
<i class="fa-solid fa-eye"></i>
|
|
267
|
+
</button>
|
|
268
|
+
</div>
|
|
269
|
+
<div class="e2ee-strength-meter">
|
|
270
|
+
<div class="e2ee-strength-bar" id="e2ee-strength-bar"></div>
|
|
271
|
+
<span class="e2ee-strength-label" id="e2ee-strength-label"></span>
|
|
272
|
+
</div>
|
|
273
|
+
<div class="e2ee-password-field">
|
|
274
|
+
<label for="e2ee-confirm-password-input">${gettext("Confirm password")}</label>
|
|
275
|
+
<input type="password" id="e2ee-confirm-password-input" class="e2ee-password-input"
|
|
276
|
+
autocomplete="off" data-1p-ignore data-lp-ignore data-lpignore="true" data-bwignore data-form-type="other" />
|
|
277
|
+
</div>
|
|
278
|
+
<div class="e2ee-password-error" id="e2ee-password-error"></div>
|
|
279
|
+
</div>
|
|
280
|
+
`
|
|
281
|
+
|
|
282
|
+
const buttons = [
|
|
283
|
+
{
|
|
284
|
+
text: gettext("Create Encrypted Document"),
|
|
285
|
+
classes: "fw-button fw-dark",
|
|
286
|
+
click: () => {
|
|
287
|
+
const input = document.getElementById(
|
|
288
|
+
"e2ee-new-password-input"
|
|
289
|
+
)
|
|
290
|
+
const confirmInput = document.getElementById(
|
|
291
|
+
"e2ee-confirm-password-input"
|
|
292
|
+
)
|
|
293
|
+
const errorEl = document.getElementById(
|
|
294
|
+
"e2ee-password-error"
|
|
295
|
+
)
|
|
296
|
+
const password = input ? input.value : ""
|
|
297
|
+
const confirmPassword = confirmInput
|
|
298
|
+
? confirmInput.value
|
|
299
|
+
: ""
|
|
300
|
+
|
|
301
|
+
// Validate password
|
|
302
|
+
const validation = validatePassword(password)
|
|
303
|
+
if (!validation.valid) {
|
|
304
|
+
if (errorEl) {
|
|
305
|
+
errorEl.textContent = validation.message
|
|
306
|
+
}
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Check confirmation
|
|
311
|
+
if (password !== confirmPassword) {
|
|
312
|
+
if (errorEl) {
|
|
313
|
+
errorEl.textContent = gettext(
|
|
314
|
+
"Passwords do not match."
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
dialogInstance.close()
|
|
321
|
+
onPassword(password)
|
|
322
|
+
resolve()
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
]
|
|
326
|
+
|
|
327
|
+
const dialog = {
|
|
328
|
+
title: gettext("Set Document Password"),
|
|
329
|
+
id: dialogId,
|
|
330
|
+
body: body,
|
|
331
|
+
buttons: buttons,
|
|
332
|
+
canClose: true
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const dialogInstance = new Dialog(dialog)
|
|
336
|
+
dialogInstance.open()
|
|
337
|
+
|
|
338
|
+
setTimeout(() => {
|
|
339
|
+
// Set up toggle visibility buttons
|
|
340
|
+
const toggleBtns = document.querySelectorAll(
|
|
341
|
+
`#${dialogId} .e2ee-toggle-visibility`
|
|
342
|
+
)
|
|
343
|
+
toggleBtns.forEach(btn => {
|
|
344
|
+
btn.addEventListener("click", () => {
|
|
345
|
+
const input = btn.parentElement.querySelector("input")
|
|
346
|
+
if (input) {
|
|
347
|
+
if (input.type === "password") {
|
|
348
|
+
input.type = "text"
|
|
349
|
+
btn.innerHTML =
|
|
350
|
+
'<i class="fa-solid fa-eye-slash"></i>'
|
|
351
|
+
} else {
|
|
352
|
+
input.type = "password"
|
|
353
|
+
btn.innerHTML = '<i class="fa-solid fa-eye"></i>'
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
// Set up strength meter
|
|
360
|
+
const passwordInput = document.getElementById(
|
|
361
|
+
"e2ee-new-password-input"
|
|
362
|
+
)
|
|
363
|
+
if (passwordInput) {
|
|
364
|
+
passwordInput.addEventListener("input", () => {
|
|
365
|
+
const score = passwordStrength(passwordInput.value)
|
|
366
|
+
const info = strengthInfo(score)
|
|
367
|
+
const bar = document.getElementById("e2ee-strength-bar")
|
|
368
|
+
const label = document.getElementById("e2ee-strength-label")
|
|
369
|
+
if (bar) {
|
|
370
|
+
bar.className = `e2ee-strength-bar ${info.cssClass}`
|
|
371
|
+
bar.style.width = `${(score + 1) * 25}%`
|
|
372
|
+
}
|
|
373
|
+
if (label) {
|
|
374
|
+
label.textContent = info.label
|
|
375
|
+
label.className = `e2ee-strength-label ${info.cssClass}`
|
|
376
|
+
}
|
|
377
|
+
})
|
|
378
|
+
// Initialize strength meter
|
|
379
|
+
passwordInput.dispatchEvent(new Event("input"))
|
|
380
|
+
passwordInput.focus()
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Submit on Enter key in confirm field
|
|
384
|
+
const confirmInput = document.getElementById(
|
|
385
|
+
"e2ee-confirm-password-input"
|
|
386
|
+
)
|
|
387
|
+
if (confirmInput) {
|
|
388
|
+
confirmInput.addEventListener("keypress", event => {
|
|
389
|
+
if (event.key === "Enter") {
|
|
390
|
+
event.preventDefault()
|
|
391
|
+
buttons[0].click()
|
|
392
|
+
}
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
}, 100)
|
|
396
|
+
})
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Show a dialog for changing the password of an existing E2EE document.
|
|
401
|
+
*
|
|
402
|
+
* The user must enter their current password (to verify identity),
|
|
403
|
+
* then enter and confirm a new password.
|
|
404
|
+
*
|
|
405
|
+
* @param {Function} onPasswordChange - Callback called with
|
|
406
|
+
* {currentPassword: string, newPassword: string}
|
|
407
|
+
* @param {Object} [options] - Optional settings
|
|
408
|
+
* @param {string} [options.currentPassword] - Prefill current password field
|
|
409
|
+
* @param {string} [options.suggestedNewPassword] - Prefill new password field
|
|
410
|
+
* @param {boolean} [options.hideCurrentPassword] - Hide the current password field
|
|
411
|
+
* @param {boolean} [options.showNewPasswordPlaintext] - Show new password as plain text
|
|
412
|
+
* @param {string} [options.infoText] - Additional explanatory HTML paragraph
|
|
413
|
+
* @returns {Promise<void>}
|
|
414
|
+
*/
|
|
415
|
+
export function changePasswordDialog(onPasswordChange, options = {}) {
|
|
416
|
+
return new Promise(resolve => {
|
|
417
|
+
const dialogId = "e2ee-change-password"
|
|
418
|
+
const currentPassword = options.currentPassword || ""
|
|
419
|
+
const suggestedNewPassword = options.suggestedNewPassword || ""
|
|
420
|
+
const hasPrefilledNew = suggestedNewPassword.length > 0
|
|
421
|
+
const hideCurrentPassword = options.hideCurrentPassword || false
|
|
422
|
+
const showNewPasswordPlaintext =
|
|
423
|
+
options.showNewPasswordPlaintext || false
|
|
424
|
+
const infoText = options.infoText || ""
|
|
425
|
+
|
|
426
|
+
const currentPasswordField = hideCurrentPassword
|
|
427
|
+
? `<input type="hidden" id="e2ee-current-password-input" value="${escapeText(currentPassword)}" />`
|
|
428
|
+
: `<div class="e2ee-password-field">
|
|
429
|
+
<label for="e2ee-current-password-input">${gettext("Current password")}</label>
|
|
430
|
+
<input type="password" id="e2ee-current-password-input" class="e2ee-password-input"
|
|
431
|
+
value="${escapeText(currentPassword)}"
|
|
432
|
+
autocomplete="off" data-1p-ignore data-lp-ignore data-lpignore="true" data-bwignore data-form-type="other" autofocus />
|
|
433
|
+
</div>
|
|
434
|
+
<hr />`
|
|
435
|
+
|
|
436
|
+
const newPasswordInputType = showNewPasswordPlaintext
|
|
437
|
+
? "text"
|
|
438
|
+
: "password"
|
|
439
|
+
|
|
440
|
+
const body = `
|
|
441
|
+
<div class="e2ee-password-dialog">
|
|
442
|
+
<p>${gettext("Change the document password. After changing, you must share the new password with all collaborators.")}</p>
|
|
443
|
+
${infoText ? `<p class="e2ee-password-hint">${infoText}</p>` : ""}
|
|
444
|
+
${currentPasswordField}
|
|
445
|
+
<div class="e2ee-password-field">
|
|
446
|
+
<label for="e2ee-new-password-input">${gettext("New password")}</label>
|
|
447
|
+
<input type="${newPasswordInputType}" id="e2ee-new-password-input" class="e2ee-password-input"
|
|
448
|
+
value="${escapeText(suggestedNewPassword)}"
|
|
449
|
+
autocomplete="off" data-1p-ignore data-lp-ignore data-lpignore="true" data-bwignore data-form-type="other" ${hideCurrentPassword ? "autofocus" : ""} />
|
|
450
|
+
${
|
|
451
|
+
showNewPasswordPlaintext
|
|
452
|
+
? ""
|
|
453
|
+
: `<button type="button" class="e2ee-toggle-visibility" title="${gettext("Show password")}">
|
|
454
|
+
<i class="fa-solid fa-eye"></i>
|
|
455
|
+
</button>`
|
|
456
|
+
}
|
|
457
|
+
</div>
|
|
458
|
+
<div class="e2ee-strength-meter" style="display: ${showNewPasswordPlaintext && hasPrefilledNew ? "none" : ""}">
|
|
459
|
+
<div class="e2ee-strength-bar" id="e2ee-strength-bar"></div>
|
|
460
|
+
<span class="e2ee-strength-label" id="e2ee-strength-label"></span>
|
|
461
|
+
</div>
|
|
462
|
+
<div class="e2ee-password-field" id="e2ee-confirm-field" style="display: ${hasPrefilledNew ? "none" : ""}">
|
|
463
|
+
<label for="e2ee-confirm-password-input">${gettext("Confirm new password")}</label>
|
|
464
|
+
<input type="password" id="e2ee-confirm-password-input" class="e2ee-password-input"
|
|
465
|
+
autocomplete="off" data-1p-ignore data-lp-ignore data-lpignore="true" data-bwignore data-form-type="other" />
|
|
466
|
+
</div>
|
|
467
|
+
<div class="e2ee-password-error" id="e2ee-password-error"></div>
|
|
468
|
+
</div>
|
|
469
|
+
`
|
|
470
|
+
|
|
471
|
+
let newPasswordChanged = !hasPrefilledNew
|
|
472
|
+
|
|
473
|
+
const buttons = [
|
|
474
|
+
{
|
|
475
|
+
text: gettext("Change Password"),
|
|
476
|
+
classes: "fw-button fw-dark",
|
|
477
|
+
click: () => {
|
|
478
|
+
const currentInput = document.getElementById(
|
|
479
|
+
"e2ee-current-password-input"
|
|
480
|
+
)
|
|
481
|
+
const newInput = document.getElementById(
|
|
482
|
+
"e2ee-new-password-input"
|
|
483
|
+
)
|
|
484
|
+
const confirmInput = document.getElementById(
|
|
485
|
+
"e2ee-confirm-password-input"
|
|
486
|
+
)
|
|
487
|
+
const errorEl = document.getElementById(
|
|
488
|
+
"e2ee-password-error"
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
const currentPasswordValue = currentInput
|
|
492
|
+
? currentInput.value
|
|
493
|
+
: ""
|
|
494
|
+
const newPassword = newInput ? newInput.value : ""
|
|
495
|
+
const confirmPassword = confirmInput
|
|
496
|
+
? confirmInput.value
|
|
497
|
+
: ""
|
|
498
|
+
|
|
499
|
+
// Validate current password
|
|
500
|
+
if (currentPasswordValue.length === 0) {
|
|
501
|
+
if (errorEl) {
|
|
502
|
+
errorEl.textContent = gettext(
|
|
503
|
+
"Please enter the current password."
|
|
504
|
+
)
|
|
505
|
+
}
|
|
506
|
+
return
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Validate new password
|
|
510
|
+
const validation = validatePassword(newPassword)
|
|
511
|
+
if (!validation.valid) {
|
|
512
|
+
if (errorEl) {
|
|
513
|
+
errorEl.textContent = validation.message
|
|
514
|
+
}
|
|
515
|
+
return
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Check confirmation only if user changed the prefilled value
|
|
519
|
+
if (newPasswordChanged && newPassword !== confirmPassword) {
|
|
520
|
+
if (errorEl) {
|
|
521
|
+
errorEl.textContent = gettext(
|
|
522
|
+
"Passwords do not match."
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
return
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Check that new password is different
|
|
529
|
+
if (currentPasswordValue === newPassword) {
|
|
530
|
+
if (errorEl) {
|
|
531
|
+
errorEl.textContent = gettext(
|
|
532
|
+
"New password must be different from the current password."
|
|
533
|
+
)
|
|
534
|
+
}
|
|
535
|
+
return
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
dialogInstance.close()
|
|
539
|
+
onPasswordChange({
|
|
540
|
+
currentPassword: currentPasswordValue,
|
|
541
|
+
newPassword
|
|
542
|
+
})
|
|
543
|
+
resolve()
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
]
|
|
547
|
+
|
|
548
|
+
const dialog = {
|
|
549
|
+
title: gettext("Change Document Password"),
|
|
550
|
+
id: dialogId,
|
|
551
|
+
body: body,
|
|
552
|
+
buttons: buttons,
|
|
553
|
+
canClose: true
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const dialogInstance = new Dialog(dialog)
|
|
557
|
+
dialogInstance.open()
|
|
558
|
+
|
|
559
|
+
setTimeout(() => {
|
|
560
|
+
// Set up toggle visibility buttons (only if new password is not plaintext)
|
|
561
|
+
if (!showNewPasswordPlaintext) {
|
|
562
|
+
const toggleBtns = document.querySelectorAll(
|
|
563
|
+
`#${dialogId} .e2ee-toggle-visibility`
|
|
564
|
+
)
|
|
565
|
+
toggleBtns.forEach(btn => {
|
|
566
|
+
btn.addEventListener("click", () => {
|
|
567
|
+
const input = btn.parentElement.querySelector("input")
|
|
568
|
+
if (input) {
|
|
569
|
+
if (input.type === "password") {
|
|
570
|
+
input.type = "text"
|
|
571
|
+
btn.innerHTML =
|
|
572
|
+
'<i class="fa-solid fa-eye-slash"></i>'
|
|
573
|
+
} else {
|
|
574
|
+
input.type = "password"
|
|
575
|
+
btn.innerHTML =
|
|
576
|
+
'<i class="fa-solid fa-eye"></i>'
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
})
|
|
580
|
+
})
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Set up strength meter
|
|
584
|
+
const passwordInput = document.getElementById(
|
|
585
|
+
"e2ee-new-password-input"
|
|
586
|
+
)
|
|
587
|
+
if (
|
|
588
|
+
passwordInput &&
|
|
589
|
+
!(showNewPasswordPlaintext && hasPrefilledNew)
|
|
590
|
+
) {
|
|
591
|
+
passwordInput.addEventListener("input", () => {
|
|
592
|
+
const score = passwordStrength(passwordInput.value)
|
|
593
|
+
const info = strengthInfo(score)
|
|
594
|
+
const bar = document.getElementById("e2ee-strength-bar")
|
|
595
|
+
const label = document.getElementById("e2ee-strength-label")
|
|
596
|
+
if (bar) {
|
|
597
|
+
bar.className = `e2ee-strength-bar ${info.cssClass}`
|
|
598
|
+
bar.style.width = `${(score + 1) * 25}%`
|
|
599
|
+
}
|
|
600
|
+
if (label) {
|
|
601
|
+
label.textContent = info.label
|
|
602
|
+
label.className = `e2ee-strength-label ${info.cssClass}`
|
|
603
|
+
}
|
|
604
|
+
})
|
|
605
|
+
passwordInput.dispatchEvent(new Event("input"))
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Track changes to new password field
|
|
609
|
+
if (passwordInput && hasPrefilledNew) {
|
|
610
|
+
passwordInput.addEventListener("input", () => {
|
|
611
|
+
if (passwordInput.value !== suggestedNewPassword) {
|
|
612
|
+
newPasswordChanged = true
|
|
613
|
+
const confirmField =
|
|
614
|
+
document.getElementById("e2ee-confirm-field")
|
|
615
|
+
if (confirmField) {
|
|
616
|
+
confirmField.style.display = ""
|
|
617
|
+
}
|
|
618
|
+
// Show strength meter again if user edits the prefilled value
|
|
619
|
+
const strengthMeter = document.querySelector(
|
|
620
|
+
`#${dialogId} .e2ee-strength-meter`
|
|
621
|
+
)
|
|
622
|
+
if (strengthMeter) {
|
|
623
|
+
strengthMeter.style.display = ""
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
})
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Submit on Enter key
|
|
630
|
+
const confirmInput = document.getElementById(
|
|
631
|
+
"e2ee-confirm-password-input"
|
|
632
|
+
)
|
|
633
|
+
if (confirmInput) {
|
|
634
|
+
confirmInput.addEventListener("keypress", event => {
|
|
635
|
+
if (event.key === "Enter") {
|
|
636
|
+
event.preventDefault()
|
|
637
|
+
buttons[0].click()
|
|
638
|
+
}
|
|
639
|
+
})
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const currentInput = document.getElementById(
|
|
643
|
+
"e2ee-current-password-input"
|
|
644
|
+
)
|
|
645
|
+
if (currentInput && !hideCurrentPassword) {
|
|
646
|
+
currentInput.focus()
|
|
647
|
+
} else if (passwordInput) {
|
|
648
|
+
passwordInput.focus()
|
|
649
|
+
}
|
|
650
|
+
}, 100)
|
|
651
|
+
})
|
|
652
|
+
}
|