@manuscripts/transform 1.4.6 → 1.5.0-LEAN-3014-B-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 (46) 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 +4 -2
  7. package/dist/cjs/lib/core-section-categories.js +2 -2
  8. package/dist/cjs/schema/index.js +8 -4
  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 +4 -2
  24. package/dist/es/lib/core-section-categories.js +2 -2
  25. package/dist/es/schema/index.js +8 -4
  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
@@ -434,6 +434,22 @@ const encoders = {
434
434
  .map((childNode) => childNode.attrs.id)
435
435
  .filter((id) => id),
436
436
  }),
437
+ affiliations_section: (node, parent, path, priority) => ({
438
+ category: (0, section_category_1.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
+ }),
445
+ contributors_section: (node, parent, path, priority) => ({
446
+ category: (0, section_category_1.buildSectionCategory)(node),
447
+ priority: priority.value++,
448
+ path: path.concat([node.attrs.id]),
449
+ elementIDs: childElements(node)
450
+ .map((childNode) => childNode.attrs.id)
451
+ .filter((id) => id),
452
+ }),
437
453
  missing_figure: (node) => ({
438
454
  position: node.attrs.position || undefined,
439
455
  }),
@@ -514,6 +530,7 @@ const encoders = {
514
530
  postCode: node.attrs.postCode,
515
531
  country: node.attrs.country,
516
532
  email: node.attrs.email,
533
+ priority: node.attrs.priority,
517
534
  }),
518
535
  contributor: (node) => ({
519
536
  role: node.attrs.role,
@@ -523,6 +540,9 @@ const encoders = {
523
540
  invitationID: node.attrs.invitationID,
524
541
  isCorresponding: node.attrs.isCorresponding,
525
542
  ORCIDIdentifier: node.attrs.ORCIDIdentifier,
543
+ footnote: node.attrs.footnote,
544
+ corresp: node.attrs.corresp,
545
+ priority: node.attrs.priority,
526
546
  }),
527
547
  };
528
548
  const modelData = (node, parent, path, priority) => {
@@ -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,12 @@ export class JATSExporter {
591
591
  this.createSerializer = () => {
592
592
  const getModel = (id) => id ? this.modelMap.get(id) : undefined;
593
593
  const nodes = {
594
- table_element_footer: () => ['table-wrap-foot', 0],
594
+ affiliation_list: () => '',
595
595
  contributor_list: () => '',
596
+ affiliations_section: () => '',
597
+ contributors_section: () => '',
598
+ table_element_footer: () => ['table-wrap-foot', 0],
596
599
  contributor: () => '',
597
- affiliation_list: () => '',
598
600
  affiliation: () => '',
599
601
  meta_section: () => '',
600
602
  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
  ];
@@ -17,6 +17,7 @@ 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
19
  import { affiliationList } from './nodes/affiliation_list';
20
+ import { affiliationsSection } from './nodes/affiliations_section';
20
21
  import { attribution } from './nodes/attribution';
21
22
  import { bibliographyElement } from './nodes/bibliography_element';
22
23
  import { bibliographyItem } from './nodes/bibliography_item';
@@ -29,6 +30,7 @@ import { comment } from './nodes/comment';
29
30
  import { commentList } from './nodes/comment_list';
30
31
  import { contributor } from './nodes/contributor';
31
32
  import { contributorList } from './nodes/contributor_list';
33
+ import { contributorsSection } from './nodes/contributors_section';
32
34
  import { crossReference } from './nodes/cross_reference';
33
35
  import { doc } from './nodes/doc';
34
36
  import { equation } from './nodes/equation';
@@ -120,11 +122,11 @@ export * from './nodes/text';
120
122
  export * from './nodes/toc_element';
121
123
  export * from './nodes/toc_section';
122
124
  export * from './nodes/affiliation';
123
- export * from './nodes/affiliation_list';
124
125
  export * from './nodes/meta_section';
125
- export * from './nodes/contributor_list';
126
126
  export * from './nodes/contributor';
127
127
  export * from './nodes/table_element_footer';
128
+ export * from './nodes/affiliations_section';
129
+ export * from './nodes/contributors_section';
128
130
  export const schema = new Schema({
129
131
  marks: {
130
132
  bold,
@@ -197,9 +199,11 @@ export const schema = new Schema({
197
199
  toc_section: tocSection,
198
200
  affiliation,
199
201
  meta_section: metaSection,
200
- affiliation_list: affiliationList,
201
- contributor_list: contributorList,
202
202
  contributor: contributor,
203
203
  table_element_footer: tableElementFooter,
204
+ affiliations_section: affiliationsSection,
205
+ contributors_section: contributorsSection,
206
+ affiliation_list: affiliationList,
207
+ contributor_list: contributorList,
204
208
  },
205
209
  });
@@ -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
  };