@manuscripts/transform 1.4.6 → 1.5.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 (52) hide show
  1. package/dist/cjs/jats/importer/jats-body-dom-parser.js +126 -0
  2. package/dist/cjs/jats/importer/jats-body-transformations.js +59 -0
  3. package/dist/cjs/jats/importer/jats-front-parser.js +7 -7
  4. package/dist/cjs/jats/importer/jats-parser-utils.js +4 -0
  5. package/dist/cjs/jats/importer/parse-jats-article.js +7 -5
  6. package/dist/cjs/jats/jats-exporter.js +2 -2
  7. package/dist/cjs/lib/core-section-categories.js +2 -2
  8. package/dist/cjs/schema/index.js +6 -6
  9. package/dist/cjs/schema/nodes/affiliation.js +3 -0
  10. package/dist/cjs/schema/nodes/affiliations_section.js +15 -0
  11. package/dist/cjs/schema/nodes/contributor.js +8 -1
  12. package/dist/cjs/schema/nodes/contributors_section.js +15 -0
  13. package/dist/cjs/schema/nodes/meta_section.js +1 -1
  14. package/dist/cjs/transformer/decode.js +51 -23
  15. package/dist/cjs/transformer/encode.js +20 -0
  16. package/dist/cjs/transformer/node-types.js +2 -0
  17. package/dist/cjs/transformer/section-category.js +12 -0
  18. package/dist/es/jats/importer/jats-body-dom-parser.js +126 -0
  19. package/dist/es/jats/importer/jats-body-transformations.js +59 -0
  20. package/dist/es/jats/importer/jats-front-parser.js +1 -1
  21. package/dist/es/jats/importer/jats-parser-utils.js +4 -0
  22. package/dist/es/jats/importer/parse-jats-article.js +7 -5
  23. package/dist/es/jats/jats-exporter.js +2 -2
  24. package/dist/es/lib/core-section-categories.js +2 -2
  25. package/dist/es/schema/index.js +6 -6
  26. package/dist/es/schema/nodes/affiliation.js +3 -0
  27. package/dist/es/schema/nodes/affiliations_section.js +11 -0
  28. package/dist/es/schema/nodes/contributor.js +6 -0
  29. package/dist/es/schema/nodes/contributors_section.js +11 -0
  30. package/dist/es/schema/nodes/meta_section.js +1 -1
  31. package/dist/es/transformer/decode.js +51 -23
  32. package/dist/es/transformer/encode.js +20 -0
  33. package/dist/es/transformer/node-types.js +2 -0
  34. package/dist/es/transformer/section-category.js +12 -0
  35. package/dist/types/jats/importer/jats-body-transformations.d.ts +4 -1
  36. package/dist/types/jats/importer/jats-front-parser.d.ts +5 -5
  37. package/dist/types/jats/importer/parse-jats-article.d.ts +5 -2
  38. package/dist/types/schema/index.d.ts +2 -2
  39. package/dist/types/schema/nodes/affiliation.d.ts +1 -0
  40. package/dist/types/schema/nodes/affiliations_section.d.ts +11 -0
  41. package/dist/types/schema/nodes/contributor.d.ts +2 -0
  42. package/dist/types/schema/nodes/contributors_section.d.ts +11 -0
  43. package/dist/types/schema/types.d.ts +1 -1
  44. package/dist/types/transformer/decode.d.ts +3 -2
  45. package/dist/types/transformer/section-category.d.ts +2 -2
  46. package/package.json +2 -2
  47. package/dist/cjs/schema/nodes/affiliation_list.js +0 -10
  48. package/dist/cjs/schema/nodes/contributor_list.js +0 -10
  49. package/dist/es/schema/nodes/affiliation_list.js +0 -7
  50. package/dist/es/schema/nodes/contributor_list.js +0 -7
  51. package/dist/types/schema/nodes/affiliation_list.d.ts +0 -10
  52. package/dist/types/schema/nodes/contributor_list.d.ts +0 -10
@@ -57,6 +57,8 @@ exports.nodeTypesMap = new Map([
57
57
  [schema_1.schema.nodes.affiliation, json_schema_1.ObjectTypes.Affiliation],
58
58
  [schema_1.schema.nodes.contributor, json_schema_1.ObjectTypes.Contributor],
59
59
  [schema_1.schema.nodes.table_element_footer, json_schema_1.ObjectTypes.TableElementFooter],
60
+ [schema_1.schema.nodes.contributors_section, json_schema_1.ObjectTypes.Section],
61
+ [schema_1.schema.nodes.affiliations_section, json_schema_1.ObjectTypes.Section],
60
62
  ]);
61
63
  const isExecutableNodeType = (type) => (0, schema_1.hasGroup)(type, schema_1.GROUP_EXECUTABLE);
62
64
  exports.isExecutableNodeType = isExecutableNodeType;
@@ -46,6 +46,10 @@ const chooseSectionNodeType = (category) => {
46
46
  return schema_1.schema.nodes.footnotes_section;
47
47
  case 'MPSectionCategory:keywords':
48
48
  return schema_1.schema.nodes.keywords_section;
49
+ case 'MPSectionCategory:affiliations':
50
+ return schema_1.schema.nodes.affiliations_section;
51
+ case 'MPSectionCategory:contributors':
52
+ return schema_1.schema.nodes.contributors_section;
49
53
  case 'MPSectionCategory:toc':
50
54
  return schema_1.schema.nodes.toc_section;
51
55
  default:
@@ -92,6 +96,10 @@ const buildSectionCategory = (node) => {
92
96
  return 'MPSectionCategory:toc';
93
97
  case schema_1.schema.nodes.graphical_abstract_section:
94
98
  return 'MPSectionCategory:abstract-graphical';
99
+ case schema_1.schema.nodes.affiliations_section:
100
+ return 'MPSectionCategory:affiliations';
101
+ case schema_1.schema.nodes.contributors_section:
102
+ return 'MPSectionCategory:contributors';
95
103
  default:
96
104
  return node.attrs.category || undefined;
97
105
  }
@@ -185,6 +193,10 @@ const chooseSectionCategoryByType = (secType) => {
185
193
  return 'MPSectionCategory:backmatter';
186
194
  case 'abstracts':
187
195
  return 'MPSectionCategory:abstracts';
196
+ case 'affiliations':
197
+ return 'MPSectionCategory:affiliations';
198
+ case 'contributors':
199
+ return 'MPSectionCategory:contributors';
188
200
  default:
189
201
  return undefined;
190
202
  }
@@ -499,6 +499,132 @@ const nodes = [
499
499
  };
500
500
  },
501
501
  },
502
+ {
503
+ tag: 'sec[sec-type="affiliations"]',
504
+ node: 'affiliations_section',
505
+ getAttrs: (node) => {
506
+ const element = node;
507
+ return {
508
+ id: element.getAttribute('id'),
509
+ category: 'MPSectionCategory:affiliations',
510
+ };
511
+ },
512
+ },
513
+ {
514
+ tag: 'aff',
515
+ node: 'affiliation',
516
+ context: 'affiliations_section/',
517
+ getAttrs: (node) => {
518
+ const element = node;
519
+ const aff = {
520
+ id: element.getAttribute('id'),
521
+ };
522
+ const institution = element.getAttribute('institution');
523
+ if (institution) {
524
+ aff.institution = institution;
525
+ }
526
+ const email = element.getAttribute('email');
527
+ if (email) {
528
+ aff.bibliographicName = JSON.parse(email);
529
+ }
530
+ const department = element.getAttribute('department');
531
+ if (department) {
532
+ aff.department = department;
533
+ }
534
+ const addressLine1 = element.getAttribute('addressLine1');
535
+ if (addressLine1) {
536
+ aff.addressLine1 = addressLine1;
537
+ }
538
+ const addressLine2 = element.getAttribute('addressLine2');
539
+ if (addressLine2) {
540
+ aff.addressLine2 = addressLine2;
541
+ }
542
+ const addressLine3 = element.getAttribute('addressLine3');
543
+ if (addressLine3) {
544
+ aff.addressLine3 = addressLine3;
545
+ }
546
+ const postCode = element.getAttribute('postCode');
547
+ if (postCode) {
548
+ aff.postCode = postCode;
549
+ }
550
+ const country = element.getAttribute('country');
551
+ if (country) {
552
+ aff.country = country;
553
+ }
554
+ const priority = element.getAttribute('priority');
555
+ if (priority) {
556
+ aff.priority = parseInt(priority);
557
+ }
558
+ return aff;
559
+ },
560
+ },
561
+ {
562
+ tag: 'sec[sec-type="contributors"]',
563
+ node: 'contributors_section',
564
+ getAttrs: (node) => {
565
+ const element = node;
566
+ return {
567
+ id: element.getAttribute('id'),
568
+ category: 'MPSectionCategory:contributors',
569
+ };
570
+ },
571
+ },
572
+ {
573
+ tag: 'contrib',
574
+ node: 'contributor',
575
+ context: 'contributors_section/',
576
+ getAttrs: (node) => {
577
+ const element = node;
578
+ const contrib = {
579
+ id: element.getAttribute('id'),
580
+ };
581
+ const role = element.getAttribute('role');
582
+ if (role) {
583
+ contrib.role = role;
584
+ }
585
+ const affiliations = element.getAttribute('affiliations');
586
+ if (affiliations) {
587
+ contrib.affiliations = JSON.parse(affiliations);
588
+ }
589
+ const footnote = element.getAttribute('footnote');
590
+ if (footnote) {
591
+ contrib.footnote = JSON.parse(footnote);
592
+ }
593
+ const corresp = element.getAttribute('corresp');
594
+ if (corresp) {
595
+ contrib.corresp = JSON.parse(corresp);
596
+ }
597
+ const bibliographicName = element.getAttribute('bibliographicName');
598
+ if (bibliographicName) {
599
+ contrib.bibliographicName = JSON.parse(bibliographicName);
600
+ }
601
+ const userID = element.getAttribute('userID');
602
+ if (userID) {
603
+ contrib.userID = userID;
604
+ }
605
+ const priority = element.getAttribute('priority');
606
+ if (priority) {
607
+ contrib.priority = parseInt(priority);
608
+ }
609
+ const invitationID = element.getAttribute('invitationID');
610
+ if (invitationID) {
611
+ contrib.invitationID = invitationID;
612
+ }
613
+ const objectType = element.getAttribute('objectType');
614
+ if (objectType) {
615
+ contrib.objectType = objectType;
616
+ }
617
+ const isCorresponding = element.getAttribute('isCorresponding');
618
+ if (isCorresponding) {
619
+ contrib.isCorresponding = JSON.parse(isCorresponding);
620
+ }
621
+ const ORCIDIdentifier = element.getAttribute('ORCIDIdentifier');
622
+ if (ORCIDIdentifier) {
623
+ contrib.ORCIDIdentifier = ORCIDIdentifier;
624
+ }
625
+ return contrib;
626
+ },
627
+ },
502
628
  {
503
629
  tag: 'sec[sec-type="keywords"]',
504
630
  node: 'keywords_section',
@@ -281,6 +281,65 @@ export const jatsBodyTransformations = {
281
281
  }
282
282
  }
283
283
  },
284
+ moveAffiliationsToBody(doc, affiliations, body, createElement) {
285
+ if (affiliations === null || affiliations === void 0 ? void 0 : affiliations.length) {
286
+ const section = createElement('sec');
287
+ section.setAttribute('sec-type', 'affiliations');
288
+ affiliations.forEach((affiliation) => {
289
+ const item = doc.createElement('aff');
290
+ item.setAttribute('id', affiliation._id);
291
+ affiliation.institution &&
292
+ item.setAttribute('institution', affiliation.institution);
293
+ affiliation.email &&
294
+ item.setAttribute('email', JSON.stringify(affiliation.email));
295
+ affiliation.department &&
296
+ item.setAttribute('department', affiliation.department);
297
+ affiliation.addressLine1 &&
298
+ item.setAttribute('addressLine1', affiliation.addressLine1);
299
+ affiliation.addressLine2 &&
300
+ item.setAttribute('addressLine2', affiliation.addressLine2);
301
+ affiliation.addressLine3 &&
302
+ item.setAttribute('addressLine3', affiliation.addressLine3);
303
+ affiliation.postCode &&
304
+ item.setAttribute('postCode', affiliation.postCode);
305
+ affiliation.country && item.setAttribute('country', affiliation.country);
306
+ (affiliation.priority === 0 || affiliation.priority) &&
307
+ item.setAttribute('priority', affiliation.priority.toString());
308
+ section.appendChild(item);
309
+ });
310
+ body.prepend(section);
311
+ }
312
+ },
313
+ moveAuthorsToBody(doc, authors, body, createElement) {
314
+ if (authors === null || authors === void 0 ? void 0 : authors.length) {
315
+ const section = createElement('sec');
316
+ section.setAttribute('sec-type', 'contributors');
317
+ authors.forEach((author) => {
318
+ const item = doc.createElement('contrib');
319
+ item.setAttribute('id', author._id);
320
+ author.role && item.setAttribute('role', author.role);
321
+ author.affiliations &&
322
+ item.setAttribute('affiliations', JSON.stringify(author.affiliations));
323
+ author.footnote &&
324
+ item.setAttribute('footnote', JSON.stringify(author.footnote));
325
+ author.corresp &&
326
+ item.setAttribute('corresp', JSON.stringify(author.corresp));
327
+ author.bibliographicName &&
328
+ item.setAttribute('bibliographicName', JSON.stringify(author.bibliographicName));
329
+ author.userID && item.setAttribute('userID', author.userID);
330
+ author.invitationID &&
331
+ item.setAttribute('invitationID', author.invitationID);
332
+ author.isCorresponding &&
333
+ item.setAttribute('isCorresponding', JSON.stringify(author.isCorresponding));
334
+ author.ORCIDIdentifier &&
335
+ item.setAttribute('ORCIDIdentifier', author.ORCIDIdentifier);
336
+ (author.priority === 0 || author.priority) &&
337
+ item.setAttribute('priority', author.priority.toString());
338
+ section.appendChild(item);
339
+ });
340
+ body.prepend(section);
341
+ }
342
+ },
284
343
  moveFloatsGroupToBody(doc, body, createElement) {
285
344
  const floatsGroup = doc.querySelector('floats-group');
286
345
  if (floatsGroup) {
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import debug from 'debug';
17
17
  import { getTrimmedTextContent } from '../../lib/utils';
18
- import { buildAffiliation, buildBibliographicName, buildContributor, buildCorresp, buildFootnote, buildSupplementaryMaterial, } from '../../transformer/builders';
18
+ import { buildAffiliation, buildBibliographicName, buildContributor, buildCorresp, buildFootnote, buildSupplementaryMaterial, } from '../../transformer';
19
19
  import { parseJournalMeta } from './jats-journal-meta-parser';
20
20
  const warn = debug('manuscripts-transform');
21
21
  const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink';
@@ -16,6 +16,7 @@
16
16
  import { ObjectTypes, } from '@manuscripts/json-schema';
17
17
  import { generateID, hasObjectType, nodeTypesMap } from '../../transformer';
18
18
  const isAuxiliaryObjectReference = hasObjectType(ObjectTypes.AuxiliaryObjectReference);
19
+ const excludedObjectTypes = [ObjectTypes.Contributor, ObjectTypes.Affiliation];
19
20
  export function flatten(arrays) {
20
21
  return [].concat(...arrays);
21
22
  }
@@ -44,6 +45,9 @@ const addMissingID = (node, replacements, warnings) => {
44
45
  warnings.push(`Unknown object type for node type ${node.type.name}`);
45
46
  return;
46
47
  }
48
+ if (excludedObjectTypes.includes(objectType)) {
49
+ return;
50
+ }
47
51
  const previousID = node.attrs.id;
48
52
  const nextID = generateID(objectType);
49
53
  if (previousID) {
@@ -71,13 +71,13 @@ export const parseJATSFront = async (front) => {
71
71
  return {
72
72
  models: generateModelIDs([
73
73
  manuscript,
74
- ...affiliations,
75
- ...authors,
76
74
  ...footnotes,
77
75
  ...correspondingList,
78
76
  journal,
79
77
  ...supplements,
80
78
  ]),
79
+ authors: [...authors],
80
+ affiliations: [...affiliations],
81
81
  };
82
82
  };
83
83
  export const parseJATSReferences = (back, body, createElement) => {
@@ -101,7 +101,7 @@ export const parseJATSReferences = (back, body, createElement) => {
101
101
  referenceIdsMap,
102
102
  };
103
103
  };
104
- export const parseJATSBody = (document, body, bibliographyItems, refModels, referenceIdsMap, footnotesOrder) => {
104
+ export const parseJATSBody = (document, body, bibliographyItems, authors, affiliations, refModels, referenceIdsMap, footnotesOrder) => {
105
105
  const createElement = createElementFn(document);
106
106
  const orderedFootnotesIDs = createOrderedFootnotesIDs(document);
107
107
  jatsBodyTransformations.moveFloatsGroupToBody(document, body, createElement);
@@ -111,6 +111,8 @@ export const parseJATSBody = (document, body, bibliographyItems, refModels, refe
111
111
  jatsBodyTransformations.moveTableFooterToEnd(body);
112
112
  jatsBodyTransformations.moveBlockNodesFromParagraph(document, body, createElement);
113
113
  jatsBodyTransformations.moveKeywordsToBody(document, body, createElement);
114
+ jatsBodyTransformations.moveAffiliationsToBody(document, affiliations, body, createElement);
115
+ jatsBodyTransformations.moveAuthorsToBody(document, authors, body, createElement);
114
116
  const node = jatsBodyDOMParser.parse(body);
115
117
  if (!node.firstChild) {
116
118
  throw new Error('No content was parsed from the JATS article body');
@@ -162,14 +164,14 @@ export const parseJATSArticle = async (doc) => {
162
164
  }
163
165
  const authorQueriesMap = markProcessingInstructions(doc);
164
166
  const createElement = createElementFn(document);
165
- const { models: frontModels } = await parseJATSFront(front);
167
+ const { models: frontModels, authors, affiliations, } = await parseJATSFront(front);
166
168
  const { references, crossReferences, referenceQueriesMap, referenceIdsMap } = parseJATSReferences(back, body, createElement);
167
169
  transformTables(doc.querySelectorAll('table-wrap > table'), createElement);
168
170
  const footnotesOrder = createEmptyFootnotesOrder();
169
171
  let elementsOrder = [];
170
172
  const bodyModels = [];
171
173
  if (body) {
172
- const bodyDoc = parseJATSBody(doc, body, references, crossReferences, referenceIdsMap, footnotesOrder);
174
+ const bodyDoc = parseJATSBody(doc, body, references, authors, affiliations, crossReferences, referenceIdsMap, footnotesOrder);
173
175
  bodyModels.push(...encode(bodyDoc).values());
174
176
  elementsOrder = getElementsOrder(bodyDoc);
175
177
  }
@@ -591,10 +591,10 @@ export class JATSExporter {
591
591
  this.createSerializer = () => {
592
592
  const getModel = (id) => id ? this.modelMap.get(id) : undefined;
593
593
  const nodes = {
594
+ affiliations_section: () => '',
595
+ contributors_section: () => '',
594
596
  table_element_footer: () => ['table-wrap-foot', 0],
595
- contributor_list: () => '',
596
597
  contributor: () => '',
597
- affiliation_list: () => '',
598
598
  affiliation: () => '',
599
599
  meta_section: () => '',
600
600
  attribution: () => ['attrib', 0],
@@ -13,7 +13,7 @@ export const coreSectionCategories = [
13
13
  desc: 'Backmatter section for grouping',
14
14
  objectType: 'MPSectionCategory',
15
15
  titles: [],
16
- priority: 170,
16
+ priority: 180,
17
17
  },
18
18
  {
19
19
  _id: 'MPSectionCategory:body',
@@ -21,6 +21,6 @@ export const coreSectionCategories = [
21
21
  desc: 'Body section for grouping',
22
22
  objectType: 'MPSectionCategory',
23
23
  titles: [],
24
- priority: 190,
24
+ priority: 200,
25
25
  },
26
26
  ];
@@ -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 { affiliationList } from './nodes/affiliation_list';
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,7 +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 { contributorList } from './nodes/contributor_list';
31
+ import { contributorsSection } from './nodes/contributors_section';
32
32
  import { crossReference } from './nodes/cross_reference';
33
33
  import { doc } from './nodes/doc';
34
34
  import { equation } from './nodes/equation';
@@ -120,11 +120,11 @@ export * from './nodes/text';
120
120
  export * from './nodes/toc_element';
121
121
  export * from './nodes/toc_section';
122
122
  export * from './nodes/affiliation';
123
- export * from './nodes/affiliation_list';
124
123
  export * from './nodes/meta_section';
125
- export * from './nodes/contributor_list';
126
124
  export * from './nodes/contributor';
127
125
  export * from './nodes/table_element_footer';
126
+ export * from './nodes/affiliations_section';
127
+ export * from './nodes/contributors_section';
128
128
  export const schema = new Schema({
129
129
  marks: {
130
130
  bold,
@@ -197,9 +197,9 @@ export const schema = new Schema({
197
197
  toc_section: tocSection,
198
198
  affiliation,
199
199
  meta_section: metaSection,
200
- affiliation_list: affiliationList,
201
- contributor_list: contributorList,
202
200
  contributor: contributor,
203
201
  table_element_footer: tableElementFooter,
202
+ affiliations_section: affiliationsSection,
203
+ contributors_section: contributorsSection,
204
204
  },
205
205
  });
@@ -8,6 +8,7 @@ export const affiliation = {
8
8
  addressLine3: { default: '' },
9
9
  postCode: { default: '' },
10
10
  country: { default: '' },
11
+ priority: { default: undefined },
11
12
  email: {
12
13
  default: {
13
14
  href: undefined,
@@ -15,5 +16,7 @@ export const affiliation = {
15
16
  },
16
17
  },
17
18
  },
19
+ group: 'block element',
20
+ toDOM: () => ['span'],
18
21
  };
19
22
  export const isAffiliationNode = (node) => node.type === node.type.schema.nodes.affiliation;
@@ -0,0 +1,11 @@
1
+ export const affiliationsSection = {
2
+ content: 'section_title? affiliation*',
3
+ attrs: {
4
+ id: { default: '' },
5
+ dataTracked: { default: null },
6
+ },
7
+ group: 'block sections',
8
+ selectable: false,
9
+ toDOM: () => ['section', 0],
10
+ };
11
+ export const isAffiliationsSectionNode = (node) => node.type === node.type.schema.nodes.affiliations_section;
@@ -3,10 +3,16 @@ export const contributor = {
3
3
  id: { default: '' },
4
4
  role: { default: '' },
5
5
  affiliations: { default: [] },
6
+ footnote: { default: undefined },
7
+ corresp: { default: undefined },
6
8
  bibliographicName: { default: {} },
7
9
  userID: { default: undefined },
8
10
  invitationID: { default: undefined },
9
11
  isCorresponding: { default: undefined },
10
12
  ORCIDIdentifier: { default: undefined },
13
+ priority: { default: undefined },
11
14
  },
15
+ group: 'block element',
16
+ toDOM: () => ['span'],
12
17
  };
18
+ export const isContributorNode = (node) => node.type === node.type.schema.nodes.contributor;
@@ -0,0 +1,11 @@
1
+ export const contributorsSection = {
2
+ content: 'section_title? contributor*',
3
+ attrs: {
4
+ id: { default: '' },
5
+ dataTracked: { default: null },
6
+ },
7
+ group: 'block sections',
8
+ selectable: false,
9
+ toDOM: () => ['section', 0],
10
+ };
11
+ export const isContributorsSectionNode = (node) => node.type === node.type.schema.nodes.contributors_section;
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  export const metaSection = {
17
- content: 'affiliation_list contributor_list comment_list',
17
+ content: 'comment_list',
18
18
  attrs: {
19
19
  id: { default: 'META_SECTION' },
20
20
  },
@@ -57,29 +57,16 @@ export const isManuscriptNode = (model) => model !== null;
57
57
  const isParagraphElement = hasObjectType(ObjectTypes.ParagraphElement);
58
58
  const isFootnote = hasObjectType(ObjectTypes.Footnote);
59
59
  const isKeyword = hasObjectType(ObjectTypes.Keyword);
60
+ const isKeywordsSection = (model) => model.category === 'MPSectionCategory:keywords';
61
+ const isAffiliationsSection = (model) => model.category === 'MPSectionCategory:affiliations';
62
+ const isContributorsSection = (model) => model.category === 'MPSectionCategory:contributors';
60
63
  const hasParentSection = (id) => (section) => section.path &&
61
64
  section.path.length > 1 &&
62
65
  section.path[section.path.length - 2] === id;
63
66
  export class Decoder {
64
- createAffiliationListNode() {
65
- const affiliationNodes = getAffiliations(this.modelMap)
66
- .map((affiliation) => this.decode(affiliation))
67
- .filter(Boolean);
68
- return schema.nodes.affiliation_list.createAndFill({}, affiliationNodes);
69
- }
70
- createContributorListNode() {
71
- const contributorNodes = getContributors(this.modelMap)
72
- .map((contributor) => this.decode(contributor))
73
- .filter(Boolean);
74
- return schema.nodes.contributor_list.createAndFill({}, contributorNodes);
75
- }
76
67
  createMetaSectionNode() {
77
- const affiliationListNode = this.createAffiliationListNode();
78
- const contributorListNode = this.createContributorListNode();
79
68
  const commentListNode = this.createCommentListNode();
80
69
  return schema.nodes.meta_section.createAndFill({}, [
81
- affiliationListNode,
82
- contributorListNode,
83
70
  commentListNode,
84
71
  ]);
85
72
  }
@@ -88,12 +75,43 @@ export class Decoder {
88
75
  ...this.comments.values(),
89
76
  ]);
90
77
  }
78
+ handleMissingRootSectionNodes(rootSectionNodes) {
79
+ if (!rootSectionNodes.find((node) => node.type.name === 'affiliations_section')) {
80
+ this.createAffiliationSectionNode(rootSectionNodes);
81
+ }
82
+ if (!rootSectionNodes.find((node) => node.type.name === 'contributors_section')) {
83
+ this.createContributorSectionNode(rootSectionNodes);
84
+ }
85
+ }
86
+ createAffiliationSectionNode(rootSectionNodes) {
87
+ const affiliationNodes = getAffiliations(this.modelMap)
88
+ .map((affiliation) => this.decode(affiliation))
89
+ .filter(Boolean);
90
+ if (affiliationNodes.length) {
91
+ const node = schema.nodes.affiliations_section.createAndFill({
92
+ id: generateNodeID(schema.nodes.section),
93
+ }, affiliationNodes);
94
+ rootSectionNodes.unshift(node);
95
+ }
96
+ }
97
+ createContributorSectionNode(rootSectionNodes) {
98
+ const contributorNodes = getContributors(this.modelMap)
99
+ .map((contributor) => this.decode(contributor))
100
+ .filter(Boolean);
101
+ if (contributorNodes.length) {
102
+ const node = schema.nodes.contributors_section.createAndFill({
103
+ id: generateNodeID(schema.nodes.section),
104
+ }, contributorNodes);
105
+ rootSectionNodes.unshift(node);
106
+ }
107
+ }
91
108
  createRootSectionNodes() {
92
109
  let rootSections = getSections(this.modelMap).filter((section) => !section.path || section.path.length <= 1);
93
110
  rootSections = this.addGeneratedLabels(rootSections);
94
111
  const rootSectionNodes = rootSections
95
112
  .map(this.decode)
96
113
  .filter(isManuscriptNode);
114
+ this.handleMissingRootSectionNodes(rootSectionNodes);
97
115
  if (!rootSectionNodes.length) {
98
116
  rootSectionNodes.push(schema.nodes.section.createAndFill({
99
117
  id: generateNodeID(schema.nodes.section),
@@ -426,13 +444,12 @@ export class Decoder {
426
444
  },
427
445
  [ObjectTypes.Section]: (data) => {
428
446
  const model = data;
429
- const isKeywordsSection = model.category === 'MPSectionCategory:keywords';
430
447
  const elements = [];
431
448
  if (model.elementIDs) {
432
449
  for (const id of model.elementIDs) {
433
450
  const element = this.getModel(id);
434
451
  if (element) {
435
- if (isKeywordsSection && isParagraphElement(element)) {
452
+ if (isKeywordsSection(model) && isParagraphElement(element)) {
436
453
  continue;
437
454
  }
438
455
  elements.push(element);
@@ -475,11 +492,17 @@ export class Decoder {
475
492
  const sectionNodeType = chooseSectionNodeType(sectionCategory);
476
493
  const commentNodes = this.createCommentsNode(model);
477
494
  commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
478
- const content = (sectionLabelNode
479
- ? [sectionLabelNode, sectionTitleNode]
480
- : [sectionTitleNode])
481
- .concat(elementNodes)
482
- .concat(nestedSections);
495
+ let content;
496
+ if (isAffiliationsSection(model) || isContributorsSection(model)) {
497
+ content = elementNodes.concat(nestedSections);
498
+ }
499
+ else {
500
+ content = (sectionLabelNode
501
+ ? [sectionLabelNode, sectionTitleNode]
502
+ : [sectionTitleNode])
503
+ .concat(elementNodes)
504
+ .concat(nestedSections);
505
+ }
483
506
  const sectionNode = sectionNodeType.createAndFill({
484
507
  id: model._id,
485
508
  category: sectionCategory,
@@ -568,6 +591,8 @@ export class Decoder {
568
591
  postCode: model.postCode,
569
592
  country: model.country,
570
593
  email: model.email,
594
+ department: model.department,
595
+ priority: model.priority,
571
596
  });
572
597
  },
573
598
  [ObjectTypes.Contributor]: (data) => {
@@ -581,6 +606,9 @@ export class Decoder {
581
606
  invitationID: model.invitationID,
582
607
  isCorresponding: model.isCorresponding,
583
608
  ORCIDIdentifier: model.ORCIDIdentifier,
609
+ footnote: model.footnote,
610
+ corresp: model.corresp,
611
+ priority: model.priority,
584
612
  });
585
613
  },
586
614
  };
@@ -426,6 +426,22 @@ const encoders = {
426
426
  .map((childNode) => childNode.attrs.id)
427
427
  .filter((id) => id),
428
428
  }),
429
+ affiliations_section: (node, parent, path, priority) => ({
430
+ category: buildSectionCategory(node),
431
+ priority: priority.value++,
432
+ path: path.concat([node.attrs.id]),
433
+ elementIDs: childElements(node)
434
+ .map((childNode) => childNode.attrs.id)
435
+ .filter((id) => id),
436
+ }),
437
+ contributors_section: (node, parent, path, priority) => ({
438
+ category: buildSectionCategory(node),
439
+ priority: priority.value++,
440
+ path: path.concat([node.attrs.id]),
441
+ elementIDs: childElements(node)
442
+ .map((childNode) => childNode.attrs.id)
443
+ .filter((id) => id),
444
+ }),
429
445
  missing_figure: (node) => ({
430
446
  position: node.attrs.position || undefined,
431
447
  }),
@@ -506,6 +522,7 @@ const encoders = {
506
522
  postCode: node.attrs.postCode,
507
523
  country: node.attrs.country,
508
524
  email: node.attrs.email,
525
+ priority: node.attrs.priority,
509
526
  }),
510
527
  contributor: (node) => ({
511
528
  role: node.attrs.role,
@@ -515,6 +532,9 @@ const encoders = {
515
532
  invitationID: node.attrs.invitationID,
516
533
  isCorresponding: node.attrs.isCorresponding,
517
534
  ORCIDIdentifier: node.attrs.ORCIDIdentifier,
535
+ footnote: node.attrs.footnote,
536
+ corresp: node.attrs.corresp,
537
+ priority: node.attrs.priority,
518
538
  }),
519
539
  };
520
540
  const modelData = (node, parent, path, priority) => {
@@ -54,6 +54,8 @@ export const nodeTypesMap = new Map([
54
54
  [schema.nodes.affiliation, ObjectTypes.Affiliation],
55
55
  [schema.nodes.contributor, ObjectTypes.Contributor],
56
56
  [schema.nodes.table_element_footer, ObjectTypes.TableElementFooter],
57
+ [schema.nodes.contributors_section, ObjectTypes.Section],
58
+ [schema.nodes.affiliations_section, ObjectTypes.Section],
57
59
  ]);
58
60
  export const isExecutableNodeType = (type) => hasGroup(type, GROUP_EXECUTABLE);
59
61
  export const isElementNodeType = (type) => hasGroup(type, GROUP_ELEMENT);