@manuscripts/body-editor 3.12.66 → 3.13.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 (289) hide show
  1. package/dist/cjs/components/toolbar/type-selector/styles.js +2 -1
  2. package/dist/cjs/versions.js +1 -1
  3. package/dist/es/components/toolbar/type-selector/styles.js +2 -1
  4. package/dist/es/versions.js +1 -1
  5. package/dist/types/components/toolbar/type-selector/styles.d.ts +2 -2
  6. package/dist/types/lib/footnotes.d.ts +4 -2
  7. package/dist/types/lib/utils.d.ts +5 -4
  8. package/dist/types/versions.d.ts +1 -1
  9. package/package.json +37 -36
  10. package/{dist/es/lib/statistics.js → src/__mocks__/styleMock.ts} +2 -14
  11. package/src/assets.ts +20 -0
  12. package/src/classes/collabProvider.ts +38 -0
  13. package/src/clipboard.ts +54 -0
  14. package/src/commands.ts +2109 -0
  15. package/src/components/ChangeHandlingForm.tsx +54 -0
  16. package/src/components/LanguageDropdown/index.tsx +343 -0
  17. package/src/components/affiliations/AffiliationForm.tsx +238 -0
  18. package/src/components/affiliations/AffiliationItem.tsx +151 -0
  19. package/src/components/affiliations/AffiliationList.tsx +71 -0
  20. package/src/components/affiliations/AffiliationsModal.tsx +676 -0
  21. package/src/components/affiliations/AffiliationsPanel.tsx +76 -0
  22. package/src/components/affiliations/CreateAffiliationModal.tsx +135 -0
  23. package/src/components/authors/AuthorDetailsForm.tsx +438 -0
  24. package/src/components/authors/AuthorList.tsx +81 -0
  25. package/src/components/authors/AuthorsModal.tsx +642 -0
  26. package/src/components/authors/AuthorsPanel.tsx +69 -0
  27. package/src/components/authors/CreateAuthorModal.tsx +172 -0
  28. package/src/components/authors/CreditDrawer.tsx +71 -0
  29. package/src/components/authors/DraggableAuthor.tsx +228 -0
  30. package/src/components/authors/useManageAffiliations.tsx +77 -0
  31. package/src/components/authors/useManageCredit.tsx +63 -0
  32. package/src/components/authors-affiliations/AuthorsAndAffiliationsModals.tsx +170 -0
  33. package/src/components/authors-affiliations/GenericPanel.tsx +164 -0
  34. package/src/components/authors-affiliations/ModalTabs.tsx +100 -0
  35. package/src/components/awards/AwardForm.tsx +338 -0
  36. package/src/components/awards/AwardModal.tsx +120 -0
  37. package/src/components/awards/DeleteAwardDiaolog.tsx +52 -0
  38. package/src/components/dialog/ConfirmationDialog.tsx +131 -0
  39. package/src/components/form/CreateModalStyles.tsx +37 -0
  40. package/src/components/form/FormFooter.tsx +54 -0
  41. package/src/components/form/FormPlaceholder.tsx +71 -0
  42. package/src/components/form/ModalFormActions.tsx +88 -0
  43. package/src/components/form/UnsavedLabel.tsx +49 -0
  44. package/src/components/hooks/use-debounce.ts +56 -0
  45. package/src/components/hooks/useAffiliationShowsErrorIndicator.ts +51 -0
  46. package/src/components/hooks/useAuthorShowsErrorIndicator.ts +73 -0
  47. package/src/components/keyboard-shortcuts-modal/FormattedShortcut.tsx +56 -0
  48. package/src/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.tsx +283 -0
  49. package/src/components/keyboard-shortcuts-modal/keyboard-shortcuts.ts +304 -0
  50. package/src/components/keywords/AddKeywordInline.tsx +284 -0
  51. package/src/components/keywords/DeleteKeywordDialog.tsx +52 -0
  52. package/src/components/modal-drawer/GenericDrawer.tsx +65 -0
  53. package/src/components/modal-drawer/GenericDrawerGroup.tsx +125 -0
  54. package/src/components/outline/DraggableTree.tsx +438 -0
  55. package/src/components/outline/ManuscriptOutline.tsx +127 -0
  56. package/src/components/outline/Outline.tsx +141 -0
  57. package/src/components/references/BibliographyItemSource.ts +80 -0
  58. package/src/components/references/CitationEditor.tsx +301 -0
  59. package/src/components/references/CitationViewer.tsx +62 -0
  60. package/src/components/references/ImportBibliographyForm.tsx +232 -0
  61. package/src/components/references/ImportBibliographyModal.tsx +159 -0
  62. package/src/components/references/ReferenceForm/PersonDropDown.tsx +93 -0
  63. package/src/components/references/ReferenceForm/ReferenceForm.tsx +701 -0
  64. package/src/components/references/ReferenceForm/config.ts +133 -0
  65. package/src/components/references/ReferenceForm/styled-components.ts +156 -0
  66. package/src/components/references/ReferenceLine.tsx +43 -0
  67. package/src/components/references/ReferenceSearch.tsx +163 -0
  68. package/src/components/references/ReferenceSearchResults.tsx +113 -0
  69. package/src/components/references/ReferenceSearchResultsPlaceholder.tsx +71 -0
  70. package/src/components/references/ReferenceSearchSection.tsx +124 -0
  71. package/src/components/references/ReferencesEditor.tsx +61 -0
  72. package/src/components/references/ReferencesModal.tsx +352 -0
  73. package/src/components/references/SearchInput.tsx +98 -0
  74. package/src/components/toolbar/InsertEmbedDialog.tsx +222 -0
  75. package/src/components/toolbar/InsertTableDialog.tsx +208 -0
  76. package/src/components/toolbar/ListMenuItem.tsx +203 -0
  77. package/src/components/toolbar/helpers.ts +547 -0
  78. package/src/components/toolbar/type-selector/OptionComponent.tsx +89 -0
  79. package/src/components/toolbar/type-selector/TypeSelector.tsx +157 -0
  80. package/src/components/toolbar/type-selector/styles.ts +79 -0
  81. package/src/components/views/CrossReferenceItems.tsx +199 -0
  82. package/src/components/views/DeleteEmbedDialog.tsx +83 -0
  83. package/src/components/views/DeleteFootnoteDialog.tsx +54 -0
  84. package/src/components/views/FigureDropdown.tsx +423 -0
  85. package/src/components/views/FootnotesSelector.tsx +255 -0
  86. package/src/components/views/InsertSpecialCharacter.tsx +245 -0
  87. package/src/components/views/LinkForm.tsx +220 -0
  88. package/src/components/views/TableCellContextMenu.tsx +241 -0
  89. package/src/configs/ManuscriptsEditor.ts +140 -0
  90. package/src/configs/editor-plugins.ts +119 -0
  91. package/src/configs/editor-views.ts +139 -0
  92. package/src/icons.ts +62 -0
  93. package/src/index.ts +65 -0
  94. package/src/keys/highlight.ts +65 -0
  95. package/src/keys/index.ts +33 -0
  96. package/src/keys/keyword.ts +69 -0
  97. package/src/keys/list.ts +156 -0
  98. package/src/keys/misc.ts +112 -0
  99. package/src/keys/title.ts +221 -0
  100. package/src/lib/__tests__/__fixtures__/doc.json +388 -0
  101. package/src/lib/__tests__/__snapshots__/paste.test.ts.snap +24 -0
  102. package/src/lib/__tests__/authors.test.ts +202 -0
  103. package/src/lib/__tests__/helpers.test.ts +46 -0
  104. package/src/lib/__tests__/paste.test.ts +57 -0
  105. package/src/lib/__tests__/plugins.test.ts +176 -0
  106. package/src/lib/__tests__/url.test.ts +31 -0
  107. package/src/lib/array-reducer.ts +68 -0
  108. package/src/lib/authors-and-affiliations.ts +81 -0
  109. package/src/lib/authors.ts +67 -0
  110. package/src/lib/capabilities.ts +55 -0
  111. package/src/lib/codemirror.ts +37 -0
  112. package/src/lib/comments.ts +123 -0
  113. package/src/lib/context-menu.ts +745 -0
  114. package/src/lib/copy.ts +30 -0
  115. package/src/lib/crossref.ts +90 -0
  116. package/src/lib/dnd.ts +29 -0
  117. package/src/lib/doc.ts +167 -0
  118. package/src/lib/dompurify.ts +28 -0
  119. package/src/lib/files.ts +180 -0
  120. package/src/lib/footnotes.ts +83 -0
  121. package/src/lib/get-media-type.ts +105 -0
  122. package/src/lib/helpers.ts +199 -0
  123. package/src/lib/hierarchy.ts +117 -0
  124. package/src/lib/languages.ts +37 -0
  125. package/src/lib/lists.ts +56 -0
  126. package/src/lib/math.ts +111 -0
  127. package/src/lib/media.ts +334 -0
  128. package/src/lib/navigation-utils.ts +235 -0
  129. package/src/lib/normalize.ts +70 -0
  130. package/src/lib/paste.ts +304 -0
  131. package/src/lib/placeholder.ts +32 -0
  132. package/src/lib/platform.ts +18 -0
  133. package/src/lib/plugins.ts +22 -0
  134. package/src/lib/popper.ts +180 -0
  135. package/src/lib/position-menu.ts +184 -0
  136. package/src/lib/references.ts +94 -0
  137. package/src/lib/template.ts +29 -0
  138. package/src/lib/track-changes-utils.ts +34 -0
  139. package/{dist/types/lib/statistics.d.ts → src/lib/url.ts} +11 -8
  140. package/src/lib/use-do-with-debounce.ts +39 -0
  141. package/src/lib/utils.ts +323 -0
  142. package/src/lib/view.ts +100 -0
  143. package/src/menus.tsx +684 -0
  144. package/src/node-type-icons.tsx +83 -0
  145. package/src/plugins/__tests__/__fixtures__/deleted-footnote.json +140 -0
  146. package/src/plugins/__tests__/__fixtures__/inserted-footnote.json +153 -0
  147. package/src/plugins/__tests__/footnotes.test.ts +67 -0
  148. package/src/plugins/accessibility_element.ts +219 -0
  149. package/src/plugins/add-subtitle.ts +102 -0
  150. package/src/plugins/affiliations.ts +136 -0
  151. package/src/plugins/alt-titles.ts +219 -0
  152. package/src/plugins/bibliography.ts +235 -0
  153. package/src/plugins/comments.ts +328 -0
  154. package/src/plugins/cross-references.ts +70 -0
  155. package/src/plugins/detect-inconsistency/detect-inconsistency-utils.ts +102 -0
  156. package/src/plugins/detect-inconsistency/index.ts +55 -0
  157. package/src/plugins/detect-inconsistency/validators.ts +283 -0
  158. package/src/plugins/doi.ts +66 -0
  159. package/src/plugins/editor-props.ts +39 -0
  160. package/src/plugins/elements.ts +60 -0
  161. package/src/plugins/footnotes.ts +298 -0
  162. package/src/plugins/link.ts +53 -0
  163. package/src/plugins/lock-body.ts +136 -0
  164. package/src/plugins/move-node.ts +55 -0
  165. package/src/plugins/objects.ts +125 -0
  166. package/src/plugins/paragraphs.ts +90 -0
  167. package/src/plugins/persist.ts +64 -0
  168. package/src/plugins/persistent-cursor.ts +66 -0
  169. package/src/plugins/placeholder.ts +219 -0
  170. package/src/plugins/prevent-empty.ts +50 -0
  171. package/src/plugins/search-replace/index.ts +157 -0
  172. package/src/plugins/search-replace/lib.ts +95 -0
  173. package/src/plugins/section_category.ts +212 -0
  174. package/src/plugins/section_title/autocompletion.ts +87 -0
  175. package/src/plugins/section_title/index.ts +105 -0
  176. package/src/plugins/selected-suggestion.ts +243 -0
  177. package/src/plugins/tables-cursor-fix.ts +143 -0
  178. package/src/plugins/translations.ts +263 -0
  179. package/src/rules.ts +60 -0
  180. package/src/selection.ts +112 -0
  181. package/src/testing/default-capabilities.ts +48 -0
  182. package/src/testing/default-editor-data.ts +73 -0
  183. package/src/testing/empty-editor-doc.json +62 -0
  184. package/src/testing/polyfill-dom.ts +43 -0
  185. package/src/testing/section-categories.json +161 -0
  186. package/src/testing/setup-editor.ts +55 -0
  187. package/src/tests.ts +42 -0
  188. package/src/toolbar.tsx +259 -0
  189. package/src/types/@citation-js/core.d.ts +16 -0
  190. package/src/types/@citation-js/index.d.ts +16 -0
  191. package/src/types/@citation-js/plugin-bibtex.d.ts +16 -0
  192. package/src/types/@citation-js/plugin-csl.d.ts +16 -0
  193. package/src/types/@citation-js/plugin-doi.d.ts +16 -0
  194. package/src/types/@citation-js/plugin-enw.d.ts +16 -0
  195. package/src/types/@citation-js/plugin-pubmed.d.ts +16 -0
  196. package/src/types/@citation-js/plugin-ris.d.ts +16 -0
  197. package/src/types/citeproc.d.ts +124 -0
  198. package/src/types/css.d.ts +16 -0
  199. package/src/types/json.d.ts +17 -0
  200. package/src/types/prosemirror-dev-tools.d.ts +16 -0
  201. package/src/types/prosemirror-dropcursor.d.ts +16 -0
  202. package/src/types/styled-components.d.ts +23 -0
  203. package/src/types.ts +49 -0
  204. package/src/useEditor.ts +244 -0
  205. package/src/versions.ts +3 -0
  206. package/src/views/ReactSubView.tsx +145 -0
  207. package/src/views/abstracts.ts +46 -0
  208. package/src/views/accessibility_element.ts +86 -0
  209. package/src/views/affiliations.ts +254 -0
  210. package/src/views/alt_title.ts +85 -0
  211. package/src/views/alt_titles_section.ts +97 -0
  212. package/src/views/attachment.ts +160 -0
  213. package/src/views/attachments.ts +72 -0
  214. package/src/views/author_notes.ts +96 -0
  215. package/src/views/award.ts +194 -0
  216. package/src/views/awards.ts +59 -0
  217. package/src/views/base_node_view.ts +85 -0
  218. package/src/views/bibliography_element.ts +313 -0
  219. package/src/views/block_view.ts +100 -0
  220. package/src/views/blockquote_element.ts +26 -0
  221. package/src/views/blockquote_element_editable.ts +24 -0
  222. package/src/views/box_element.ts +33 -0
  223. package/src/views/caption.ts +37 -0
  224. package/src/views/caption_title.ts +33 -0
  225. package/src/views/citation.ts +67 -0
  226. package/src/views/citation_editable.ts +312 -0
  227. package/src/views/contributors.ts +341 -0
  228. package/src/views/creators.ts +82 -0
  229. package/src/views/cross_reference.ts +74 -0
  230. package/src/views/cross_reference_editable.ts +113 -0
  231. package/src/views/editable_block.ts +141 -0
  232. package/src/views/embed.ts +345 -0
  233. package/src/views/empty.ts +26 -0
  234. package/src/views/equation.ts +47 -0
  235. package/src/views/equation_editable.ts +84 -0
  236. package/src/views/equation_element.ts +29 -0
  237. package/src/views/equation_element_editable.ts +21 -0
  238. package/src/views/figure.ts +84 -0
  239. package/src/views/figure_editable.ts +361 -0
  240. package/src/views/figure_element.ts +197 -0
  241. package/src/views/figure_element_editable.ts +21 -0
  242. package/src/views/footnote.ts +236 -0
  243. package/src/views/footnotes_element.ts +54 -0
  244. package/src/views/general_table_footnote.ts +140 -0
  245. package/src/views/hero_image.ts +76 -0
  246. package/src/views/hero_image_editable.ts +21 -0
  247. package/src/views/image_element.ts +373 -0
  248. package/src/views/image_element_editable.ts +21 -0
  249. package/src/views/inline_equation.ts +47 -0
  250. package/src/views/inline_equation_editable.ts +70 -0
  251. package/src/views/inline_footnote.ts +264 -0
  252. package/src/views/inline_footnote_editable.ts +20 -0
  253. package/src/views/keyword.ts +127 -0
  254. package/src/views/keyword_group.ts +109 -0
  255. package/src/views/link.ts +27 -0
  256. package/src/views/link_editable.ts +169 -0
  257. package/src/views/list.ts +46 -0
  258. package/src/views/list_item.ts +26 -0
  259. package/src/views/paragraph.ts +26 -0
  260. package/src/views/paragraph_editable.ts +21 -0
  261. package/src/views/placeholder.ts +35 -0
  262. package/src/views/placeholder_element.ts +38 -0
  263. package/src/views/placeholder_element_editable.ts +21 -0
  264. package/src/views/pullquote_element.ts +106 -0
  265. package/src/views/pullquote_element_editable.ts +24 -0
  266. package/src/views/quote_image_editable.ts +84 -0
  267. package/src/views/section.ts +64 -0
  268. package/src/views/section_label.ts +20 -0
  269. package/src/views/section_title.ts +87 -0
  270. package/src/views/section_title_editable.ts +21 -0
  271. package/src/views/subtitle.ts +42 -0
  272. package/src/views/subtitle_editable.ts +20 -0
  273. package/src/views/subtitles.ts +34 -0
  274. package/src/views/subtitles_editable.ts +21 -0
  275. package/src/views/supplement.ts +236 -0
  276. package/src/views/supplement_editable.ts +20 -0
  277. package/src/views/supplements.ts +65 -0
  278. package/src/views/supplements_editable.ts +21 -0
  279. package/src/views/table_cell.ts +150 -0
  280. package/src/views/table_element.ts +33 -0
  281. package/src/views/table_element_editable.ts +21 -0
  282. package/src/views/table_element_footer.ts +41 -0
  283. package/src/views/title.ts +46 -0
  284. package/src/views/title_editable.ts +20 -0
  285. package/src/views/trans_graphical_abstract.ts +55 -0
  286. package/src/views/trans_graphical_abstract_editable.ts +21 -0
  287. package/src/views/translated_abstract.ts +53 -0
  288. package/src/views/translated_abstract_editable.ts +21 -0
  289. package/dist/cjs/lib/statistics.js +0 -38
@@ -0,0 +1,57 @@
1
+ /*!
2
+ * © 2019 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { schema } from '@manuscripts/transform'
18
+ import { Slice } from 'prosemirror-model'
19
+
20
+ import { transformPasted } from '../paste'
21
+
22
+ test('transformPasted handler', () => {
23
+ const slice = Slice.fromJSON(schema, {
24
+ content: [
25
+ {
26
+ type: 'paragraph',
27
+ attrs: {},
28
+ content: [{ type: 'hard_break' }],
29
+ },
30
+ {
31
+ type: 'paragraph',
32
+ attrs: {
33
+ id: 'p-1',
34
+ },
35
+ content: [
36
+ {
37
+ type: 'text',
38
+ text: 'These rhythmic patterns then sum together to create the signals that muscles need to carry out the movements.',
39
+ },
40
+ ],
41
+ },
42
+ ],
43
+ openStart: 1,
44
+ openEnd: 1,
45
+ })
46
+
47
+ expect(slice.content.childCount).toBe(2)
48
+ expect(slice.content.child(1).attrs.id).toBe('p-1')
49
+
50
+ const result = transformPasted(slice)
51
+
52
+ expect(result.content.childCount).toBe(1)
53
+ expect(result.content.size).toBe(111)
54
+ expect(slice.content.child(0).attrs.id).toBeNull()
55
+
56
+ expect(result).toMatchSnapshot('transform-pasted')
57
+ })
@@ -0,0 +1,176 @@
1
+ /*!
2
+ * © 2019 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import {
17
+ ActualManuscriptNode,
18
+ schema,
19
+ SectionCategory,
20
+ UserProfile,
21
+ } from '@manuscripts/transform'
22
+ import { createMemoryHistory } from 'history'
23
+ import { EditorState } from 'prosemirror-state'
24
+ import { EditorView } from 'prosemirror-view'
25
+
26
+ import plugins from '../../configs/editor-plugins'
27
+ import { EditorProps } from '../../configs/ManuscriptsEditor'
28
+ import { defaultCapabilities } from '../../testing/default-capabilities'
29
+ import sectionCategories from '../../testing/section-categories.json'
30
+ import { PopperManager } from '../popper'
31
+ import { getMatchingDescendant } from '../utils'
32
+ import jsonDoc from './__fixtures__/doc.json'
33
+ const userProfile: UserProfile = {
34
+ _id: 'MPUserProfile:1',
35
+ userID: 'MPUser:1',
36
+ }
37
+
38
+ const history = createMemoryHistory()
39
+
40
+ const buildProps = (doc: ActualManuscriptNode): EditorProps => ({
41
+ doc,
42
+ locale: 'en-US',
43
+ popper: new PopperManager(),
44
+ projectID: '',
45
+ getCurrentUser: () => userProfile,
46
+ history,
47
+ setComment: () => undefined,
48
+ setSelectedComment: () => undefined,
49
+ setEditorSelectedSuggestion: () => undefined,
50
+ theme: {},
51
+ getFiles: () => [],
52
+ fileManagement: {
53
+ download: () => undefined, // eslint-disable-line @typescript-eslint/no-empty-function
54
+ //@ts-ignore
55
+ upload: () => undefined, // eslint-disable-line @typescript-eslint/no-empty-function
56
+ },
57
+ getCapabilities: () => defaultCapabilities,
58
+ cslProps: {
59
+ style: '',
60
+ locale: '',
61
+ },
62
+ sectionCategories: new Map(
63
+ sectionCategories.map((category: SectionCategory) => [
64
+ category.id,
65
+ category,
66
+ ])
67
+ ),
68
+ fetchOEmbedHtml: async () => undefined,
69
+ })
70
+
71
+ describe('editor view', () => {
72
+ const doc = schema.nodeFromJSON(jsonDoc) as ActualManuscriptNode
73
+ beforeAll(() => {
74
+ const root = document.createElement('div')
75
+ root.id = 'root'
76
+ window.document.body.appendChild(root)
77
+ })
78
+
79
+ test('loads without plugins', () => {
80
+ const state = EditorState.create({
81
+ doc,
82
+ schema,
83
+ })
84
+
85
+ const view = new EditorView(null, { state })
86
+
87
+ view.dispatch(view.state.tr.setMeta('update', true)) // trigger plugins
88
+
89
+ const emptySection = view.state.doc.child(2).child(0)
90
+ expect(emptySection.attrs.id).toBe('empty-section-1')
91
+ expect(emptySection.childCount).toBe(1)
92
+
93
+ const sectionWithEmptyParagraphs = view.state.doc.child(2).child(3)
94
+ expect(sectionWithEmptyParagraphs.attrs.id).toBe(
95
+ 'section-with-empty-paragraphs-1'
96
+ )
97
+ expect(sectionWithEmptyParagraphs.childCount).toBe(3)
98
+
99
+ const sectionWithList = view.state.doc.child(2).child(6)
100
+ expect(sectionWithList.attrs.id).toBe('section-with-list')
101
+ expect(sectionWithList.childCount).toBe(2)
102
+ })
103
+
104
+ test('loads with plugins', () => {
105
+ const state = EditorState.create({
106
+ doc,
107
+ schema,
108
+ plugins: plugins(buildProps(doc)),
109
+ })
110
+
111
+ const view = new EditorView(null, { state })
112
+
113
+ view.dispatch(view.state.tr.setMeta('INIT', true)) // trigger plugins
114
+
115
+ const sectionWithEmptyParagraphs = view.state.doc.child(2).child(3)
116
+ expect(sectionWithEmptyParagraphs.attrs.id).toBe(
117
+ 'section-with-empty-paragraphs-1'
118
+ )
119
+ expect(sectionWithEmptyParagraphs.childCount).toBe(2)
120
+ expect(sectionWithEmptyParagraphs.content.child(0).type).toBe(
121
+ schema.nodes.section_title
122
+ )
123
+ expect(sectionWithEmptyParagraphs.content.child(1).type).toBe(
124
+ schema.nodes.paragraph
125
+ )
126
+ expect(sectionWithEmptyParagraphs.content.child(1).textContent).toBe('')
127
+
128
+ const sectionWithList = view.state.doc.child(2).child(6)
129
+ expect(sectionWithList.attrs.id).toBe('section-with-list')
130
+ expect(sectionWithList.childCount).toBe(2)
131
+ expect(sectionWithList.content.child(0).type).toBe(
132
+ sectionWithList.type.schema.nodes.section_title
133
+ )
134
+
135
+ const orderedList = sectionWithList.content.child(1)
136
+ expect(orderedList.type).toBe(sectionWithList.type.schema.nodes.list)
137
+ expect(orderedList.textContent).toBe('Test ')
138
+
139
+ const inlineEquation = getMatchingDescendant(
140
+ orderedList,
141
+ (node) => node.type === node.type.schema.nodes.inline_equation
142
+ )
143
+
144
+ expect(inlineEquation).not.toBeUndefined()
145
+
146
+ const sectionWithFigure = view.state.doc.child(2).child(7)
147
+ expect(sectionWithFigure.attrs.id).toBe('section-with-figure')
148
+ expect(sectionWithFigure.childCount).toBe(2)
149
+ expect(sectionWithFigure.content.child(0).type).toBe(
150
+ schema.nodes.section_title
151
+ )
152
+
153
+ const figureElement = sectionWithFigure.content.child(1)
154
+ expect(figureElement.childCount).toBe(5)
155
+ expect(figureElement.type).toBe(schema.nodes.figure_element)
156
+ expect(figureElement.content.child(0).type).toBe(schema.nodes.figure)
157
+ expect(figureElement.content.child(1).type).toBe(schema.nodes.caption)
158
+
159
+ const sectionWithTable = view.state.doc.child(2).child(8)
160
+ expect(sectionWithTable.attrs.id).toBe('section-with-table')
161
+ expect(sectionWithTable.childCount).toBe(2)
162
+ expect(sectionWithTable.content.child(0).type).toBe(
163
+ sectionWithTable.type.schema.nodes.section_title
164
+ )
165
+
166
+ const tableElement = sectionWithTable.content.child(1)
167
+ expect(tableElement.childCount).toBe(5)
168
+ expect(tableElement.type).toBe(tableElement.type.schema.nodes.table_element)
169
+ expect(tableElement.content.child(0).type).toBe(
170
+ tableElement.type.schema.nodes.caption_title
171
+ )
172
+ expect(tableElement.content.child(1).type).toBe(
173
+ tableElement.type.schema.nodes.table
174
+ )
175
+ })
176
+ })
@@ -0,0 +1,31 @@
1
+ /*!
2
+ * © 2019 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { allowedHref } from '../url'
18
+
19
+ describe('url', () => {
20
+ test('allows valid protocols', () => {
21
+ expect(allowedHref('http://example.com')).toBe(true)
22
+ expect(allowedHref('https://example.com')).toBe(true)
23
+ expect(allowedHref('ftp://example.com')).toBe(true)
24
+ expect(allowedHref(' http://example.com')).toBe(true) // leading space
25
+ })
26
+
27
+ test('rejects invalid protocols', () => {
28
+ expect(allowedHref('javascript:alert("hi")')).toBe(false)
29
+ expect(allowedHref('httpp://example.com')).toBe(false)
30
+ })
31
+ })
@@ -0,0 +1,68 @@
1
+ /*!
2
+ * © 2024 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { Attrs } from 'prosemirror-model'
18
+
19
+ export type Update<T> = {
20
+ type: 'update'
21
+ items: T[]
22
+ }
23
+
24
+ export type Delete<T> = {
25
+ type: 'delete'
26
+ item: T
27
+ }
28
+
29
+ export type Set<T> = {
30
+ type: 'set'
31
+ state: T[]
32
+ }
33
+
34
+ export type Action<T> = Update<T> | Delete<T> | Set<T>
35
+
36
+ export const arrayReducer = <T>(
37
+ isEqual: (a: T, b: T) => boolean = (a, b) => a === b
38
+ ) => {
39
+ return (state: T[], action: Action<T>): T[] => {
40
+ const handleUpdate = (state: T[], items: T[]): T[] => {
41
+ const copy = [...state]
42
+ items.forEach((item) => {
43
+ const index = copy.findIndex((i) => isEqual(item, i))
44
+ if (index >= 0) {
45
+ copy[index] = item
46
+ } else {
47
+ copy.push(item)
48
+ }
49
+ })
50
+ return copy
51
+ }
52
+ const handleDelete = (state: T[], item: T): T[] => {
53
+ return state.filter((i) => !isEqual(item, i))
54
+ }
55
+
56
+ switch (action.type) {
57
+ case 'update':
58
+ return handleUpdate(state, action.items)
59
+ case 'delete':
60
+ return handleDelete(state, action.item)
61
+ case 'set':
62
+ return action.state
63
+ }
64
+ }
65
+ }
66
+
67
+ export const attrsReducer = <T extends Attrs>() =>
68
+ arrayReducer<T>((a, b) => a.id === b.id)
@@ -0,0 +1,81 @@
1
+ /*!
2
+ * © 2026 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ generateNodeID,
19
+ ManuscriptEditorView,
20
+ ManuscriptNodeType,
21
+ schema,
22
+ } from '@manuscripts/transform'
23
+ import { Attrs } from 'prosemirror-model'
24
+
25
+ import { AffiliationAttrs, ContributorAttrs } from './authors'
26
+ import { findInsertionPosition } from './utils'
27
+ import { findChildByType, updateNodeAttrs } from './view'
28
+
29
+ function insertNode(
30
+ parentType: ManuscriptNodeType,
31
+ childType: ManuscriptNodeType
32
+ ) {
33
+ return (view: ManuscriptEditorView, attrs: Attrs) => {
34
+ const parent = findChildByType(view, parentType)
35
+ let tr = view.state.tr
36
+
37
+ if (parent) {
38
+ tr = tr.insert(parent.pos + 1, childType.create(attrs))
39
+ } else {
40
+ const insertPos = findInsertionPosition(parentType, view.state.doc)
41
+ const wrapper = parentType.create({
42
+ id: generateNodeID(parentType),
43
+ })
44
+ tr = tr
45
+ .insert(insertPos, wrapper)
46
+ .insert(insertPos + 1, childType.create(attrs))
47
+ }
48
+
49
+ view.dispatch(tr)
50
+ }
51
+ }
52
+
53
+ function upsertNode<T extends Attrs>(
54
+ nodeType: ManuscriptNodeType,
55
+ insertFn: (view: ManuscriptEditorView, attrs: T) => void
56
+ ) {
57
+ return (view: ManuscriptEditorView, attrs: T) => {
58
+ if (!updateNodeAttrs(view, nodeType, attrs)) {
59
+ insertFn(view, attrs)
60
+ }
61
+ }
62
+ }
63
+
64
+ const insertAuthorNode = insertNode(
65
+ schema.nodes.contributors,
66
+ schema.nodes.contributor
67
+ )
68
+ const insertAffiliationNode = insertNode(
69
+ schema.nodes.affiliations,
70
+ schema.nodes.affiliation
71
+ )
72
+
73
+ export const upsertAuthor = upsertNode<ContributorAttrs>(
74
+ schema.nodes.contributor,
75
+ insertAuthorNode
76
+ )
77
+
78
+ export const upsertAffiliation = upsertNode<AffiliationAttrs>(
79
+ schema.nodes.affiliation,
80
+ insertAffiliationNode
81
+ )
@@ -0,0 +1,67 @@
1
+ /*!
2
+ * © 2019 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { AffiliationNode, ContributorNode } from '@manuscripts/transform'
18
+
19
+ import { TrackableAttributes } from '../types'
20
+
21
+ export type AffiliationAttrs = TrackableAttributes<AffiliationNode>
22
+ export type ContributorAttrs = TrackableAttributes<ContributorNode>
23
+
24
+ export const affiliationLabel = (affiliation: AffiliationAttrs) => {
25
+ const institution = affiliation.institution
26
+ if (!institution) {
27
+ return '(unknown institution)'
28
+ }
29
+ const department = affiliation.department
30
+ return department ? `${institution} (${department})`.trim() : institution
31
+ }
32
+
33
+ export const affiliationName = (affiliation: AffiliationAttrs) => {
34
+ return [
35
+ affiliation.department,
36
+ affiliation.institution,
37
+ affiliation.addressLine1,
38
+ affiliation.city,
39
+ affiliation.county,
40
+ affiliation.country,
41
+ affiliation.postCode,
42
+ ]
43
+ .filter(Boolean)
44
+ .join(', ')
45
+ }
46
+
47
+ export const authorLabel = (author: ContributorAttrs) => {
48
+ const parts = [
49
+ author.prefix,
50
+ author.family ? initials(author) : author.given,
51
+ author.family,
52
+ author.suffix,
53
+ ].filter(Boolean)
54
+ return parts.length ? parts.join(' ') : 'Unknown Author'
55
+ }
56
+
57
+ export const initials = (contributor: ContributorAttrs): string =>
58
+ contributor.given
59
+ ? contributor.given
60
+ .trim()
61
+ .split(' ')
62
+ .map((part) => part.substring(0, 1).toUpperCase() + '.')
63
+ .join('')
64
+ : ''
65
+
66
+ export const authorComparator = (a: ContributorAttrs, b: ContributorAttrs) =>
67
+ a.priority - b.priority
@@ -0,0 +1,55 @@
1
+ /*!
2
+ * © 2025 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ export type Capabilities = {
18
+ /* track changes */
19
+ handleSuggestion: boolean
20
+ editWithoutTracking: boolean
21
+ rejectOwnSuggestion: boolean
22
+
23
+ /* comments */
24
+ handleOwnComments: boolean
25
+ resolveOwnComment: boolean
26
+ handleOthersComments: boolean
27
+ resolveOthersComment: boolean
28
+ createComment: boolean
29
+
30
+ /* file handling */
31
+ downloadFiles: boolean
32
+ changeDesignation: boolean
33
+ moveFile: boolean
34
+ replaceFile: boolean
35
+ uploadFile: boolean
36
+ detachFile: boolean
37
+ setMainManuscript: boolean
38
+
39
+ /* editor */
40
+ formatArticle: boolean
41
+ editArticle: boolean
42
+ editMetadata: boolean
43
+ editCitationsAndRefs: boolean
44
+ seeEditorToolbar: boolean
45
+ seeReferencesButtons: boolean
46
+ }
47
+
48
+ export enum Actions {
49
+ // proceed = 'proceed',
50
+ updateAttachment = 'update-attachment',
51
+ updateDueDate = 'update-due-date',
52
+ addNote = 'add-note',
53
+ setMainManuscript = 'set-main-manuscript',
54
+ editWithoutTracking = 'edit-without-tracking',
55
+ }
@@ -0,0 +1,37 @@
1
+ /*!
2
+ * © 2019 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import 'codemirror/addon/display/placeholder'
18
+ import 'codemirror/lib/codemirror.css'
19
+
20
+ import CodeMirror from 'codemirror'
21
+
22
+ type CreateEditor = (
23
+ options: CodeMirror.EditorConfiguration
24
+ ) => Promise<CodeMirror.Editor>
25
+
26
+ export const createEditor: CreateEditor = async (options) => {
27
+ return CodeMirror(
28
+ () => {
29
+ // no-op
30
+ },
31
+ {
32
+ ...options,
33
+ lineNumbers: true,
34
+ lineWrapping: true,
35
+ }
36
+ )
37
+ }
@@ -0,0 +1,123 @@
1
+ /*!
2
+ * © 2024 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import {
17
+ CommentNode,
18
+ HighlightMarkerNode,
19
+ ManuscriptNode,
20
+ } from '@manuscripts/transform'
21
+ import { NodeWithPos } from 'prosemirror-utils'
22
+ import { EditorView } from 'prosemirror-view'
23
+
24
+ import { addNodeComment } from '../commands'
25
+
26
+ export type CommentAttrs = CommentNode['attrs']
27
+ export type HighlightMarkerAttrs = HighlightMarkerNode['attrs']
28
+
29
+ export type Comment = NodeComment | InlineComment
30
+
31
+ export type NodeComment = {
32
+ key: CommentKey
33
+ node: CommentNode
34
+ pos: number
35
+ target: NodeWithPos
36
+ }
37
+
38
+ export type InlineComment = {
39
+ key: CommentKey
40
+ node: CommentNode
41
+ pos: number
42
+ target: NodeWithPos
43
+ range: CommentRange
44
+ }
45
+
46
+ export type CommentRange = {
47
+ pos: number
48
+ size: number
49
+ }
50
+
51
+ export type CommentKey = string
52
+
53
+ export type CommentSelection = {
54
+ key: CommentKey
55
+ id: string
56
+ isNew: boolean
57
+ }
58
+
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ export const isNodeComment = (c: any): c is NodeComment => !c.range
61
+
62
+ export const isReply = (comment: Comment) =>
63
+ comment.node.attrs.target?.startsWith('MPComment')
64
+
65
+ export const getCommentKey = (
66
+ comment: CommentAttrs,
67
+ range: CommentRange | undefined,
68
+ target: ManuscriptNode
69
+ ): CommentKey => {
70
+ if (!range) {
71
+ return target.attrs.id
72
+ } else if (!range.size) {
73
+ return `${target.attrs.id}-${range.pos}`
74
+ } else {
75
+ return comment.id
76
+ }
77
+ }
78
+
79
+ export const getCommentRange = (comment: CommentAttrs) => {
80
+ if (!comment.selector) {
81
+ return
82
+ }
83
+ return {
84
+ pos: comment.selector.from,
85
+ size: comment.selector.to - comment.selector.from,
86
+ }
87
+ }
88
+
89
+ export const createCommentMarker = (
90
+ tagName: string,
91
+ key: CommentKey,
92
+ count?: number
93
+ ) => {
94
+ const element = document.createElement(tagName)
95
+ element.id = getMarkerID(key)
96
+ element.dataset.key = key
97
+ element.classList.add('comment-marker')
98
+ element.tabIndex = 0
99
+
100
+ if (count && count > 1) {
101
+ element.dataset.count = String(count)
102
+ }
103
+
104
+ element.innerHTML = `
105
+ <svg width="16" height="13" viewBox="0 0 16 13" fill="none" xmlns="http://www.w3.org/2000/svg">
106
+ <path d="M4.0625 2.9375V7.3125L1.4375 11.6875H12.8125C13.7794 11.6875 14.5625 10.9044 14.5625 9.9375V2.9375C14.5625 1.97062 13.7794 1.1875 12.8125 1.1875H5.8125C4.84562 1.1875 4.0625 1.97062 4.0625 2.9375Z"
107
+ fill="#FFFCDB" stroke="#FFBD26" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
108
+ <path d="M6.6875 4.6875H11.9375" stroke="#FFBD26" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
109
+ <path d="M6.6875 8.1875H9.3125" stroke="#FFBD26" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
110
+ </svg>
111
+ `
112
+ return element
113
+ }
114
+
115
+ const getMarkerID = (id: string) => {
116
+ return `${id}-comment-marker`
117
+ }
118
+
119
+ export const handleComment = (node: ManuscriptNode, view: EditorView): void => {
120
+ const { state } = view
121
+
122
+ addNodeComment(node, state, view.dispatch)
123
+ }