@manuscripts/transform 1.5.4-LEAN-3030 → 1.5.4-LEAN-2995

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 (55) hide show
  1. package/dist/cjs/jats/importer/jats-body-dom-parser.js +18 -23
  2. package/dist/cjs/jats/importer/jats-body-transformations.js +3 -3
  3. package/dist/cjs/jats/jats-exporter.js +6 -11
  4. package/dist/cjs/lib/utils.js +1 -10
  5. package/dist/cjs/schema/index.js +12 -16
  6. package/dist/cjs/schema/nodes/affiliations_section.js +15 -0
  7. package/dist/cjs/schema/nodes/contributors_section.js +15 -0
  8. package/dist/cjs/schema/nodes/keyword.js +1 -0
  9. package/dist/cjs/schema/nodes/keywords_element.js +1 -1
  10. package/dist/cjs/schema/nodes/{keyword_group.js → keywords_group.js} +6 -6
  11. package/dist/cjs/schema/nodes/{keywords.js → keywords_section.js} +7 -7
  12. package/dist/cjs/schema/nodes/manuscript.js +1 -1
  13. package/dist/cjs/transformer/decode.js +113 -108
  14. package/dist/cjs/transformer/encode.js +37 -15
  15. package/dist/cjs/transformer/node-names.js +1 -1
  16. package/dist/cjs/transformer/node-title.js +1 -1
  17. package/dist/cjs/transformer/node-types.js +4 -7
  18. package/dist/cjs/transformer/section-category.js +8 -35
  19. package/dist/es/jats/importer/jats-body-dom-parser.js +18 -23
  20. package/dist/es/jats/importer/jats-body-transformations.js +3 -3
  21. package/dist/es/jats/jats-exporter.js +6 -11
  22. package/dist/es/lib/utils.js +0 -8
  23. package/dist/es/schema/index.js +12 -16
  24. package/dist/es/schema/nodes/{affiliations.js → affiliations_section.js} +3 -3
  25. package/dist/es/schema/nodes/{contributors.js → contributors_section.js} +3 -3
  26. package/dist/es/schema/nodes/keyword.js +1 -0
  27. package/dist/es/schema/nodes/keywords_element.js +1 -1
  28. package/dist/es/schema/nodes/{keyword_group.js → keywords_group.js} +4 -4
  29. package/dist/es/schema/nodes/{keywords.js → keywords_section.js} +5 -5
  30. package/dist/es/schema/nodes/manuscript.js +1 -1
  31. package/dist/es/transformer/decode.js +114 -109
  32. package/dist/es/transformer/encode.js +37 -15
  33. package/dist/es/transformer/node-names.js +1 -1
  34. package/dist/es/transformer/node-title.js +1 -1
  35. package/dist/es/transformer/node-types.js +4 -7
  36. package/dist/es/transformer/section-category.js +7 -33
  37. package/dist/types/lib/utils.d.ts +0 -1
  38. package/dist/types/schema/index.d.ts +3 -3
  39. package/dist/types/schema/nodes/affiliations_section.d.ts +11 -0
  40. package/dist/types/schema/nodes/contributors_section.d.ts +11 -0
  41. package/dist/types/schema/nodes/keyword.d.ts +1 -0
  42. package/dist/types/schema/nodes/{keyword_group.d.ts → keywords_group.d.ts} +3 -3
  43. package/dist/types/schema/nodes/{keywords.d.ts → keywords_section.d.ts} +3 -3
  44. package/dist/types/schema/types.d.ts +1 -1
  45. package/dist/types/transformer/decode.d.ts +6 -6
  46. package/dist/types/transformer/encode.d.ts +1 -1
  47. package/dist/types/transformer/section-category.d.ts +0 -1
  48. package/package.json +1 -1
  49. package/dist/cjs/schema/nodes/affiliations.js +0 -15
  50. package/dist/cjs/schema/nodes/contributors.js +0 -15
  51. package/dist/cjs/schema/nodes/core_section.js +0 -27
  52. package/dist/es/schema/nodes/core_section.js +0 -24
  53. package/dist/types/schema/nodes/affiliations.d.ts +0 -11
  54. package/dist/types/schema/nodes/contributors.d.ts +0 -11
  55. package/dist/types/schema/nodes/core_section.d.ts +0 -17
@@ -424,11 +424,9 @@ export class JATSExporter {
424
424
  }
425
425
  };
426
426
  this.buildBody = (fragment) => {
427
+ const content = this.serializeFragment(fragment);
427
428
  const body = this.document.createElement('body');
428
- fragment.forEach((cFragment) => {
429
- const serializedNode = this.serializeNode(cFragment);
430
- body.append(...serializedNode.childNodes);
431
- });
429
+ body.appendChild(content);
432
430
  this.fixBody(body, fragment);
433
431
  return body;
434
432
  };
@@ -595,8 +593,8 @@ export class JATSExporter {
595
593
  const getModel = (id) => id ? this.modelMap.get(id) : undefined;
596
594
  const nodes = {
597
595
  title: () => '',
598
- affiliations: () => '',
599
- contributors: () => '',
596
+ affiliations_section: () => '',
597
+ contributors_section: () => '',
600
598
  table_element_footer: () => ['table-wrap-foot', 0],
601
599
  contributor: () => '',
602
600
  affiliation: () => '',
@@ -605,10 +603,7 @@ export class JATSExporter {
605
603
  bibliography_element: () => '',
606
604
  bibliography_item: () => '',
607
605
  comment_list: () => '',
608
- keyword_group: () => '',
609
- body: () => ['body', 0],
610
- abstracts: () => ['abstract', 0],
611
- backmatter: () => ['backmatter', 0],
606
+ keywords_group: () => '',
612
607
  bibliography_section: (node) => [
613
608
  'ref-list',
614
609
  { id: normalizeID(node.attrs.id) },
@@ -787,7 +782,7 @@ export class JATSExporter {
787
782
  },
788
783
  keyword: () => '',
789
784
  keywords_element: () => '',
790
- keywords: () => '',
785
+ keywords_section: () => '',
791
786
  link: (node) => {
792
787
  const text = node.textContent;
793
788
  if (!text) {
@@ -84,11 +84,3 @@ export const getTrimmedTextContent = (node, querySelector) => {
84
84
  }
85
85
  return (_b = (_a = node.querySelector(querySelector)) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim();
86
86
  };
87
- export const groupBy = (list, getKey) => list.reduce((previous, currentItem) => {
88
- const group = getKey(currentItem);
89
- if (!previous[group]) {
90
- previous[group] = [];
91
- }
92
- previous[group].push(currentItem);
93
- return previous;
94
- }, {});
@@ -16,7 +16,7 @@
16
16
  import { Schema } from 'prosemirror-model';
17
17
  import { bold, code, italic, smallcaps, strikethrough, styled, subscript, superscript, tracked_delete, tracked_insert, underline, } from './marks';
18
18
  import { affiliation } from './nodes/affiliation';
19
- import { affiliations } from './nodes/affiliations';
19
+ import { affiliationsSection } from './nodes/affiliations_section';
20
20
  import { attribution } from './nodes/attribution';
21
21
  import { bibliographyElement } from './nodes/bibliography_element';
22
22
  import { bibliographyItem } from './nodes/bibliography_item';
@@ -28,8 +28,7 @@ import { citation } from './nodes/citation';
28
28
  import { comment } from './nodes/comment';
29
29
  import { commentList } from './nodes/comment_list';
30
30
  import { contributor } from './nodes/contributor';
31
- import { contributors } from './nodes/contributors';
32
- import { coreSection } from './nodes/core_section';
31
+ import { contributorsSection } from './nodes/contributors_section';
33
32
  import { crossReference } from './nodes/cross_reference';
34
33
  import { doc } from './nodes/doc';
35
34
  import { equation } from './nodes/equation';
@@ -46,8 +45,9 @@ import { highlightMarker } from './nodes/highlight_marker';
46
45
  import { inlineEquation } from './nodes/inline_equation';
47
46
  import { inlineFootnote } from './nodes/inline_footnote';
48
47
  import { keyword } from './nodes/keyword';
49
- import { keywordGroup } from './nodes/keyword_group';
50
- import { keywords } from './nodes/keywords';
48
+ import { keywordsElement } from './nodes/keywords_element';
49
+ import { keywordsGroup } from './nodes/keywords_group';
50
+ import { keywordsSection } from './nodes/keywords_section';
51
51
  import { link } from './nodes/link';
52
52
  import { bulletList, listItem, orderedList } from './nodes/list';
53
53
  import { listing } from './nodes/listing';
@@ -71,7 +71,6 @@ import { text } from './nodes/text';
71
71
  import { title } from './nodes/title';
72
72
  import { tocElement } from './nodes/toc_element';
73
73
  import { tocSection } from './nodes/toc_section';
74
- import { keywordsElement } from "./nodes/keywords_element";
75
74
  export * from './groups';
76
75
  export * from './types';
77
76
  export * from './nodes/comment';
@@ -101,7 +100,7 @@ export * from './nodes/inline_equation';
101
100
  export * from './nodes/inline_footnote';
102
101
  export * from './nodes/keyword';
103
102
  export * from './nodes/keywords_element';
104
- export * from './nodes/keywords';
103
+ export * from './nodes/keywords_section';
105
104
  export * from './nodes/link';
106
105
  export * from './nodes/list';
107
106
  export * from './nodes/listing';
@@ -126,8 +125,8 @@ export * from './nodes/meta_section';
126
125
  export * from './nodes/contributor';
127
126
  export * from './nodes/table_element_footer';
128
127
  export * from './nodes/title';
129
- export * from './nodes/affiliations';
130
- export * from './nodes/contributors';
128
+ export * from './nodes/affiliations_section';
129
+ export * from './nodes/contributors_section';
131
130
  export const schema = new Schema({
132
131
  marks: {
133
132
  bold,
@@ -154,9 +153,6 @@ export const schema = new Schema({
154
153
  caption,
155
154
  caption_title: captionTitle,
156
155
  citation,
157
- abstracts: coreSection,
158
- body: coreSection,
159
- backmatter: coreSection,
160
156
  cross_reference: crossReference,
161
157
  doc,
162
158
  equation,
@@ -174,8 +170,8 @@ export const schema = new Schema({
174
170
  inline_footnote: inlineFootnote,
175
171
  keyword,
176
172
  keywords_element: keywordsElement,
177
- keywords: keywords,
178
- keyword_group: keywordGroup,
173
+ keywords_section: keywordsSection,
174
+ keywords_group: keywordsGroup,
179
175
  link,
180
176
  list_item: listItem,
181
177
  listing,
@@ -206,7 +202,7 @@ export const schema = new Schema({
206
202
  contributor: contributor,
207
203
  table_element_footer: tableElementFooter,
208
204
  title: title,
209
- affiliations: affiliations,
210
- contributors: contributors,
205
+ affiliations_section: affiliationsSection,
206
+ contributors_section: contributorsSection,
211
207
  },
212
208
  });
@@ -1,11 +1,11 @@
1
- export const affiliations = {
1
+ export const affiliationsSection = {
2
2
  content: 'section_title? affiliation*',
3
3
  attrs: {
4
4
  id: { default: '' },
5
5
  dataTracked: { default: null },
6
6
  },
7
- group: 'block',
7
+ group: 'block sections',
8
8
  selectable: false,
9
9
  toDOM: () => ['section', 0],
10
10
  };
11
- export const isAffiliationsNode = (node) => node.type === node.type.schema.nodes.affiliations;
11
+ export const isAffiliationsSectionNode = (node) => node.type === node.type.schema.nodes.affiliations_section;
@@ -1,11 +1,11 @@
1
- export const contributors = {
1
+ export const contributorsSection = {
2
2
  content: 'section_title? contributor*',
3
3
  attrs: {
4
4
  id: { default: '' },
5
5
  dataTracked: { default: null },
6
6
  },
7
- group: 'block',
7
+ group: 'block sections',
8
8
  selectable: false,
9
9
  toDOM: () => ['section', 0],
10
10
  };
11
- export const isContributorsNode = (node) => node.type === node.type.schema.nodes.contributors;
11
+ export const isContributorsSectionNode = (node) => node.type === node.type.schema.nodes.contributors_section;
@@ -18,6 +18,7 @@ export const keyword = {
18
18
  content: 'inline*',
19
19
  attrs: {
20
20
  id: { default: '' },
21
+ contents: { default: '' },
21
22
  dataTracked: { default: null },
22
23
  comments: { default: null },
23
24
  },
@@ -15,7 +15,7 @@
15
15
  */
16
16
  export const keywordsElement = {
17
17
  atom: true,
18
- content: 'keyword_group*',
18
+ content: 'keywords_group*',
19
19
  attrs: {
20
20
  id: { default: '' },
21
21
  contents: { default: '' },
@@ -13,7 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- export const keywordGroup = {
16
+ export const keywordsGroup = {
17
17
  content: 'keyword*',
18
18
  attrs: {
19
19
  id: { default: '' },
@@ -28,11 +28,11 @@ export const keywordGroup = {
28
28
  },
29
29
  ],
30
30
  toDOM: (node) => {
31
- const keywordGroupNode = node;
31
+ const keywordsGroupNode = node;
32
32
  return [
33
33
  'div',
34
34
  {
35
- id: keywordGroupNode.attrs.id,
35
+ id: keywordsGroupNode.attrs.id,
36
36
  class: 'keywords',
37
37
  spellcheck: 'false',
38
38
  contenteditable: false,
@@ -41,4 +41,4 @@ export const keywordGroup = {
41
41
  ];
42
42
  },
43
43
  };
44
- export const isKeywordGroupNode = (node) => node.type === node.type.schema.nodes.keywords_group;
44
+ export const isKeywordsGroupNode = (node) => node.type === node.type.schema.nodes.keywords_group;
@@ -13,13 +13,13 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- export const keywords = {
16
+ export const keywordsSection = {
17
17
  content: 'section_title (keywords_element | placeholder_element)',
18
18
  attrs: {
19
19
  id: { default: '' },
20
20
  dataTracked: { default: null },
21
21
  },
22
- group: 'block',
22
+ group: 'block sections',
23
23
  selectable: false,
24
24
  parseDOM: [
25
25
  {
@@ -27,11 +27,11 @@ export const keywords = {
27
27
  },
28
28
  ],
29
29
  toDOM: (node) => {
30
- const keywords = node;
30
+ const keywordsSectionNode = node;
31
31
  return [
32
32
  'div',
33
33
  {
34
- id: keywords.attrs.id,
34
+ id: keywordsSectionNode.attrs.id,
35
35
  class: 'keywords',
36
36
  spellcheck: 'false',
37
37
  contenteditable: false,
@@ -40,4 +40,4 @@ export const keywords = {
40
40
  ];
41
41
  },
42
42
  };
43
- export const isKeywordsNode = (node) => node.type === node.type.schema.nodes.keywords;
43
+ export const isKeywordsSectionNode = (node) => node.type === node.type.schema.nodes.keywords_section;
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  export const manuscript = {
17
- content: 'title? contributors? affiliations? keywords? abstracts body backmatter meta_section',
17
+ content: 'title* (section | sections)+ meta_section',
18
18
  attrs: {
19
19
  id: { default: '' },
20
20
  },
@@ -28,13 +28,12 @@ import { ObjectTypes, } from '@manuscripts/json-schema';
28
28
  import debug from 'debug';
29
29
  import { DOMParser } from 'prosemirror-model';
30
30
  import { MissingElement } from '../errors';
31
- import { groupBy } from '../lib/utils';
32
31
  import { schema, } from '../schema';
33
32
  import { buildTitles } from './builders';
34
33
  import { insertHighlightMarkers } from './highlight-markers';
35
34
  import { generateNodeID } from './id';
36
35
  import { ExtraObjectTypes, hasObjectType, isCommentAnnotation, isManuscript, } from './object-types';
37
- import { chooseCoreSectionBySection, chooseSectionLableName, chooseSectionNodeType, chooseSecType, guessSectionCategory, } from './section-category';
36
+ import { chooseSectionLableName, chooseSectionNodeType, chooseSecType, guessSectionCategory, } from './section-category';
38
37
  import { timestamp } from './timestamp';
39
38
  const warn = debug('manuscripts-transform');
40
39
  const parser = DOMParser.fromSchema(schema);
@@ -55,41 +54,17 @@ export const sortSectionsByPriority = (a, b) => a.priority === b.priority ? 0 :
55
54
  const getSections = (modelMap) => getModelsByType(modelMap, ObjectTypes.Section).sort(sortSectionsByPriority);
56
55
  const getAffiliations = (modelMap) => getModelsByType(modelMap, ObjectTypes.Affiliation);
57
56
  const getContributors = (modelMap) => getModelsByType(modelMap, ObjectTypes.Contributor);
58
- const getKeywordElements = (modelMap) => getModelsByType(modelMap, ObjectTypes.KeywordsElement);
59
- const getKeywordGroups = (modelMap) => getModelsByType(modelMap, ObjectTypes.KeywordGroup);
60
- const getKeywords = (modelMap) => getModelsByType(modelMap, ObjectTypes.Keyword);
61
- const getTitles = (modelMap) => getModelsByType(modelMap, ObjectTypes.Titles)[0];
62
57
  export const isManuscriptNode = (model) => model !== null;
58
+ const isParagraphElement = hasObjectType(ObjectTypes.ParagraphElement);
63
59
  const isFootnote = hasObjectType(ObjectTypes.Footnote);
60
+ const isKeyword = hasObjectType(ObjectTypes.Keyword);
61
+ const isKeywordsSection = (model) => model.category === 'MPSectionCategory:keywords';
62
+ const isAffiliationsSection = (model) => model.category === 'MPSectionCategory:affiliations';
63
+ const isContributorsSection = (model) => model.category === 'MPSectionCategory:contributors';
64
64
  const hasParentSection = (id) => (section) => section.path &&
65
65
  section.path.length > 1 &&
66
66
  section.path[section.path.length - 2] === id;
67
67
  export class Decoder {
68
- createTitleNode() {
69
- const titles = getTitles(this.modelMap) || buildTitles();
70
- return this.decode(titles);
71
- }
72
- createAffiliationsNode() {
73
- const affiliations = getAffiliations(this.modelMap)
74
- .map((a) => this.decode(a))
75
- .filter(Boolean);
76
- return schema.nodes.affiliations.createAndFill({}, affiliations);
77
- }
78
- createContributorsNode() {
79
- const contributors = getContributors(this.modelMap)
80
- .map((c) => this.decode(c))
81
- .filter(Boolean);
82
- return schema.nodes.contributors.createAndFill({}, contributors);
83
- }
84
- createKeywordsNode() {
85
- const elements = getKeywordElements(this.modelMap)
86
- .map((e) => this.decode(e))
87
- .filter(Boolean);
88
- return schema.nodes.keywords.createAndFill({}, [
89
- schema.nodes.section_title.create({}, schema.text('Keywords')),
90
- ...elements,
91
- ]);
92
- }
93
68
  createMetaSectionNode() {
94
69
  const commentListNode = this.createCommentListNode();
95
70
  return schema.nodes.meta_section.createAndFill({}, [
@@ -101,51 +76,54 @@ export class Decoder {
101
76
  ...this.comments.values(),
102
77
  ]);
103
78
  }
79
+ handleMissingRootSectionNodes(rootSectionNodes) {
80
+ if (!rootSectionNodes.find((node) => node.type.name === 'affiliations_section')) {
81
+ this.createAffiliationSectionNode(rootSectionNodes);
82
+ }
83
+ if (!rootSectionNodes.find((node) => node.type.name === 'contributors_section')) {
84
+ this.createContributorSectionNode(rootSectionNodes);
85
+ }
86
+ }
87
+ createAffiliationSectionNode(rootSectionNodes) {
88
+ const affiliationNodes = getAffiliations(this.modelMap)
89
+ .map((affiliation) => this.decode(affiliation))
90
+ .filter(Boolean);
91
+ if (affiliationNodes.length) {
92
+ const node = schema.nodes.affiliations_section.createAndFill({
93
+ id: generateNodeID(schema.nodes.section),
94
+ }, affiliationNodes);
95
+ rootSectionNodes.unshift(node);
96
+ }
97
+ }
98
+ createContributorSectionNode(rootSectionNodes) {
99
+ const contributorNodes = getContributors(this.modelMap)
100
+ .map((contributor) => this.decode(contributor))
101
+ .filter(Boolean);
102
+ if (contributorNodes.length) {
103
+ const node = schema.nodes.contributors_section.createAndFill({
104
+ id: generateNodeID(schema.nodes.section),
105
+ }, contributorNodes);
106
+ rootSectionNodes.unshift(node);
107
+ }
108
+ }
109
+ createTitleNode() {
110
+ const titles = getModelsByType(this.modelMap, ObjectTypes.Titles)[0] ||
111
+ buildTitles();
112
+ return this.decode(titles);
113
+ }
104
114
  createRootSectionNodes() {
105
- let rootSections = getSections(this.modelMap)
106
- .filter((section) => !section.path || section.path.length <= 1)
107
- .filter((section) => section.category !== 'MPSectionCategory:contributors' &&
108
- section.category !== 'MPSectionCategory:affiliations' &&
109
- section.category !== 'MPSectionCategory:keywords');
115
+ let rootSections = getSections(this.modelMap).filter((section) => !section.path || section.path.length <= 1);
110
116
  rootSections = this.addGeneratedLabels(rootSections);
111
- const sectionGroups = groupBy(rootSections, (sec) => {
112
- var _a;
113
- return chooseCoreSectionBySection((_a = sec.category) !== null && _a !== void 0 ? _a : '');
114
- });
115
- this.ensureCoreSectionsExist(sectionGroups);
116
- const absSectionNode = sectionGroups['MPSectionCategory:abstracts']
117
- .map(this.decode)
118
- .filter(isManuscriptNode);
119
- const bodySectionNodes = sectionGroups['MPSectionCategory:body']
120
- .map(this.decode)
121
- .filter(isManuscriptNode);
122
- const backmatterSectionNodes = sectionGroups['MPSectionCategory:backmatter']
117
+ const rootSectionNodes = rootSections
123
118
  .map(this.decode)
124
119
  .filter(isManuscriptNode);
125
- const abstractCoreSectionNodes = this.createAndFill(schema.nodes.abstracts, absSectionNode);
126
- const bodyCoreSectionNodes = this.createAndFill(schema.nodes.body, bodySectionNodes);
127
- const backmatterCoreSectionNodes = this.createAndFill(schema.nodes.backmatter, backmatterSectionNodes);
128
- return {
129
- abstracts: abstractCoreSectionNodes,
130
- body: bodyCoreSectionNodes,
131
- backmatter: backmatterCoreSectionNodes,
132
- };
133
- }
134
- ensureCoreSectionsExist(coreSections) {
135
- if (!coreSections['MPSectionCategory:abstracts']) {
136
- coreSections['MPSectionCategory:abstracts'] = [];
137
- }
138
- if (!coreSections['MPSectionCategory:body']) {
139
- coreSections['MPSectionCategory:body'] = [];
120
+ this.handleMissingRootSectionNodes(rootSectionNodes);
121
+ if (!rootSectionNodes.length) {
122
+ rootSectionNodes.push(schema.nodes.section.createAndFill({
123
+ id: generateNodeID(schema.nodes.section),
124
+ }));
140
125
  }
141
- if (!coreSections['MPSectionCategory:backmatter']) {
142
- coreSections['MPSectionCategory:backmatter'] = [];
143
- }
144
- }
145
- createAndFill(nodeType, content) {
146
- return nodeType.createAndFill({
147
- id: generateNodeID(nodeType),
148
- }, content);
126
+ return rootSectionNodes;
149
127
  }
150
128
  createCommentsNode(model) {
151
129
  const comments = [];
@@ -351,32 +329,19 @@ export class Decoder {
351
329
  },
352
330
  [ObjectTypes.KeywordsElement]: (data) => {
353
331
  const model = data;
354
- const keywordGroups = getKeywordGroups(this.modelMap).map(k => this.decode(k));
332
+ const keywordGroups = this.getKeywordGroups();
355
333
  return schema.nodes.keywords_element.create({
356
334
  id: model._id,
357
335
  paragraphStyle: model.paragraphStyle,
358
336
  }, keywordGroups);
359
337
  },
360
- [ObjectTypes.KeywordGroup]: (data) => {
361
- const keywordGroup = data;
362
- const keywords = getKeywords(this.modelMap).filter(k => k.containedGroup === keywordGroup._id).map(k => this.decode(k));
363
- const comments = this.createCommentsNode(keywordGroup);
364
- comments.forEach((c) => this.comments.set(c.attrs.id, c));
365
- return schema.nodes.keyword_group.create({
366
- id: keywordGroup._id,
367
- type: keywordGroup.type,
368
- comments: comments.map((c) => c.attrs.id),
369
- }, keywords);
370
- },
371
338
  [ObjectTypes.Keyword]: (data) => {
372
- const keyword = data;
373
- const comments = this.createCommentsNode(keyword);
374
- comments.forEach((c) => this.comments.set(c.attrs.id, c));
339
+ const model = data;
375
340
  return schema.nodes.keyword.create({
376
- id: keyword._id,
377
- contents: keyword.name,
378
- comments: comments.map((c) => c.attrs.id),
379
- }, schema.text(keyword.name));
341
+ id: model._id,
342
+ contents: model.name,
343
+ comments: this.createCommentsNode(model),
344
+ }, schema.text(model.name));
380
345
  },
381
346
  [ObjectTypes.ListElement]: (data) => {
382
347
  const model = data;
@@ -498,6 +463,9 @@ export class Decoder {
498
463
  for (const id of model.elementIDs) {
499
464
  const element = this.getModel(id);
500
465
  if (element) {
466
+ if (isKeywordsSection(model) && isParagraphElement(element)) {
467
+ continue;
468
+ }
501
469
  elements.push(element);
502
470
  }
503
471
  else if (this.allowMissingElements) {
@@ -538,11 +506,17 @@ export class Decoder {
538
506
  const sectionNodeType = chooseSectionNodeType(sectionCategory);
539
507
  const commentNodes = this.createCommentsNode(model);
540
508
  commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
541
- const content = (sectionLabelNode
542
- ? [sectionLabelNode, sectionTitleNode]
543
- : [sectionTitleNode])
544
- .concat(elementNodes)
545
- .concat(nestedSections);
509
+ let content;
510
+ if (isAffiliationsSection(model) || isContributorsSection(model)) {
511
+ content = elementNodes.concat(nestedSections);
512
+ }
513
+ else {
514
+ content = (sectionLabelNode
515
+ ? [sectionLabelNode, sectionTitleNode]
516
+ : [sectionTitleNode])
517
+ .concat(elementNodes)
518
+ .concat(nestedSections);
519
+ }
546
520
  const sectionNode = sectionNodeType.createAndFill({
547
521
  id: model._id,
548
522
  category: sectionCategory,
@@ -661,21 +635,13 @@ export class Decoder {
661
635
  };
662
636
  this.getModel = (id) => this.modelMap.get(id);
663
637
  this.createArticleNode = (manuscriptID) => {
664
- const title = this.createTitleNode();
665
- const contributors = this.createContributorsNode();
666
- const affiliations = this.createAffiliationsNode();
667
- const keywords = this.createKeywordsNode();
668
- const { abstracts, body, backmatter } = this.createRootSectionNodes();
669
- const meta = this.createMetaSectionNode();
638
+ const titlesNode = this.createTitleNode();
639
+ const rootSectionNodes = this.createRootSectionNodes();
640
+ const metaSectionNode = this.createMetaSectionNode();
670
641
  const contents = [
671
- title,
672
- contributors,
673
- affiliations,
674
- keywords,
675
- abstracts,
676
- body,
677
- backmatter,
678
- meta,
642
+ titlesNode,
643
+ ...rootSectionNodes,
644
+ metaSectionNode,
679
645
  ];
680
646
  return schema.nodes.manuscript.create({
681
647
  id: manuscriptID || this.getManuscriptID(),
@@ -717,6 +683,23 @@ export class Decoder {
717
683
  }
718
684
  return parser.parse(template.content.firstElementChild, options);
719
685
  };
686
+ this.getKeywords = (id) => {
687
+ const keywordsOfKind = [];
688
+ const keywordsByGroup = [...this.modelMap.values()].filter((m) => m.objectType === ObjectTypes.Keyword &&
689
+ m.containedGroup === id);
690
+ for (const model of keywordsByGroup) {
691
+ if (isKeyword(model)) {
692
+ const keyword = this.parseContents(model.name || '', 'div', this.getComments(model), {
693
+ topNode: schema.nodes.keyword.create({
694
+ id: model._id,
695
+ contents: model.name,
696
+ }),
697
+ });
698
+ keywordsOfKind.push(keyword);
699
+ }
700
+ }
701
+ return keywordsOfKind;
702
+ };
720
703
  this.getManuscriptID = () => {
721
704
  for (const item of this.modelMap.values()) {
722
705
  if (isManuscript(item)) {
@@ -801,4 +784,26 @@ export class Decoder {
801
784
  }
802
785
  return listing;
803
786
  }
787
+ getKeywordGroups() {
788
+ const kwdGroupNodes = [];
789
+ const kwdGroupsModels = [
790
+ ...this.modelMap.values(),
791
+ ].filter((model) => model.objectType === ObjectTypes.KeywordGroup);
792
+ if (kwdGroupsModels.length > 0) {
793
+ for (const kwdGroupModel of kwdGroupsModels) {
794
+ const keywords = this.getKeywords(kwdGroupModel._id);
795
+ const commentNodes = this.createCommentsNode(kwdGroupModel);
796
+ commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
797
+ const contents = [];
798
+ contents.push(...keywords);
799
+ const kwdGroupNode = schema.nodes.keywords_group.create({
800
+ id: kwdGroupModel._id,
801
+ type: kwdGroupModel.type,
802
+ comments: commentNodes.map((c) => c.attrs.id),
803
+ }, contents);
804
+ kwdGroupNodes.push(kwdGroupNode);
805
+ }
806
+ }
807
+ return kwdGroupNodes;
808
+ }
804
809
  }
@@ -420,9 +420,34 @@ const encoders = {
420
420
  elementType: 'div',
421
421
  paragraphStyle: node.attrs.paragraphStyle || undefined,
422
422
  }),
423
- keyword_group: (node) => ({
423
+ keywords_group: (node) => ({
424
424
  type: node.attrs.type,
425
425
  }),
426
+ keywords_section: (node, parent, path, priority) => ({
427
+ category: buildSectionCategory(node),
428
+ priority: priority.value++,
429
+ title: inlineContentsOfNodeType(node, node.type.schema.nodes.section_title),
430
+ path: path.concat([node.attrs.id]),
431
+ elementIDs: childElements(node)
432
+ .map((childNode) => childNode.attrs.id)
433
+ .filter((id) => id),
434
+ }),
435
+ affiliations_section: (node, parent, path, priority) => ({
436
+ category: buildSectionCategory(node),
437
+ priority: priority.value++,
438
+ path: path.concat([node.attrs.id]),
439
+ elementIDs: childElements(node)
440
+ .map((childNode) => childNode.attrs.id)
441
+ .filter((id) => id),
442
+ }),
443
+ contributors_section: (node, parent, path, priority) => ({
444
+ category: buildSectionCategory(node),
445
+ priority: priority.value++,
446
+ path: path.concat([node.attrs.id]),
447
+ elementIDs: childElements(node)
448
+ .map((childNode) => childNode.attrs.id)
449
+ .filter((id) => id),
450
+ }),
426
451
  missing_figure: (node) => ({
427
452
  position: node.attrs.position || undefined,
428
453
  }),
@@ -540,16 +565,9 @@ export const modelFromNode = (node, parent, path, priority) => {
540
565
  if (isHighlightableModel(model)) {
541
566
  extractHighlightMarkers(model, commentAnnotationsMap);
542
567
  }
543
- return { model, commentAnnotationsMap };
568
+ const comments = [...commentAnnotationsMap.values()];
569
+ return { model, comments };
544
570
  };
545
- function isCoreSection(child) {
546
- return (child.type === schema.nodes.abstracts ||
547
- child.type === schema.nodes.body ||
548
- child.type === schema.nodes.backmatter ||
549
- child.type === schema.nodes.affiliations ||
550
- child.type === schema.nodes.contributors ||
551
- child.type === schema.nodes.keywords);
552
- }
553
571
  export const encode = (node) => {
554
572
  const models = new Map();
555
573
  const priority = {
@@ -557,10 +575,6 @@ export const encode = (node) => {
557
575
  };
558
576
  const placeholders = ['placeholder', 'placeholder_element'];
559
577
  const addModel = (path, parent) => (child) => {
560
- if (isCoreSection(child)) {
561
- child.forEach(addModel([], child));
562
- return;
563
- }
564
578
  if (!child.attrs.id) {
565
579
  return;
566
580
  }
@@ -573,10 +587,18 @@ export const encode = (node) => {
573
587
  if (placeholders.includes(child.type.name)) {
574
588
  return;
575
589
  }
576
- const { model } = modelFromNode(child, parent, path, priority);
590
+ const { model, comments } = modelFromNode(child, parent, path, priority);
577
591
  if (models.has(model._id)) {
578
592
  throw Error(`Encountered duplicate ids in models map while encoding: ${model._id}`);
579
593
  }
594
+ comments.forEach((comment) => {
595
+ const proseMirrorComment = models.get(comment._id);
596
+ if (proseMirrorComment) {
597
+ ;
598
+ proseMirrorComment.selector = comment.selector;
599
+ models.set(comment._id, proseMirrorComment);
600
+ }
601
+ });
580
602
  models.set(model._id, model);
581
603
  child.forEach(addModel(path.concat(child.attrs.id), child));
582
604
  };