@manuscripts/transform 1.2.4-LEAN-2357 → 1.2.4-LEAN-2066

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 +33 -1
  2. package/dist/cjs/jats/importer/jats-body-transformations.js +26 -53
  3. package/dist/cjs/jats/importer/jats-comments.js +24 -0
  4. package/dist/cjs/jats/importer/jats-front-parser.js +0 -25
  5. package/dist/cjs/jats/importer/parse-jats-article.js +3 -6
  6. package/dist/cjs/jats/jats-exporter.js +19 -54
  7. package/dist/cjs/lib/utils.js +12 -1
  8. package/dist/cjs/schema/index.js +2 -0
  9. package/dist/cjs/schema/nodes/keyword.js +1 -2
  10. package/dist/cjs/schema/nodes/keywords_element.js +2 -1
  11. package/dist/cjs/schema/nodes/keywords_group.js +48 -0
  12. package/dist/cjs/schema/nodes/keywords_section.js +1 -1
  13. package/dist/cjs/schema/nodes/section.js +1 -1
  14. package/dist/cjs/transformer/decode.js +29 -23
  15. package/dist/cjs/transformer/encode.js +9 -13
  16. package/dist/cjs/transformer/node-types.js +1 -0
  17. package/dist/cjs/transformer/object-types.js +2 -1
  18. package/dist/cjs/transformer/section-category.js +1 -23
  19. package/dist/es/jats/importer/jats-body-dom-parser.js +33 -1
  20. package/dist/es/jats/importer/jats-body-transformations.js +27 -54
  21. package/dist/es/jats/importer/jats-comments.js +25 -1
  22. package/dist/es/jats/importer/jats-front-parser.js +1 -26
  23. package/dist/es/jats/importer/parse-jats-article.js +3 -6
  24. package/dist/es/jats/jats-exporter.js +19 -54
  25. package/dist/es/lib/utils.js +10 -0
  26. package/dist/es/schema/index.js +2 -0
  27. package/dist/es/schema/nodes/keyword.js +1 -2
  28. package/dist/es/schema/nodes/keywords_element.js +2 -1
  29. package/dist/es/schema/nodes/keywords_group.js +44 -0
  30. package/dist/es/schema/nodes/keywords_section.js +1 -1
  31. package/dist/es/schema/nodes/section.js +1 -1
  32. package/dist/es/transformer/decode.js +29 -23
  33. package/dist/es/transformer/encode.js +10 -14
  34. package/dist/es/transformer/node-types.js +1 -0
  35. package/dist/es/transformer/object-types.js +1 -0
  36. package/dist/es/transformer/section-category.js +1 -19
  37. package/dist/types/jats/importer/jats-body-transformations.d.ts +2 -7
  38. package/dist/types/jats/importer/jats-front-parser.d.ts +6 -11
  39. package/dist/types/jats/jats-exporter.d.ts +0 -2
  40. package/dist/types/lib/utils.d.ts +1 -0
  41. package/dist/types/schema/nodes/keywords_group.d.ts +26 -0
  42. package/dist/types/schema/types.d.ts +1 -1
  43. package/dist/types/transformer/decode.d.ts +1 -0
  44. package/dist/types/transformer/object-types.d.ts +2 -1
  45. package/dist/types/transformer/section-category.d.ts +2 -3
  46. package/package.json +2 -2
@@ -505,6 +505,22 @@ const nodes = [
505
505
  };
506
506
  },
507
507
  },
508
+ {
509
+ tag: 'sec[sec-type="keywords"]',
510
+ node: 'keywords_section',
511
+ getAttrs: (node) => {
512
+ const element = node;
513
+ return {
514
+ id: element.getAttribute('id'),
515
+ category: (0, transformer_1.chooseSectionCategory)(element),
516
+ };
517
+ },
518
+ },
519
+ {
520
+ tag: 'kwd-group-list',
521
+ context: 'keywords_section/',
522
+ node: 'keywords_element',
523
+ },
508
524
  {
509
525
  tag: 'sec',
510
526
  node: 'section',
@@ -516,6 +532,22 @@ const nodes = [
516
532
  };
517
533
  },
518
534
  },
535
+ {
536
+ tag: 'kwd-group',
537
+ context: 'keywords_element/',
538
+ node: 'keywords_group',
539
+ getAttrs: (node) => {
540
+ const element = node;
541
+ return {
542
+ type: element.getAttribute('kwd-group-type'),
543
+ };
544
+ },
545
+ },
546
+ {
547
+ tag: 'kwd',
548
+ context: 'keywords_group//',
549
+ node: 'keyword',
550
+ },
519
551
  {
520
552
  tag: 'label',
521
553
  context: 'section/',
@@ -568,7 +600,7 @@ const nodes = [
568
600
  {
569
601
  tag: 'title',
570
602
  node: 'section_title',
571
- context: 'section/|footnotes_section/|bibliography_section/',
603
+ context: 'section/|footnotes_section/|bibliography_section/|keywords_section/',
572
604
  },
573
605
  {
574
606
  tag: 'title',
@@ -19,17 +19,6 @@ exports.jatsBodyTransformations = void 0;
19
19
  const transformer_1 = require("../../transformer");
20
20
  const removeNodeFromParent = (node) => node.parentNode && node.parentNode.removeChild(node);
21
21
  const capitalizeFirstLetter = (str) => str.charAt(0).toUpperCase() + str.slice(1);
22
- const createSectionContainer = (type, createElement) => {
23
- const sectionContainer = createElement('sec');
24
- const sectionCategory = (0, transformer_1.chooseSectionCategoryByType)(type);
25
- sectionContainer.setAttribute('sec-type', sectionCategory ? (0, transformer_1.chooseSecType)(sectionCategory) : '');
26
- const title = createElement('title');
27
- title.textContent = sectionCategory
28
- ? (0, transformer_1.getSectionTitles)(sectionCategory)[0]
29
- : ' ';
30
- sectionContainer.appendChild(title);
31
- return sectionContainer;
32
- };
33
22
  exports.jatsBodyTransformations = {
34
23
  ensureSection(body, createElement) {
35
24
  let section = createElement('sec');
@@ -157,64 +146,34 @@ exports.jatsBodyTransformations = {
157
146
  section.append(...floatsGroup.children);
158
147
  return section;
159
148
  },
160
- moveAbstractsIntoContainer(doc, abstractsContainer, createElement) {
149
+ moveSectionsToBody(doc, body, references, createElement) {
161
150
  const abstractNodes = doc.querySelectorAll('front > article-meta > abstract');
162
- abstractNodes.forEach((abstractNode) => {
151
+ for (const abstractNode of abstractNodes) {
163
152
  const abstract = this.createAbstract(abstractNode, createElement);
164
153
  removeNodeFromParent(abstractNode);
165
- abstractsContainer.appendChild(abstract);
166
- });
167
- },
168
- wrapBodySections(doc, bodyContainer) {
169
- const bodySections = doc.querySelectorAll('body > sec:not([sec-type="backmatter"]), body > sec:not([sec-type])');
170
- bodySections.forEach((section) => {
171
- removeNodeFromParent(section);
172
- bodyContainer.appendChild(section);
173
- });
174
- },
175
- moveBackSectionsIntoContainer(doc, backmatterContainer) {
154
+ body.insertBefore(abstract, body.firstChild);
155
+ }
176
156
  for (const section of doc.querySelectorAll('back > sec')) {
177
157
  removeNodeFromParent(section);
178
- backmatterContainer.appendChild(section);
158
+ body.appendChild(section);
179
159
  }
180
- },
181
- moveAcknowledgmentsIntoContainer(doc, backmatterContainer, createElement) {
182
160
  const ackNode = doc.querySelector('back > ack');
183
161
  if (ackNode) {
184
162
  const acknowledgements = this.createAcknowledgments(ackNode, createElement);
185
163
  removeNodeFromParent(ackNode);
186
- backmatterContainer.appendChild(acknowledgements);
164
+ body.appendChild(acknowledgements);
187
165
  }
188
- },
189
- moveAppendicesIntoContainer(doc, backmatterContainer, createElement) {
190
166
  const appGroup = doc.querySelectorAll('back > app-group > app');
191
167
  for (const app of appGroup) {
192
168
  const appendix = this.createAppendixSection(app, createElement);
193
169
  removeNodeFromParent(app);
194
- backmatterContainer.appendChild(appendix);
170
+ body.appendChild(appendix);
195
171
  }
196
- },
197
- moveBibliographyIntoContainer(doc, backmatterContainer, references, createElement) {
198
172
  if (references) {
199
- backmatterContainer.appendChild(this.createBibliography(doc, references, createElement));
173
+ body.appendChild(this.createBibliography(doc, references, createElement));
200
174
  }
201
175
  },
202
- moveSectionsToBody(doc, body, references, createElement) {
203
- const bodyContainer = createSectionContainer('body', createElement);
204
- const abstractsContainer = createSectionContainer('abstracts', createElement);
205
- const backmatterContainer = createSectionContainer('backmatter', createElement);
206
- this.mapFootnotesToSections(doc, backmatterContainer, createElement);
207
- this.wrapBodySections(doc, bodyContainer);
208
- this.moveAbstractsIntoContainer(doc, abstractsContainer, createElement);
209
- this.moveBackSectionsIntoContainer(doc, backmatterContainer);
210
- this.moveAcknowledgmentsIntoContainer(doc, backmatterContainer, createElement);
211
- this.moveAppendicesIntoContainer(doc, backmatterContainer, createElement);
212
- this.moveBibliographyIntoContainer(doc, backmatterContainer, references, createElement);
213
- body.insertBefore(abstractsContainer, body.firstChild);
214
- body.insertBefore(bodyContainer, abstractsContainer.nextSibling);
215
- body.append(backmatterContainer);
216
- },
217
- mapFootnotesToSections(doc, backmatterContainer, createElement) {
176
+ mapFootnotesToSections(doc, body, createElement) {
218
177
  const footnoteGroups = [...doc.querySelectorAll('fn[fn-type]')];
219
178
  for (const footnote of footnoteGroups) {
220
179
  const type = footnote.getAttribute('fn-type') || '';
@@ -231,7 +190,7 @@ exports.jatsBodyTransformations = {
231
190
  section.append(...footnote.children);
232
191
  removeNodeFromParent(footnote);
233
192
  section.setAttribute('sec-type', (0, transformer_1.chooseSecType)(category));
234
- backmatterContainer.append(section);
193
+ body.append(section);
235
194
  }
236
195
  }
237
196
  const footnotes = [...doc.querySelectorAll('fn')];
@@ -246,7 +205,7 @@ exports.jatsBodyTransformations = {
246
205
  }
247
206
  if (!footnotesSection && containingGroup.innerHTML) {
248
207
  const section = this.createFootnotes([containingGroup], createElement);
249
- backmatterContainer.append(section);
208
+ body.append(section);
250
209
  }
251
210
  let regularFootnoteGroups = [
252
211
  ...doc.querySelectorAll('back > fn-group:not([fn-type])'),
@@ -259,7 +218,7 @@ exports.jatsBodyTransformations = {
259
218
  if (regularFootnoteGroups.length > 0) {
260
219
  regularFootnoteGroups.map((g) => removeNodeFromParent(g));
261
220
  const footnotes = this.createFootnotes(regularFootnoteGroups, createElement);
262
- backmatterContainer.appendChild(footnotes);
221
+ body.appendChild(footnotes);
263
222
  }
264
223
  },
265
224
  moveCaptionsToEnd(body) {
@@ -307,4 +266,18 @@ exports.jatsBodyTransformations = {
307
266
  paragraph === null || paragraph === void 0 ? void 0 : paragraph.replaceWith(parent);
308
267
  });
309
268
  },
269
+ moveKeywordsToBody(document, body, createElement) {
270
+ const keywordGroups = [...document.querySelectorAll('kwd-group')];
271
+ if (keywordGroups.length > 0) {
272
+ const section = createElement('sec');
273
+ section.setAttribute('sec-type', 'keywords');
274
+ const title = createElement('title');
275
+ title.textContent = 'Keywords';
276
+ section.append(title);
277
+ const kwdGroupsEl = createElement('kwd-group-list');
278
+ kwdGroupsEl.append(keywordGroups[0]);
279
+ section.append(kwdGroupsEl);
280
+ body.prepend(section);
281
+ }
282
+ },
310
283
  };
@@ -63,6 +63,21 @@ const insertToken = (doc, processingInstructionNode, authorQueriesMap) => {
63
63
  return parentElement.insertBefore(tokenNode, processingInstructionNode);
64
64
  }
65
65
  };
66
+ const extractCommentsFromKeywords = (tokens, model, authorQueriesMap) => {
67
+ var _a;
68
+ const commentAnnotations = [];
69
+ const name = model.name;
70
+ const filteredTokens = filterAndSortTokens(tokens, name);
71
+ let content = name;
72
+ for (const token of filteredTokens) {
73
+ content = name.replace(token, '');
74
+ const query = authorQueriesMap.get(token);
75
+ const commentAnnotation = (0, transformer_1.buildComment)((_a = model.containedGroup) !== null && _a !== void 0 ? _a : (0, uuid_1.v4)(), `${query}`, undefined, [(0, transformer_1.buildContribution)(DEFAULT_PROFILE_ID)], DEFAULT_ANNOTATION_COLOR);
76
+ model['name'] = content;
77
+ commentAnnotations.push(commentAnnotation);
78
+ }
79
+ return commentAnnotations;
80
+ };
66
81
  const createComments = (authorQueriesMap, manuscriptModels) => {
67
82
  const tokens = [...authorQueriesMap.keys()];
68
83
  const commentAnnotations = [];
@@ -71,10 +86,19 @@ const createComments = (authorQueriesMap, manuscriptModels) => {
71
86
  const comments = addCommentsFromMarkedProcessingInstructions(tokens, model, authorQueriesMap);
72
87
  commentAnnotations.push(...comments);
73
88
  }
89
+ else if ((0, transformer_1.isKeyword)(model)) {
90
+ const comments = extractCommentsFromKeywords(tokens, model, authorQueriesMap);
91
+ commentAnnotations.push(...comments);
92
+ }
74
93
  }
75
94
  return commentAnnotations;
76
95
  };
77
96
  exports.createComments = createComments;
97
+ function filterAndSortTokens(tokens, content) {
98
+ return tokens
99
+ .filter((token) => content.indexOf(token) >= 0)
100
+ .sort((a, b) => content.indexOf(a) - content.indexOf(b));
101
+ }
78
102
  const addCommentsFromMarkedProcessingInstructions = (tokens, model, authorQueriesMap) => {
79
103
  const commentAnnotations = [];
80
104
  for (const field of ['contents', 'caption', 'title']) {
@@ -68,31 +68,6 @@ exports.jatsFrontParser = {
68
68
  }
69
69
  return (0, jats_journal_meta_parser_1.parseJournalMeta)(journalMeta);
70
70
  },
71
- parseKeywords(keywordGroupNodes) {
72
- var _a, _b;
73
- if (!keywordGroupNodes) {
74
- return { groups: [], keywords: [] };
75
- }
76
- let keywordPriority = 1;
77
- const keywordGroups = { groups: [], keywords: [] };
78
- for (const keywordGroupNode of keywordGroupNodes) {
79
- const manuscriptKeywordGroup = (0, builders_1.buildKeywordGroup)({
80
- title: ((_a = keywordGroupNode.querySelector('title')) === null || _a === void 0 ? void 0 : _a.textContent) || undefined,
81
- label: ((_b = keywordGroupNode.querySelector('label')) === null || _b === void 0 ? void 0 : _b.textContent) || undefined,
82
- type: keywordGroupNode.getAttribute('kwd-group-type') || undefined,
83
- });
84
- keywordGroups.groups.push(manuscriptKeywordGroup);
85
- for (const keywordNode of keywordGroupNode.querySelectorAll('kwd')) {
86
- if (keywordNode.textContent) {
87
- const keyword = (0, builders_1.buildKeyword)(keywordNode.textContent);
88
- keyword.priority = keywordPriority++;
89
- keyword.containedGroup = manuscriptKeywordGroup._id;
90
- keywordGroups.keywords.push(keyword);
91
- }
92
- }
93
- }
94
- return keywordGroups;
95
- },
96
71
  parseDates(historyNode) {
97
72
  if (!historyNode) {
98
73
  return undefined;
@@ -54,9 +54,6 @@ const parseJATSFront = async (front) => {
54
54
  const manuscriptMeta = Object.assign({ title: title ? inlineContentsFromJATSTitle(title) : undefined, subtitle: subtitle ? inlineContentsFromJATSTitle(subtitle) : undefined, runningTitle: runningTitle
55
55
  ? inlineContentsFromJATSTitle(runningTitle)
56
56
  : undefined }, jats_front_parser_1.jatsFrontParser.parseCounts(articleMeta === null || articleMeta === void 0 ? void 0 : articleMeta.querySelector('counts')));
57
- const keywordGroupNodes = articleMeta === null || articleMeta === void 0 ? void 0 : articleMeta.querySelectorAll('kwd-group');
58
- const { keywords, groups: keywordGroups } = jats_front_parser_1.jatsFrontParser.parseKeywords(keywordGroupNodes);
59
- const manuscript_keywordIDs = keywords.length > 0 ? keywords.map((k) => k._id) : undefined;
60
57
  const { affiliations, affiliationIDs } = jats_front_parser_1.jatsFrontParser.parseAffiliationNodes([
61
58
  ...front.querySelectorAll('article-meta > contrib-group > aff'),
62
59
  ]);
@@ -73,14 +70,12 @@ const parseJATSFront = async (front) => {
73
70
  const supplements = jats_front_parser_1.jatsFrontParser.parseSupplements([
74
71
  ...front.querySelectorAll('article-meta > supplementary-material'),
75
72
  ]);
76
- const manuscript = Object.assign(Object.assign(Object.assign(Object.assign({}, (0, builders_1.buildManuscript)()), manuscriptMeta), { keywordIDs: manuscript_keywordIDs }), history);
73
+ const manuscript = Object.assign(Object.assign(Object.assign({}, (0, builders_1.buildManuscript)()), manuscriptMeta), history);
77
74
  return {
78
75
  models: generateModelIDs([
79
76
  manuscript,
80
- ...keywords,
81
77
  ...affiliations,
82
78
  ...authors,
83
- ...keywordGroups,
84
79
  ...footnotes,
85
80
  ...correspondingList,
86
81
  journal,
@@ -116,10 +111,12 @@ const parseJATSBody = (document, body, bibliographyItems, refModels, referenceId
116
111
  const orderedFootnotesIDs = (0, footnotes_order_1.createOrderedFootnotesIDs)(document);
117
112
  jats_body_transformations_1.jatsBodyTransformations.moveFloatsGroupToBody(document, body, createElement);
118
113
  jats_body_transformations_1.jatsBodyTransformations.ensureSection(body, createElement);
114
+ jats_body_transformations_1.jatsBodyTransformations.mapFootnotesToSections(document, body, createElement);
119
115
  jats_body_transformations_1.jatsBodyTransformations.moveSectionsToBody(document, body, bibliographyItems, createElement);
120
116
  jats_body_transformations_1.jatsBodyTransformations.moveCaptionsToEnd(body);
121
117
  jats_body_transformations_1.jatsBodyTransformations.moveTableFooterToEnd(body);
122
118
  jats_body_transformations_1.jatsBodyTransformations.moveBlockNodesFromParagraph(document, body, createElement);
119
+ jats_body_transformations_1.jatsBodyTransformations.moveKeywordsToBody(document, body, createElement);
123
120
  const node = jats_body_dom_parser_1.jatsBodyDOMParser.parse(body);
124
121
  if (!node.firstChild) {
125
122
  throw new Error('No content was parsed from the JATS article body');
@@ -136,10 +136,8 @@ class JATSExporter {
136
136
  article.appendChild(body);
137
137
  const back = this.buildBack(body);
138
138
  article.appendChild(back);
139
- this.unwrapBody(body);
140
139
  this.moveAbstracts(front, body);
141
140
  this.moveFloatsGroup(body, article);
142
- this.removeBackContainer(body);
143
141
  }
144
142
  await this.rewriteIDs(idGenerator);
145
143
  if (mediaPathGenerator) {
@@ -400,9 +398,7 @@ class JATSExporter {
400
398
  if (history.childElementCount) {
401
399
  articleMeta.appendChild(history);
402
400
  }
403
- if (manuscript.keywordIDs) {
404
- this.buildKeywords(articleMeta, manuscript.keywordIDs);
405
- }
401
+ this.buildKeywords(articleMeta);
406
402
  let countingElements = [];
407
403
  if (manuscript.genericCounts) {
408
404
  const elements = manuscript.genericCounts.map((el) => {
@@ -626,6 +622,7 @@ class JATSExporter {
626
622
  bibliography_element: () => '',
627
623
  bibliography_item: () => '',
628
624
  comment_list: () => '',
625
+ keywords_group: () => '',
629
626
  bibliography_section: (node) => [
630
627
  'ref-list',
631
628
  { id: normalizeID(node.attrs.id) },
@@ -1394,49 +1391,21 @@ class JATSExporter {
1394
1391
  table.insertBefore(tfoot, tbody);
1395
1392
  }
1396
1393
  };
1397
- this.unwrapBody = (body) => {
1398
- const container = body.querySelector(':scope > sec[sec-type="body"]');
1399
- if (!container) {
1400
- return;
1401
- }
1402
- const sections = container.querySelectorAll(':scope > sec');
1403
- sections.forEach((section) => {
1404
- body.appendChild(section.cloneNode(true));
1405
- });
1406
- body.removeChild(container);
1407
- };
1408
- this.removeBackContainer = (body) => {
1409
- const container = body.querySelector(':scope > sec[sec-type="backmatter"]');
1410
- if (!container) {
1411
- return;
1412
- }
1413
- const isContainerEmpty = container.children.length === 0;
1414
- if (!isContainerEmpty) {
1415
- warn('Backmatter section is not empty.');
1416
- }
1417
- body.removeChild(container);
1418
- };
1419
1394
  this.moveAbstracts = (front, body) => {
1420
- const container = body.querySelector(':scope > sec[sec-type="abstracts"]');
1421
- let abstractSections;
1422
- if (container) {
1423
- abstractSections = Array.from(container.querySelectorAll(':scope > sec'));
1424
- }
1425
- else {
1426
- abstractSections = Array.from(body.querySelectorAll(':scope > sec')).filter((section) => {
1427
- const sectionType = section.getAttribute('sec-type');
1428
- if (sectionType === 'abstract' ||
1429
- sectionType === 'abstract-teaser' ||
1430
- sectionType === 'abstract-graphical') {
1431
- return true;
1432
- }
1433
- const sectionTitle = section.querySelector(':scope > title');
1434
- if (!sectionTitle) {
1435
- return false;
1436
- }
1437
- return sectionTitle.textContent === 'Abstract';
1438
- });
1439
- }
1395
+ const sections = body.querySelectorAll(':scope > sec');
1396
+ const abstractSections = Array.from(sections).filter((section) => {
1397
+ const sectionType = section.getAttribute('sec-type');
1398
+ if (sectionType === 'abstract' ||
1399
+ sectionType === 'abstract-teaser' ||
1400
+ sectionType === 'abstract-graphical') {
1401
+ return true;
1402
+ }
1403
+ const sectionTitle = section.querySelector(':scope > title');
1404
+ if (!sectionTitle) {
1405
+ return false;
1406
+ }
1407
+ return sectionTitle.textContent === 'Abstract';
1408
+ });
1440
1409
  if (abstractSections.length) {
1441
1410
  for (const abstractSection of abstractSections) {
1442
1411
  const abstractNode = this.document.createElement('abstract');
@@ -1457,9 +1426,6 @@ class JATSExporter {
1457
1426
  }
1458
1427
  }
1459
1428
  }
1460
- if (container) {
1461
- body.removeChild(container);
1462
- }
1463
1429
  };
1464
1430
  this.moveSectionsToBack = (back, body) => {
1465
1431
  const availabilitySection = body.querySelector('sec[sec-type="availability"]');
@@ -1562,10 +1528,8 @@ class JATSExporter {
1562
1528
  return name;
1563
1529
  };
1564
1530
  }
1565
- buildKeywords(articleMeta, keywordIDs) {
1566
- const keywords = keywordIDs
1567
- .map((id) => this.modelMap.get(id))
1568
- .filter((model) => model && model.name);
1531
+ buildKeywords(articleMeta) {
1532
+ const keywords = [...this.modelMap.values()].filter((model) => model.objectType === json_schema_1.ObjectTypes.Keyword);
1569
1533
  const keywordGroups = new Map();
1570
1534
  keywords.forEach((keyword) => {
1571
1535
  const containedGroup = keyword.containedGroup || '';
@@ -1599,6 +1563,7 @@ class JATSExporter {
1599
1563
  kwd.textContent = keyword.name;
1600
1564
  kwdGroup.appendChild(kwd);
1601
1565
  }
1566
+ articleMeta.appendChild(kwdGroup);
1602
1567
  }
1603
1568
  }
1604
1569
  }
@@ -15,7 +15,8 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.findParentNodeClosestToPos = exports.isInGraphicalAbstractSection = exports.findNodePositions = exports.iterateChildren = void 0;
18
+ exports.findParentNodeClosestToPos = exports.isInBibliographySection = exports.isInGraphicalAbstractSection = exports.findNodePositions = exports.iterateChildren = void 0;
19
+ const bibliography_section_1 = require("../schema/nodes/bibliography_section");
19
20
  const graphical_abstract_section_1 = require("../schema/nodes/graphical_abstract_section");
20
21
  function* iterateChildren(node, recurse = false) {
21
22
  for (let i = 0; i < node.childCount; i++) {
@@ -50,6 +51,16 @@ const isInGraphicalAbstractSection = ($pos) => {
50
51
  return false;
51
52
  };
52
53
  exports.isInGraphicalAbstractSection = isInGraphicalAbstractSection;
54
+ const isInBibliographySection = ($pos) => {
55
+ for (let i = $pos.depth; i > 0; i--) {
56
+ const node = $pos.node(i);
57
+ if ((0, bibliography_section_1.isBibliographySectionNode)(node)) {
58
+ return true;
59
+ }
60
+ }
61
+ return false;
62
+ };
63
+ exports.isInBibliographySection = isInBibliographySection;
53
64
  const findParentNodeClosestToPos = ($pos, predicate) => {
54
65
  for (let i = $pos.depth; i > 0; i--) {
55
66
  const node = $pos.node(i);
@@ -59,6 +59,7 @@ const inline_equation_1 = require("./nodes/inline_equation");
59
59
  const inline_footnote_1 = require("./nodes/inline_footnote");
60
60
  const keyword_1 = require("./nodes/keyword");
61
61
  const keywords_element_1 = require("./nodes/keywords_element");
62
+ const keywords_group_1 = require("./nodes/keywords_group");
62
63
  const keywords_section_1 = require("./nodes/keywords_section");
63
64
  const link_1 = require("./nodes/link");
64
65
  const list_1 = require("./nodes/list");
@@ -173,6 +174,7 @@ exports.schema = new prosemirror_model_1.Schema({
173
174
  keyword: keyword_1.keyword,
174
175
  keywords_element: keywords_element_1.keywordsElement,
175
176
  keywords_section: keywords_section_1.keywordsSection,
177
+ keywords_group: keywords_group_1.keywordsGroup,
176
178
  link: link_1.link,
177
179
  list_item: list_1.listItem,
178
180
  listing: listing_1.listing,
@@ -25,8 +25,7 @@ exports.keyword = {
25
25
  dataTracked: { default: null },
26
26
  comments: { default: null },
27
27
  },
28
- inline: true,
29
- group: 'inline',
28
+ group: 'block',
30
29
  selectable: false,
31
30
  parseDOM: [
32
31
  {
@@ -18,11 +18,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.keywordsElement = void 0;
19
19
  exports.keywordsElement = {
20
20
  atom: true,
21
- content: 'keyword*',
21
+ content: 'keywords_group*',
22
22
  attrs: {
23
23
  id: { default: '' },
24
24
  contents: { default: '' },
25
25
  paragraphStyle: { default: '' },
26
+ type: { default: '' },
26
27
  dataTracked: { default: null },
27
28
  },
28
29
  group: 'block element',
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /*!
3
+ * © 2019 Atypon Systems LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.isKeywordsGroupNode = exports.keywordsGroup = void 0;
19
+ exports.keywordsGroup = {
20
+ content: 'keyword*',
21
+ attrs: {
22
+ id: { default: '' },
23
+ type: { default: '' },
24
+ dataTracked: { default: null },
25
+ },
26
+ group: 'block',
27
+ selectable: false,
28
+ parseDOM: [
29
+ {
30
+ tag: 'div.keywords',
31
+ },
32
+ ],
33
+ toDOM: (node) => {
34
+ const keywordsGroupNode = node;
35
+ return [
36
+ 'div',
37
+ {
38
+ id: keywordsGroupNode.attrs.id,
39
+ class: 'keywords',
40
+ spellcheck: 'false',
41
+ contenteditable: false,
42
+ },
43
+ 0,
44
+ ];
45
+ },
46
+ };
47
+ const isKeywordsGroupNode = (node) => node.type === node.type.schema.nodes.keywords_group;
48
+ exports.isKeywordsGroupNode = isKeywordsGroupNode;
@@ -17,7 +17,7 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.isKeywordsSectionNode = exports.keywordsSection = void 0;
19
19
  exports.keywordsSection = {
20
- content: 'section_title_plain (keywords_element | placeholder_element)',
20
+ content: 'section_title (keywords_element | placeholder_element)',
21
21
  attrs: {
22
22
  id: { default: '' },
23
23
  dataTracked: { default: null },
@@ -35,7 +35,7 @@ const choosePageBreakStyle = (element) => {
35
35
  return exports.PAGE_BREAK_NONE;
36
36
  };
37
37
  exports.section = {
38
- content: 'section_label? section_title (paragraph | element)* sections*',
38
+ content: 'section_label? section_title (paragraph | element)* section*',
39
39
  attrs: {
40
40
  id: { default: '' },
41
41
  category: { default: '' },
@@ -275,11 +275,11 @@ class Decoder {
275
275
  },
276
276
  [json_schema_1.ObjectTypes.KeywordsElement]: (data) => {
277
277
  const model = data;
278
- const keywords = this.getKeywords();
278
+ const keywordGroups = this.getKeywordGroups();
279
279
  return schema_1.schema.nodes.keywords_element.create({
280
280
  id: model._id,
281
281
  paragraphStyle: model.paragraphStyle,
282
- }, keywords);
282
+ }, keywordGroups);
283
283
  },
284
284
  [json_schema_1.ObjectTypes.Keyword]: (data) => {
285
285
  const model = data;
@@ -426,9 +426,7 @@ class Decoder {
426
426
  const elementNodes = elements
427
427
  .map(this.decode)
428
428
  .filter(exports.isManuscriptNode);
429
- const sectionTitle = isKeywordsSection
430
- ? 'section_title_plain'
431
- : 'section_title';
429
+ const sectionTitle = 'section_title';
432
430
  const sectionTitleNode = model.title
433
431
  ? this.parseContents(model.title, 'h1', this.getComments(model), {
434
432
  topNode: schema_1.schema.nodes[sectionTitle].create(),
@@ -572,19 +570,6 @@ class Decoder {
572
570
  id: (0, id_1.generateNodeID)(schema_1.schema.nodes.section),
573
571
  }));
574
572
  }
575
- const keywordsSection = getSections(this.modelMap).filter((section) => section.category === 'MPSectionCategory:keywords');
576
- const keywords = this.getKeywords();
577
- if (keywordsSection.length === 0 && keywords.length > 0) {
578
- const keywordsSection = schema_1.schema.nodes.keywords_section.createAndFill({
579
- id: (0, id_1.generateNodeID)(schema_1.schema.nodes.keywords_section),
580
- }, [
581
- schema_1.schema.nodes.section_title_plain.create({}, schema_1.schema.text('Keywords')),
582
- schema_1.schema.nodes.keywords_element.create({
583
- id: (0, id_1.generateNodeID)(schema_1.schema.nodes.keywords_element),
584
- }, keywords),
585
- ]);
586
- rootSectionNodes.unshift(keywordsSection);
587
- }
588
573
  const contents = rootSectionNodes;
589
574
  if (this.comments.size) {
590
575
  const comments = schema_1.schema.nodes.comment_list.createAndFill({
@@ -632,17 +617,16 @@ class Decoder {
632
617
  }
633
618
  return parser.parse(template.content.firstElementChild, options);
634
619
  };
635
- this.getKeywords = () => {
620
+ this.getKeywords = (id) => {
636
621
  const keywordsOfKind = [];
637
- for (const model of this.modelMap.values()) {
638
- const commentNodes = this.createCommentsNode(model);
639
- commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
622
+ const keywordsByGroup = [...this.modelMap.values()].filter((m) => m.objectType === json_schema_1.ObjectTypes.Keyword &&
623
+ m.containedGroup === id);
624
+ for (const model of keywordsByGroup) {
640
625
  if (isKeyword(model)) {
641
626
  const keyword = this.parseContents(model.name || '', 'div', this.getComments(model), {
642
627
  topNode: schema_1.schema.nodes.keyword.create({
643
628
  id: model._id,
644
629
  contents: model.name,
645
- comments: commentNodes.map((c) => c.attrs.id),
646
630
  }),
647
631
  });
648
632
  keywordsOfKind.push(keyword);
@@ -689,5 +673,27 @@ class Decoder {
689
673
  this.modelMap = modelMap;
690
674
  this.allowMissingElements = allowMissingElements;
691
675
  }
676
+ getKeywordGroups() {
677
+ const kwdGroupNodes = [];
678
+ const kwdGroupsModels = [
679
+ ...this.modelMap.values(),
680
+ ].filter((model) => model.objectType === json_schema_1.ObjectTypes.KeywordGroup);
681
+ if (kwdGroupsModels.length > 0) {
682
+ for (const kwdGroupModel of kwdGroupsModels) {
683
+ const keywords = this.getKeywords(kwdGroupModel._id);
684
+ const commentNodes = this.createCommentsNode(kwdGroupModel);
685
+ commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
686
+ const contents = [];
687
+ contents.push(...keywords);
688
+ const kwdGroupNode = schema_1.schema.nodes.keywords_group.create({
689
+ id: kwdGroupModel._id,
690
+ type: kwdGroupModel.type,
691
+ comments: commentNodes.map((c) => c.attrs.id),
692
+ }, contents);
693
+ kwdGroupNodes.push(kwdGroupNode);
694
+ }
695
+ }
696
+ return kwdGroupNodes;
697
+ }
692
698
  }
693
699
  exports.Decoder = Decoder;