@manuscripts/transform 1.5.5 → 1.5.7-LEAN-3030

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 +23 -18
  2. package/dist/cjs/jats/importer/jats-body-transformations.js +3 -3
  3. package/dist/cjs/jats/jats-exporter.js +11 -6
  4. package/dist/cjs/lib/utils.js +10 -1
  5. package/dist/cjs/schema/index.js +15 -11
  6. package/dist/cjs/schema/nodes/{affiliations_section.js → affiliations.js} +9 -8
  7. package/dist/cjs/schema/nodes/{contributors_section.js → contributors.js} +8 -7
  8. package/dist/cjs/schema/nodes/core_section.js +27 -0
  9. package/dist/cjs/schema/nodes/graphical_abstract_section.js +1 -0
  10. package/dist/cjs/schema/nodes/keyword.js +0 -1
  11. package/dist/cjs/schema/nodes/{keywords_group.js → keyword_group.js} +6 -6
  12. package/dist/cjs/schema/nodes/{keywords_section.js → keywords.js} +7 -7
  13. package/dist/cjs/schema/nodes/keywords_element.js +1 -1
  14. package/dist/cjs/schema/nodes/manuscript.js +1 -1
  15. package/dist/cjs/transformer/decode.js +110 -113
  16. package/dist/cjs/transformer/encode.js +24 -28
  17. package/dist/cjs/transformer/node-names.js +1 -1
  18. package/dist/cjs/transformer/node-title.js +1 -1
  19. package/dist/cjs/transformer/node-types.js +7 -4
  20. package/dist/cjs/transformer/section-category.js +35 -8
  21. package/dist/es/jats/importer/jats-body-dom-parser.js +23 -18
  22. package/dist/es/jats/importer/jats-body-transformations.js +3 -3
  23. package/dist/es/jats/jats-exporter.js +11 -6
  24. package/dist/es/lib/utils.js +8 -0
  25. package/dist/es/schema/index.js +15 -11
  26. package/dist/es/schema/nodes/{affiliations_section.js → affiliations.js} +7 -6
  27. package/dist/es/schema/nodes/{contributors_section.js → contributors.js} +6 -5
  28. package/dist/es/schema/nodes/core_section.js +24 -0
  29. package/dist/es/schema/nodes/graphical_abstract_section.js +1 -0
  30. package/dist/es/schema/nodes/keyword.js +0 -1
  31. package/dist/es/schema/nodes/{keywords_group.js → keyword_group.js} +4 -4
  32. package/dist/es/schema/nodes/{keywords_section.js → keywords.js} +5 -5
  33. package/dist/es/schema/nodes/keywords_element.js +1 -1
  34. package/dist/es/schema/nodes/manuscript.js +1 -1
  35. package/dist/es/transformer/decode.js +111 -114
  36. package/dist/es/transformer/encode.js +24 -28
  37. package/dist/es/transformer/node-names.js +1 -1
  38. package/dist/es/transformer/node-title.js +1 -1
  39. package/dist/es/transformer/node-types.js +7 -4
  40. package/dist/es/transformer/section-category.js +33 -7
  41. package/dist/types/lib/utils.d.ts +1 -0
  42. package/dist/types/schema/index.d.ts +3 -3
  43. package/dist/types/schema/nodes/affiliations.d.ts +11 -0
  44. package/dist/types/schema/nodes/contributors.d.ts +12 -0
  45. package/dist/types/schema/nodes/core_section.d.ts +17 -0
  46. package/dist/types/schema/nodes/keyword.d.ts +0 -1
  47. package/dist/types/schema/nodes/{keywords_group.d.ts → keyword_group.d.ts} +3 -3
  48. package/dist/types/schema/nodes/{keywords_section.d.ts → keywords.d.ts} +3 -3
  49. package/dist/types/schema/types.d.ts +1 -1
  50. package/dist/types/transformer/decode.d.ts +6 -6
  51. package/dist/types/transformer/encode.d.ts +1 -1
  52. package/dist/types/transformer/section-category.d.ts +1 -0
  53. package/package.json +1 -1
  54. package/dist/types/schema/nodes/affiliations_section.d.ts +0 -11
  55. package/dist/types/schema/nodes/contributors_section.d.ts +0 -12
@@ -424,9 +424,11 @@ export class JATSExporter {
424
424
  }
425
425
  };
426
426
  this.buildBody = (fragment) => {
427
- const content = this.serializeFragment(fragment);
428
427
  const body = this.document.createElement('body');
429
- body.appendChild(content);
428
+ fragment.forEach((cFragment) => {
429
+ const serializedNode = this.serializeNode(cFragment);
430
+ body.append(...serializedNode.childNodes);
431
+ });
430
432
  this.fixBody(body, fragment);
431
433
  return body;
432
434
  };
@@ -593,8 +595,8 @@ export class JATSExporter {
593
595
  const getModel = (id) => id ? this.modelMap.get(id) : undefined;
594
596
  const nodes = {
595
597
  title: () => '',
596
- affiliations_section: () => '',
597
- contributors_section: () => '',
598
+ affiliations: () => '',
599
+ contributors: () => '',
598
600
  table_element_footer: () => ['table-wrap-foot', 0],
599
601
  contributor: () => '',
600
602
  affiliation: () => '',
@@ -603,7 +605,10 @@ export class JATSExporter {
603
605
  bibliography_element: () => '',
604
606
  bibliography_item: () => '',
605
607
  comment_list: () => '',
606
- keywords_group: () => '',
608
+ keyword_group: () => '',
609
+ body: () => ['body', 0],
610
+ abstracts: () => ['abstract', 0],
611
+ backmatter: () => ['backmatter', 0],
607
612
  bibliography_section: (node) => [
608
613
  'ref-list',
609
614
  { id: normalizeID(node.attrs.id) },
@@ -782,7 +787,7 @@ export class JATSExporter {
782
787
  },
783
788
  keyword: () => '',
784
789
  keywords_element: () => '',
785
- keywords_section: () => '',
790
+ keywords: () => '',
786
791
  link: (node) => {
787
792
  const text = node.textContent;
788
793
  if (!text) {
@@ -84,3 +84,11 @@ 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 { affiliationsSection } from './nodes/affiliations_section';
19
+ import { affiliations } from './nodes/affiliations';
20
20
  import { attribution } from './nodes/attribution';
21
21
  import { bibliographyElement } from './nodes/bibliography_element';
22
22
  import { bibliographyItem } from './nodes/bibliography_item';
@@ -28,7 +28,8 @@ 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 { contributorsSection } from './nodes/contributors_section';
31
+ import { contributors } from './nodes/contributors';
32
+ import { coreSection } from './nodes/core_section';
32
33
  import { crossReference } from './nodes/cross_reference';
33
34
  import { doc } from './nodes/doc';
34
35
  import { equation } from './nodes/equation';
@@ -45,9 +46,9 @@ import { highlightMarker } from './nodes/highlight_marker';
45
46
  import { inlineEquation } from './nodes/inline_equation';
46
47
  import { inlineFootnote } from './nodes/inline_footnote';
47
48
  import { keyword } from './nodes/keyword';
49
+ import { keywordGroup } from './nodes/keyword_group';
50
+ import { keywords } from './nodes/keywords';
48
51
  import { keywordsElement } from './nodes/keywords_element';
49
- import { keywordsGroup } from './nodes/keywords_group';
50
- import { keywordsSection } from './nodes/keywords_section';
51
52
  import { link } from './nodes/link';
52
53
  import { bulletList, listItem, orderedList } from './nodes/list';
53
54
  import { listing } from './nodes/listing';
@@ -100,7 +101,7 @@ export * from './nodes/inline_equation';
100
101
  export * from './nodes/inline_footnote';
101
102
  export * from './nodes/keyword';
102
103
  export * from './nodes/keywords_element';
103
- export * from './nodes/keywords_section';
104
+ export * from './nodes/keywords';
104
105
  export * from './nodes/link';
105
106
  export * from './nodes/list';
106
107
  export * from './nodes/listing';
@@ -125,8 +126,8 @@ export * from './nodes/meta_section';
125
126
  export * from './nodes/contributor';
126
127
  export * from './nodes/table_element_footer';
127
128
  export * from './nodes/title';
128
- export * from './nodes/affiliations_section';
129
- export * from './nodes/contributors_section';
129
+ export * from './nodes/affiliations';
130
+ export * from './nodes/contributors';
130
131
  export const schema = new Schema({
131
132
  marks: {
132
133
  bold,
@@ -153,6 +154,9 @@ export const schema = new Schema({
153
154
  caption,
154
155
  caption_title: captionTitle,
155
156
  citation,
157
+ abstracts: coreSection,
158
+ body: coreSection,
159
+ backmatter: coreSection,
156
160
  cross_reference: crossReference,
157
161
  doc,
158
162
  equation,
@@ -170,8 +174,8 @@ export const schema = new Schema({
170
174
  inline_footnote: inlineFootnote,
171
175
  keyword,
172
176
  keywords_element: keywordsElement,
173
- keywords_section: keywordsSection,
174
- keywords_group: keywordsGroup,
177
+ keywords: keywords,
178
+ keyword_group: keywordGroup,
175
179
  link,
176
180
  list_item: listItem,
177
181
  listing,
@@ -202,7 +206,7 @@ export const schema = new Schema({
202
206
  contributor: contributor,
203
207
  table_element_footer: tableElementFooter,
204
208
  title: title,
205
- affiliations_section: affiliationsSection,
206
- contributors_section: contributorsSection,
209
+ affiliations: affiliations,
210
+ contributors: contributors,
207
211
  },
208
212
  });
@@ -1,10 +1,11 @@
1
- export const affiliationsSection = {
1
+ import { schema } from '../index';
2
+ export const affiliations = {
2
3
  content: 'section_title? affiliation*',
3
4
  attrs: {
4
5
  id: { default: 'META_SECTION_AFFILLIATIONS' },
5
6
  dataTracked: { default: null },
6
7
  },
7
- group: 'block sections',
8
+ group: 'block',
8
9
  selectable: false,
9
10
  parseDOM: [
10
11
  {
@@ -18,15 +19,15 @@ export const affiliationsSection = {
18
19
  },
19
20
  ],
20
21
  toDOM: (node) => {
21
- const affiliationsSectionNode = node;
22
+ const affiliations = node;
22
23
  return [
23
24
  'section',
24
25
  {
25
26
  class: 'affiliations',
26
- id: affiliationsSectionNode.attrs.id,
27
+ id: affiliations.attrs.id,
27
28
  },
28
- 0
29
+ 0,
29
30
  ];
30
31
  },
31
32
  };
32
- export const isAffiliationsSectionNode = (node) => node.type === node.type.schema.nodes.affiliations_section;
33
+ export const isAffiliationsNode = (node) => node.type === schema.nodes.affiliations;
@@ -1,11 +1,12 @@
1
- export const contributorsSection = {
1
+ import { schema } from '../index';
2
+ export const contributors = {
2
3
  content: 'section_title? contributor*',
3
4
  attrs: {
4
5
  id: { default: 'META_SECTION_CONTRIBUTORS' },
5
6
  dataTracked: { default: null },
6
7
  contents: { default: '' },
7
8
  },
8
- group: 'block sections',
9
+ group: 'block',
9
10
  selectable: false,
10
11
  parseDOM: [
11
12
  {
@@ -18,15 +19,15 @@ export const contributorsSection = {
18
19
  },
19
20
  ],
20
21
  toDOM: (node) => {
21
- const contributorsSectionNode = node;
22
+ const contributors = node;
22
23
  return [
23
24
  'section',
24
25
  {
25
26
  class: 'contributors',
26
- id: contributorsSectionNode.attrs.id,
27
+ id: contributors.attrs.id,
27
28
  },
28
29
  0,
29
30
  ];
30
31
  },
31
32
  };
32
- export const isContributorsSectionNode = (node) => node.type === node.type.schema.nodes.contributors_section;
33
+ export const isContributorsNode = (node) => node.type === schema.nodes.contributors;
@@ -0,0 +1,24 @@
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
+ export const coreSection = {
17
+ content: 'sections*',
18
+ atom: true,
19
+ attrs: {
20
+ id: { default: '' },
21
+ },
22
+ group: 'block',
23
+ toDOM: () => ['section', 0],
24
+ };
@@ -18,6 +18,7 @@ export const graphicalAbstractSection = {
18
18
  attrs: {
19
19
  id: { default: '' },
20
20
  category: { default: '' },
21
+ dataTracked: { default: null },
21
22
  },
22
23
  group: 'block sections',
23
24
  selectable: false,
@@ -18,7 +18,6 @@ export const keyword = {
18
18
  content: 'inline*',
19
19
  attrs: {
20
20
  id: { default: '' },
21
- contents: { default: '' },
22
21
  dataTracked: { default: null },
23
22
  comments: { default: null },
24
23
  },
@@ -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 keywordsGroup = {
16
+ export const keywordGroup = {
17
17
  content: 'keyword*',
18
18
  attrs: {
19
19
  id: { default: '' },
@@ -28,11 +28,11 @@ export const keywordsGroup = {
28
28
  },
29
29
  ],
30
30
  toDOM: (node) => {
31
- const keywordsGroupNode = node;
31
+ const keywordGroupNode = node;
32
32
  return [
33
33
  'div',
34
34
  {
35
- id: keywordsGroupNode.attrs.id,
35
+ id: keywordGroupNode.attrs.id,
36
36
  class: 'keywords',
37
37
  spellcheck: 'false',
38
38
  contenteditable: false,
@@ -41,4 +41,4 @@ export const keywordsGroup = {
41
41
  ];
42
42
  },
43
43
  };
44
- export const isKeywordsGroupNode = (node) => node.type === node.type.schema.nodes.keywords_group;
44
+ export const isKeywordGroupNode = (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 keywordsSection = {
16
+ export const keywords = {
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 sections',
22
+ group: 'block',
23
23
  selectable: false,
24
24
  parseDOM: [
25
25
  {
@@ -27,11 +27,11 @@ export const keywordsSection = {
27
27
  },
28
28
  ],
29
29
  toDOM: (node) => {
30
- const keywordsSectionNode = node;
30
+ const keywords = node;
31
31
  return [
32
32
  'div',
33
33
  {
34
- id: keywordsSectionNode.attrs.id,
34
+ id: keywords.attrs.id,
35
35
  class: 'keywords',
36
36
  spellcheck: 'false',
37
37
  contenteditable: false,
@@ -40,4 +40,4 @@ export const keywordsSection = {
40
40
  ];
41
41
  },
42
42
  };
43
- export const isKeywordsSectionNode = (node) => node.type === node.type.schema.nodes.keywords_section;
43
+ export const isKeywordsNode = (node) => node.type === node.type.schema.nodes.keywords;
@@ -15,7 +15,7 @@
15
15
  */
16
16
  export const keywordsElement = {
17
17
  atom: true,
18
- content: 'keywords_group*',
18
+ content: 'keyword_group*',
19
19
  attrs: {
20
20
  id: { default: '' },
21
21
  contents: { default: '' },
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  export const manuscript = {
17
- content: 'title* (section | sections)+ meta_section',
17
+ content: 'title? contributors? affiliations? keywords? abstracts body backmatter meta_section',
18
18
  attrs: {
19
19
  id: { default: '' },
20
20
  },
@@ -28,12 +28,13 @@ 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';
31
32
  import { schema, } from '../schema';
32
33
  import { buildTitles } from './builders';
33
34
  import { insertHighlightMarkers } from './highlight-markers';
34
35
  import { generateNodeID } from './id';
35
36
  import { ExtraObjectTypes, hasObjectType, isCommentAnnotation, isManuscript, } from './object-types';
36
- import { chooseSectionLableName, chooseSectionNodeType, chooseSecType, guessSectionCategory, } from './section-category';
37
+ import { chooseCoreSectionBySection, chooseSectionLableName, chooseSectionNodeType, chooseSecType, guessSectionCategory, } from './section-category';
37
38
  import { timestamp } from './timestamp';
38
39
  const warn = debug('manuscripts-transform');
39
40
  const parser = DOMParser.fromSchema(schema);
@@ -54,17 +55,41 @@ export const sortSectionsByPriority = (a, b) => a.priority === b.priority ? 0 :
54
55
  const getSections = (modelMap) => getModelsByType(modelMap, ObjectTypes.Section).sort(sortSectionsByPriority);
55
56
  const getAffiliations = (modelMap) => getModelsByType(modelMap, ObjectTypes.Affiliation);
56
57
  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];
57
62
  export const isManuscriptNode = (model) => model !== null;
58
- const isParagraphElement = hasObjectType(ObjectTypes.ParagraphElement);
59
63
  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
+ }
68
93
  createMetaSectionNode() {
69
94
  const commentListNode = this.createCommentListNode();
70
95
  return schema.nodes.meta_section.createAndFill({}, [
@@ -76,54 +101,51 @@ export class Decoder {
76
101
  ...this.comments.values(),
77
102
  ]);
78
103
  }
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
- }
114
104
  createRootSectionNodes() {
115
- let rootSections = getSections(this.modelMap).filter((section) => !section.path || section.path.length <= 1);
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');
116
110
  rootSections = this.addGeneratedLabels(rootSections);
117
- const rootSectionNodes = 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']
118
123
  .map(this.decode)
119
124
  .filter(isManuscriptNode);
120
- this.handleMissingRootSectionNodes(rootSectionNodes);
121
- if (!rootSectionNodes.length) {
122
- rootSectionNodes.push(schema.nodes.section.createAndFill({
123
- id: generateNodeID(schema.nodes.section),
124
- }));
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'] = [];
125
140
  }
126
- return rootSectionNodes;
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);
127
149
  }
128
150
  createCommentsNode(model) {
129
151
  const comments = [];
@@ -329,19 +351,34 @@ export class Decoder {
329
351
  },
330
352
  [ObjectTypes.KeywordsElement]: (data) => {
331
353
  const model = data;
332
- const keywordGroups = this.getKeywordGroups();
354
+ const keywordGroups = getKeywordGroups(this.modelMap).map((k) => this.decode(k));
333
355
  return schema.nodes.keywords_element.create({
334
356
  id: model._id,
335
357
  paragraphStyle: model.paragraphStyle,
336
358
  }, keywordGroups);
337
359
  },
360
+ [ObjectTypes.KeywordGroup]: (data) => {
361
+ const keywordGroup = data;
362
+ const keywords = getKeywords(this.modelMap)
363
+ .filter((k) => k.containedGroup === keywordGroup._id)
364
+ .map((k) => this.decode(k));
365
+ const comments = this.createCommentsNode(keywordGroup);
366
+ comments.forEach((c) => this.comments.set(c.attrs.id, c));
367
+ return schema.nodes.keyword_group.create({
368
+ id: keywordGroup._id,
369
+ type: keywordGroup.type,
370
+ comments: comments.map((c) => c.attrs.id),
371
+ }, keywords);
372
+ },
338
373
  [ObjectTypes.Keyword]: (data) => {
339
- const model = data;
374
+ const keyword = data;
375
+ const comments = this.createCommentsNode(keyword);
376
+ comments.forEach((c) => this.comments.set(c.attrs.id, c));
340
377
  return schema.nodes.keyword.create({
341
- id: model._id,
342
- contents: model.name,
343
- comments: this.createCommentsNode(model),
344
- }, schema.text(model.name));
378
+ id: keyword._id,
379
+ contents: keyword.name,
380
+ comments: comments.map((c) => c.attrs.id),
381
+ }, schema.text(keyword.name));
345
382
  },
346
383
  [ObjectTypes.ListElement]: (data) => {
347
384
  const model = data;
@@ -463,9 +500,6 @@ export class Decoder {
463
500
  for (const id of model.elementIDs) {
464
501
  const element = this.getModel(id);
465
502
  if (element) {
466
- if (isKeywordsSection(model) && isParagraphElement(element)) {
467
- continue;
468
- }
469
503
  elements.push(element);
470
504
  }
471
505
  else if (this.allowMissingElements) {
@@ -506,17 +540,11 @@ export class Decoder {
506
540
  const sectionNodeType = chooseSectionNodeType(sectionCategory);
507
541
  const commentNodes = this.createCommentsNode(model);
508
542
  commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
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
- }
543
+ const content = (sectionLabelNode
544
+ ? [sectionLabelNode, sectionTitleNode]
545
+ : [sectionTitleNode])
546
+ .concat(elementNodes)
547
+ .concat(nestedSections);
520
548
  const sectionNode = sectionNodeType.createAndFill({
521
549
  id: model._id,
522
550
  category: sectionCategory,
@@ -635,13 +663,21 @@ export class Decoder {
635
663
  };
636
664
  this.getModel = (id) => this.modelMap.get(id);
637
665
  this.createArticleNode = (manuscriptID) => {
638
- const titlesNode = this.createTitleNode();
639
- const rootSectionNodes = this.createRootSectionNodes();
640
- const metaSectionNode = this.createMetaSectionNode();
666
+ const title = this.createTitleNode();
667
+ const contributors = this.createContributorsNode();
668
+ const affiliations = this.createAffiliationsNode();
669
+ const keywords = this.createKeywordsNode();
670
+ const { abstracts, body, backmatter } = this.createRootSectionNodes();
671
+ const meta = this.createMetaSectionNode();
641
672
  const contents = [
642
- titlesNode,
643
- ...rootSectionNodes,
644
- metaSectionNode,
673
+ title,
674
+ contributors,
675
+ affiliations,
676
+ keywords,
677
+ abstracts,
678
+ body,
679
+ backmatter,
680
+ meta,
645
681
  ];
646
682
  return schema.nodes.manuscript.create({
647
683
  id: manuscriptID || this.getManuscriptID(),
@@ -683,23 +719,6 @@ export class Decoder {
683
719
  }
684
720
  return parser.parse(template.content.firstElementChild, options);
685
721
  };
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
- };
703
722
  this.getManuscriptID = () => {
704
723
  for (const item of this.modelMap.values()) {
705
724
  if (isManuscript(item)) {
@@ -784,26 +803,4 @@ export class Decoder {
784
803
  }
785
804
  return listing;
786
805
  }
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
- }
809
806
  }