@manuscripts/transform 4.5.1 → 4.5.3

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 (66) hide show
  1. package/dist/cjs/jats/__tests__/data/section-categories.js +5 -5
  2. package/dist/cjs/jats/__tests__/jats-importer.test.js +19 -5
  3. package/dist/cjs/jats/exporter/jats-exporter.js +725 -910
  4. package/dist/cjs/jats/exporter/jats-versions.js +9 -9
  5. package/dist/cjs/jats/importer/jats-dom-parser.js +25 -24
  6. package/dist/cjs/jats/importer/jats-transformations.js +14 -30
  7. package/dist/cjs/lib/section-categories.js +31 -1
  8. package/dist/cjs/schema/index.js +3 -0
  9. package/dist/cjs/schema/migration/migration-scripts/4.5.3.js +37 -0
  10. package/dist/cjs/schema/migration/migration-scripts/index.js +2 -0
  11. package/dist/cjs/schema/nodes/abstract.js +51 -0
  12. package/dist/cjs/schema/nodes/abstracts.js +1 -1
  13. package/dist/cjs/schema/nodes/footnote.js +1 -0
  14. package/dist/cjs/schema/nodes/general_table_footnote.js +1 -0
  15. package/dist/cjs/transformer/node-names.js +1 -0
  16. package/dist/cjs/transformer/node-title.js +2 -0
  17. package/dist/cjs/version.js +1 -1
  18. package/dist/es/jats/__tests__/data/section-categories.js +5 -5
  19. package/dist/es/jats/__tests__/jats-importer.test.js +19 -5
  20. package/dist/es/jats/exporter/jats-exporter.js +728 -913
  21. package/dist/es/jats/exporter/jats-versions.js +7 -7
  22. package/dist/es/jats/importer/jats-dom-parser.js +25 -24
  23. package/dist/es/jats/importer/jats-transformations.js +14 -30
  24. package/dist/es/lib/section-categories.js +28 -0
  25. package/dist/es/schema/index.js +3 -0
  26. package/dist/es/schema/migration/migration-scripts/4.5.3.js +35 -0
  27. package/dist/es/schema/migration/migration-scripts/index.js +2 -0
  28. package/dist/es/schema/nodes/abstract.js +47 -0
  29. package/dist/es/schema/nodes/abstracts.js +1 -1
  30. package/dist/es/schema/nodes/footnote.js +1 -0
  31. package/dist/es/schema/nodes/general_table_footnote.js +1 -0
  32. package/dist/es/transformer/node-names.js +1 -0
  33. package/dist/es/transformer/node-title.js +2 -0
  34. package/dist/es/version.js +1 -1
  35. package/dist/types/jats/exporter/jats-exporter.d.ts +31 -27
  36. package/dist/types/jats/exporter/jats-versions.d.ts +4 -4
  37. package/dist/types/jats/importer/jats-dom-parser.d.ts +0 -1
  38. package/dist/types/lib/section-categories.d.ts +3 -0
  39. package/dist/types/schema/index.d.ts +1 -0
  40. package/dist/types/schema/migration/migration-scripts/4.5.3.d.ts +8 -0
  41. package/dist/types/schema/migration/migration-scripts/index.d.ts +2 -1
  42. package/dist/types/schema/nodes/abstract.d.ts +26 -0
  43. package/dist/types/schema/types.d.ts +1 -1
  44. package/dist/types/version.d.ts +1 -1
  45. package/package.json +1 -1
  46. package/src/jats/__tests__/__snapshots__/jats-exporter.test.ts.snap +3 -3
  47. package/src/jats/__tests__/__snapshots__/jats-importer.test.ts.snap +86 -132
  48. package/src/jats/__tests__/__snapshots__/jats-roundtrip.test.ts.snap +3 -3
  49. package/src/jats/__tests__/data/section-categories.ts +5 -5
  50. package/src/jats/__tests__/jats-importer.test.ts +22 -6
  51. package/src/jats/exporter/jats-exporter.ts +855 -1126
  52. package/src/jats/exporter/jats-versions.ts +11 -11
  53. package/src/jats/importer/jats-dom-parser.ts +34 -34
  54. package/src/jats/importer/jats-transformations.ts +21 -37
  55. package/src/lib/section-categories.ts +36 -0
  56. package/src/schema/index.ts +3 -0
  57. package/src/schema/migration/migration-scripts/4.5.3.ts +57 -0
  58. package/src/schema/migration/migration-scripts/index.ts +2 -0
  59. package/src/schema/nodes/abstract.ts +66 -0
  60. package/src/schema/nodes/abstracts.ts +2 -1
  61. package/src/schema/nodes/footnote.ts +1 -0
  62. package/src/schema/nodes/general_table_footnote.ts +1 -0
  63. package/src/schema/types.ts +1 -0
  64. package/src/transformer/node-names.ts +1 -0
  65. package/src/transformer/node-title.ts +2 -0
  66. package/src/version.ts +1 -1
@@ -59,7 +59,7 @@ const w3c_xmlserializer_1 = __importDefault(require("w3c-xmlserializer"));
59
59
  const citeproc_1 = require("../../lib/citeproc");
60
60
  const credit_roles_1 = require("../../lib/credit-roles");
61
61
  const footnotes_1 = require("../../lib/footnotes");
62
- const html_1 = require("../../lib/html");
62
+ const section_categories_1 = require("../../lib/section-categories");
63
63
  const xml_1 = require("../../lib/xml");
64
64
  const schema_1 = require("../../schema");
65
65
  const transformer_1 = require("../../transformer");
@@ -67,20 +67,6 @@ const citeproc_2 = require("./citeproc");
67
67
  const jats_versions_1 = require("./jats-versions");
68
68
  const labels_1 = require("./labels");
69
69
  const normalizeID = (id) => id.replace(/:/g, '_');
70
- const parser = prosemirror_model_1.DOMParser.fromSchema(schema_1.schema);
71
- const insertAbstractNode = (front, abstractNode) => {
72
- const articleMeta = front.querySelector(':scope > article-meta');
73
- if (!articleMeta) {
74
- return;
75
- }
76
- const insertBeforeElement = articleMeta.querySelector('kwd-group, funding-group, support-group, conference, counts, custom-meta-group');
77
- if (insertBeforeElement) {
78
- articleMeta.insertBefore(abstractNode, insertBeforeElement);
79
- }
80
- else {
81
- articleMeta.appendChild(abstractNode);
82
- }
83
- };
84
70
  const createCounter = () => {
85
71
  const counts = new Map();
86
72
  return {
@@ -93,7 +79,7 @@ const createCounter = () => {
93
79
  };
94
80
  };
95
81
  exports.createCounter = createCounter;
96
- const createDefaultIdGenerator = () => {
82
+ const createDefaultIDGenerator = () => {
97
83
  const counter = (0, exports.createCounter)();
98
84
  return async (element) => {
99
85
  const value = String(counter.increment(element.nodeName));
@@ -111,6 +97,7 @@ const chooseRefType = (type) => {
111
97
  case schema_1.schema.nodes.table_element:
112
98
  return 'table';
113
99
  case schema_1.schema.nodes.section:
100
+ case schema_1.schema.nodes.abstract:
114
101
  return 'sec';
115
102
  case schema_1.schema.nodes.equation:
116
103
  case schema_1.schema.nodes.equation_element:
@@ -119,10 +106,10 @@ const chooseRefType = (type) => {
119
106
  return 'supplementary-material';
120
107
  }
121
108
  };
122
- const sortContributors = (a, b) => Number(a.attrs.priority) - Number(b.attrs.priority);
123
109
  class JATSExporter {
124
110
  constructor() {
125
111
  this.nodesMap = new Map();
112
+ this.contributorLabels = new Map();
126
113
  this.populateNodesMap = () => {
127
114
  this.manuscriptNode.descendants((node) => {
128
115
  const type = node.type;
@@ -136,53 +123,53 @@ class JATSExporter {
136
123
  this.populateNodesMap();
137
124
  this.initCiteprocEngine(options);
138
125
  this.createSerializer();
139
- const versionIds = (0, jats_versions_1.selectVersionIds)(options.version ?? '1.2');
140
- this.document = document.implementation.createDocument(null, 'article', document.implementation.createDocumentType('article', versionIds.publicId, versionIds.systemId));
141
- const article = this.document.documentElement;
142
- article.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', xml_1.XLINK_NAMESPACE);
143
- const front = this.buildFront();
144
- article.appendChild(front);
145
- article.setAttribute('article-type', manuscriptNode.attrs.articleType || 'other');
146
- article.setAttributeNS(xml_1.XML_NAMESPACE, 'lang', manuscriptNode.attrs.primaryLanguageCode || 'en');
126
+ const versionIDs = (0, jats_versions_1.selectVersionIDs)(options.version ?? '1.2');
127
+ this.document = document.implementation.createDocument(null, 'article', document.implementation.createDocumentType('article', versionIDs.publicID, versionIDs.systemID));
128
+ const $article = this.document.documentElement;
129
+ $article.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', xml_1.XLINK_NAMESPACE);
147
130
  this.labelTargets = (0, labels_1.buildTargets)(manuscriptNode.descendants.bind(manuscriptNode));
148
131
  this.footnoteLabels = (0, footnotes_1.generateFootnoteLabels)(manuscriptNode);
149
- const body = this.buildBody();
150
- article.appendChild(body);
151
- this.addParagraphsToSections(article);
152
- const back = this.buildBack(body);
153
- this.moveCoiStatementToAuthorNotes(back, front);
154
- article.appendChild(back);
155
- this.unwrapBody(body);
156
- this.moveAbstracts(front, body);
157
- this.removeBackContainer(body);
158
- this.updateFootnoteTypes(front, back);
159
- this.fillEmptyTableFooters(article);
160
- this.fillEmptyFootnotes(article);
161
- this.fillEmptyListItem(article);
162
- this.moveAwards(front, body);
163
- this.moveFloatsGroup(article);
132
+ const $body = this.buildBody();
133
+ const $back = this.buildBack();
134
+ this.routeBackmatterSections($body, $back);
135
+ const $coiStatement = this.extractCoiStatement($back);
136
+ $back.appendChild(this.buildRefList());
137
+ const $front = this.buildFront($coiStatement);
138
+ $article.appendChild($front);
139
+ $article.setAttribute('article-type', manuscriptNode.attrs.articleType || 'other');
140
+ $article.setAttributeNS(xml_1.XML_NAMESPACE, 'lang', manuscriptNode.attrs.primaryLanguageCode || 'en');
141
+ $article.appendChild($body);
142
+ $article.appendChild($back);
143
+ this.addParagraphsToSections($article);
144
+ this.fillEmptyTableFooters($article);
145
+ this.fillEmptyFootnotes($article);
146
+ this.fillEmptyListItem($article);
147
+ const $floatsGroup = this.buildFloatsGroup();
148
+ if ($floatsGroup) {
149
+ $article.appendChild($floatsGroup);
150
+ }
164
151
  await this.rewriteIDs();
165
152
  return (0, w3c_xmlserializer_1.default)(this.document);
166
153
  };
167
- this.appendChildNodeOfType = (element, node, type) => {
154
+ this.appendChildNodeOfType = ($element, node, type) => {
168
155
  const childNode = this.getFirstChildOfType(type, node);
169
156
  if (childNode) {
170
- element.appendChild(this.serializeNode(childNode));
157
+ $element.appendChild(this.serializeNode(childNode));
171
158
  }
172
159
  };
173
- this.appendChildrenNodeOfType = (element, node, type) => {
160
+ this.appendChildrenNodeOfType = ($element, node, type) => {
174
161
  const children = this.getChildrenOfType(type, node);
175
- children.map((childNode) => element.appendChild(this.serializeNode(childNode)));
162
+ children.map((childNode) => $element.appendChild(this.serializeNode(childNode)));
176
163
  };
177
- this.appendCaption = (element, node) => {
178
- const caption = this.createElement('caption');
179
- this.appendChildNodeOfType(caption, node, schema_1.schema.nodes.caption_title);
164
+ this.appendCaption = ($element, node) => {
165
+ const $caption = this.createElement('caption');
166
+ this.appendChildNodeOfType($caption, node, schema_1.schema.nodes.caption_title);
180
167
  const captionNode = this.getChildrenOfType(schema_1.schema.nodes.caption, node)[0];
181
168
  if (captionNode && captionNode.content.size > 2) {
182
- this.appendChildrenNodeOfType(caption, captionNode, schema_1.schema.nodes.text_block);
169
+ this.appendChildrenNodeOfType($caption, captionNode, schema_1.schema.nodes.text_block);
183
170
  }
184
- if (caption.children.length > 0) {
185
- element.appendChild(caption);
171
+ if ($caption.children.length > 0) {
172
+ $element.appendChild($caption);
186
173
  }
187
174
  };
188
175
  this.initCiteprocEngine = (options) => {
@@ -217,33 +204,32 @@ class JATSExporter {
217
204
  this.engine = engine;
218
205
  this.renderedCitations = new Map(output.map((i) => [i[0], i[2]]));
219
206
  };
220
- this.nodeFromJATS = (JATSFragment) => {
221
- JATSFragment = JATSFragment.trim();
222
- JATSFragment = JATSFragment.replace(' ', ' ');
223
- if (!JATSFragment.length) {
207
+ this.nodeFromJATS = (fragment) => {
208
+ fragment = fragment.trim().replace(' ', ' ');
209
+ if (!fragment.length) {
224
210
  return null;
225
211
  }
226
- const template = this.createElement('template');
227
- template.innerHTML = JATSFragment;
228
- return template.firstChild;
212
+ const $template = this.createElement('template');
213
+ $template.innerHTML = fragment;
214
+ return $template.firstChild;
229
215
  };
230
- this.rewriteIDs = async (generator = createDefaultIdGenerator()) => {
216
+ this.rewriteIDs = async (generator = createDefaultIDGenerator()) => {
231
217
  const ids = new Map();
232
- for (const element of this.document.querySelectorAll('[id]')) {
233
- const oldID = element.getAttribute('id');
234
- const newID = await generator(element);
218
+ for (const $element of this.document.querySelectorAll('[id]')) {
219
+ const oldID = $element.getAttribute('id');
220
+ const newID = await generator($element);
235
221
  if (newID) {
236
- element.setAttribute('id', newID);
222
+ $element.setAttribute('id', newID);
237
223
  }
238
224
  else {
239
- element.removeAttribute('id');
225
+ $element.removeAttribute('id');
240
226
  }
241
227
  if (oldID) {
242
228
  ids.set(oldID, newID);
243
229
  }
244
230
  }
245
- for (const node of this.document.querySelectorAll('[rid]')) {
246
- const rids = node.getAttribute('rid');
231
+ for (const $node of this.document.querySelectorAll('[rid]')) {
232
+ const rids = $node.getAttribute('rid');
247
233
  if (rids) {
248
234
  const newRids = rids
249
235
  .split(/\s+/)
@@ -251,126 +237,113 @@ class JATSExporter {
251
237
  .map((rid) => ids.get(rid))
252
238
  .filter(Boolean);
253
239
  if (newRids.length) {
254
- node.setAttribute('rid', newRids.join(' '));
240
+ $node.setAttribute('rid', newRids.join(' '));
255
241
  }
256
242
  }
257
243
  }
258
244
  };
259
- this.setTitleContent = (element, title) => {
260
- const htmlTitleNode = (0, html_1.nodeFromHTML)(`<h1>${title}</h1>`);
261
- if (htmlTitleNode) {
262
- const titleNode = parser.parse(htmlTitleNode, {
263
- topNode: schema_1.schema.nodes.section_title.create(),
264
- });
265
- const jatsTitleNode = this.serializeNode(titleNode);
266
- while (jatsTitleNode.firstChild) {
267
- element.appendChild(jatsTitleNode.firstChild);
268
- }
245
+ this.buildFront = ($coiStatement) => {
246
+ const $front = this.createElement('front');
247
+ const $articleMeta = this.createElement('article-meta');
248
+ $front.appendChild($articleMeta);
249
+ const contributors = this.getChildrenOfType(schema_1.schema.nodes.contributor).sort((a, b) => Number(a.attrs.priority) - Number(b.attrs.priority));
250
+ this.contributorLabels = this.computeContributorLabels(contributors);
251
+ const $frontNodes = [
252
+ this.buildArticleID(),
253
+ this.buildTitleGroup(),
254
+ this.buildContributorGroup(contributors),
255
+ this.buildAuthorNotes($coiStatement),
256
+ this.buildSupplements(),
257
+ this.buildHistory(),
258
+ this.buildSelfUris(),
259
+ this.buildAbstracts(),
260
+ this.buildKeywords(),
261
+ this.buildAwards(),
262
+ this.buildCounts(),
263
+ ];
264
+ $frontNodes.forEach(($nodes) => this.appendChildren($articleMeta, $nodes));
265
+ return $front;
266
+ };
267
+ this.appendChildren = ($parent, $elements) => {
268
+ if (!$elements) {
269
+ return;
269
270
  }
271
+ const list = Array.isArray($elements) ? $elements : [$elements];
272
+ list.forEach(($element) => $parent.appendChild($element));
270
273
  };
271
- this.buildFront = () => {
272
- const front = this.createElement('front');
273
- const articleMeta = this.createElement('article-meta');
274
- front.appendChild(articleMeta);
274
+ this.buildArticleID = () => {
275
275
  if (this.manuscriptNode.attrs.doi) {
276
- const articleID = this.createElement('article-id');
277
- articleID.setAttribute('pub-id-type', 'doi');
278
- articleID.textContent = this.manuscriptNode.attrs.doi;
279
- articleMeta.appendChild(articleID);
276
+ const $articleID = this.createElement('article-id');
277
+ $articleID.setAttribute('pub-id-type', 'doi');
278
+ $articleID.textContent = this.manuscriptNode.attrs.doi;
279
+ return $articleID;
280
280
  }
281
- const titleGroup = this.createElement('title-group');
281
+ };
282
+ this.buildTitleGroup = () => {
283
+ const $titleGroup = this.createElement('title-group');
282
284
  const titleNode = this.getFirstChildOfType(schema_1.schema.nodes.title);
283
285
  if (titleNode) {
284
- const element = this.createElement('article-title');
285
- this.setTitleContent(element, titleNode.textContent);
286
- titleGroup.appendChild(element);
286
+ $titleGroup.appendChild(this.serializeNode(titleNode));
287
287
  }
288
- const subtitlesNodes = this.getChildrenOfType(schema_1.schema.nodes.subtitle);
289
- subtitlesNodes.forEach((subtitleNode) => {
290
- const element = this.createElement('subtitle');
291
- this.setTitleContent(element, subtitleNode.textContent);
292
- titleGroup.appendChild(element);
293
- });
294
- const altTitlesNodes = this.getChildrenOfType(schema_1.schema.nodes.alt_title);
295
- altTitlesNodes.forEach((titleNode) => {
296
- const element = this.createElement('alt-title');
297
- element.setAttribute('alt-title-type', titleNode.attrs.type);
298
- this.setTitleContent(element, titleNode.textContent);
299
- titleGroup.appendChild(element);
300
- });
301
- articleMeta.appendChild(titleGroup);
302
- this.buildContributors(articleMeta);
303
- const supplementsNodes = this.getChildrenOfType(schema_1.schema.nodes.supplement);
304
- supplementsNodes.forEach((node) => {
305
- const supplementaryMaterial = this.createElement('supplementary-material');
306
- supplementaryMaterial.setAttribute('id', normalizeID(node.attrs.id));
307
- supplementaryMaterial.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', node.attrs.href ?? '');
308
- supplementaryMaterial.setAttribute('mimetype', node.attrs.mimeType ?? '');
309
- supplementaryMaterial.setAttribute('mime-subtype', node.attrs.mimeSubType ?? '');
310
- this.appendCaption(supplementaryMaterial, node);
311
- articleMeta.append(supplementaryMaterial);
288
+ this.getChildrenOfType(schema_1.schema.nodes.subtitle).forEach((node) => $titleGroup.appendChild(this.serializeNode(node)));
289
+ this.getChildrenOfType(schema_1.schema.nodes.alt_title).forEach((node) => $titleGroup.appendChild(this.serializeNode(node)));
290
+ return $titleGroup;
291
+ };
292
+ this.buildSupplements = () => this.getChildrenOfType(schema_1.schema.nodes.supplement).map((node) => this.serializeNode(node));
293
+ this.buildHistory = () => {
294
+ const $history = this.createElement('history');
295
+ const dates = new Map([
296
+ ['accepted', this.manuscriptNode.attrs.acceptanceDate],
297
+ ['corrected', this.manuscriptNode.attrs.correctionDate],
298
+ ['retracted', this.manuscriptNode.attrs.retractionDate],
299
+ ['received', this.manuscriptNode.attrs.receiveDate],
300
+ ['rev-recd', this.manuscriptNode.attrs.revisionReceiveDate],
301
+ ['rev-request', this.manuscriptNode.attrs.revisionRequestDate],
302
+ ]);
303
+ dates.forEach((val, key) => {
304
+ if (val) {
305
+ $history.appendChild(this.buildDateElement(val, key));
306
+ }
312
307
  });
313
- const history = articleMeta.querySelector('history') || this.createElement('history');
314
- if (this.manuscriptNode.attrs.acceptanceDate) {
315
- const date = this.buildDateElement(this.manuscriptNode.attrs.acceptanceDate, 'accepted');
316
- history.appendChild(date);
317
- }
318
- if (this.manuscriptNode.attrs.correctionDate) {
319
- const date = this.buildDateElement(this.manuscriptNode.attrs.correctionDate, 'corrected');
320
- history.appendChild(date);
308
+ if ($history.childElementCount) {
309
+ return $history;
321
310
  }
322
- if (this.manuscriptNode.attrs.retractionDate) {
323
- const date = this.buildDateElement(this.manuscriptNode.attrs.retractionDate, 'retracted');
324
- history.appendChild(date);
325
- }
326
- if (this.manuscriptNode.attrs.receiveDate) {
327
- const date = this.buildDateElement(this.manuscriptNode.attrs.receiveDate, 'received');
328
- history.appendChild(date);
329
- }
330
- if (this.manuscriptNode.attrs.revisionReceiveDate) {
331
- const date = this.buildDateElement(this.manuscriptNode.attrs.revisionReceiveDate, 'rev-recd');
332
- history.appendChild(date);
333
- }
334
- if (this.manuscriptNode.attrs.revisionRequestDate) {
335
- const date = this.buildDateElement(this.manuscriptNode.attrs.revisionRequestDate, 'rev-request');
336
- history.appendChild(date);
337
- }
338
- if (history.childElementCount) {
339
- articleMeta.appendChild(history);
311
+ };
312
+ this.buildCounts = () => {
313
+ const $counts = this.createElement('counts');
314
+ const counts = new Map([
315
+ ['fig-count', this.getChildrenOfType(schema_1.schema.nodes.figure).length],
316
+ ['table-count', this.getChildrenOfType(schema_1.schema.nodes.table).length],
317
+ [
318
+ 'equation-count',
319
+ this.getChildrenOfType(schema_1.schema.nodes.equation_element).length,
320
+ ],
321
+ [
322
+ 'ref-count',
323
+ this.getChildrenOfType(schema_1.schema.nodes.bibliography_item).length,
324
+ ],
325
+ ['word-count', this.manuscriptNode.textContent.split(/\s+/).length],
326
+ ]);
327
+ counts.forEach((val, key) => {
328
+ if (val) {
329
+ $counts.appendChild(this.createElement(key, undefined, { count: String(val) }));
330
+ }
331
+ });
332
+ if ($counts.childElementCount) {
333
+ return $counts;
340
334
  }
341
- this.buildKeywords(articleMeta);
342
- let countingElements = [];
343
- const figureCount = this.getChildrenOfType(schema_1.schema.nodes.figure).length;
344
- countingElements.push(this.buildCountingElement('fig-count', figureCount));
345
- const tableCount = this.getChildrenOfType(schema_1.schema.nodes.table).length;
346
- countingElements.push(this.buildCountingElement('table-count', tableCount));
347
- const equationCount = this.getChildrenOfType(schema_1.schema.nodes.equation_element).length;
348
- countingElements.push(this.buildCountingElement('equation-count', equationCount));
349
- const referencesCount = this.getChildrenOfType(schema_1.schema.nodes.bibliography_item).length;
350
- countingElements.push(this.buildCountingElement('ref-count', referencesCount));
351
- const wordCount = this.manuscriptNode.textContent.split(/\s+/).length;
352
- countingElements.push(this.buildCountingElement('word-count', wordCount));
353
- countingElements = countingElements.filter((el) => el);
354
- if (countingElements.length > 0) {
355
- const counts = this.createElement('counts');
356
- counts.append(...countingElements);
357
- articleMeta.append(counts);
335
+ };
336
+ this.buildAwards = () => {
337
+ const awards = this.getFirstChildOfType(schema_1.schema.nodes.awards);
338
+ if (!awards || !awards.childCount) {
339
+ return;
358
340
  }
359
- const attachments = this.getChildrenOfType(schema_1.schema.nodes.attachment);
360
- attachments.forEach((attachment) => {
361
- const $selfUri = this.createElement('self-uri');
362
- $selfUri.setAttribute('content-type', attachment.attrs.type);
363
- $selfUri.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', attachment.attrs.href);
364
- const insertBeforeElements = articleMeta.querySelector('related-article, related-object, abstract, trans-abstract, kwd-group, funding-group, support-group, conference, counts, custom-meta-group');
365
- insertBeforeElements
366
- ? articleMeta.insertBefore($selfUri, insertBeforeElements)
367
- : articleMeta.appendChild($selfUri);
368
- });
369
- return front;
341
+ return this.serializeNode(awards);
370
342
  };
343
+ this.buildSelfUris = () => this.getChildrenOfType(schema_1.schema.nodes.attachment).map((attachment) => this.serializeNode(attachment));
371
344
  this.buildDateElement = (timestamp, type) => {
372
- const dateElement = this.createElement('date');
373
- dateElement.setAttribute('date-type', type);
345
+ const $date = this.createElement('date');
346
+ $date.setAttribute('date-type', type);
374
347
  const date = new Date(timestamp * 1000);
375
348
  const lookup = {
376
349
  year: date.getUTCFullYear().toString(),
@@ -378,127 +351,154 @@ class JATSExporter {
378
351
  day: date.getUTCDate().toString().padStart(2, '0'),
379
352
  };
380
353
  for (const [key, value] of Object.entries(lookup).reverse()) {
381
- const el = this.createElement(key);
382
- el.textContent = value;
383
- dateElement.appendChild(el);
384
- }
385
- return dateElement;
386
- };
387
- this.buildCountingElement = (tagName, count) => {
388
- if (count) {
389
- const wordCount = this.createElement(tagName);
390
- wordCount.setAttribute('count', String(count));
391
- return wordCount;
354
+ const $datePart = this.createElement(key, value);
355
+ $date.appendChild($datePart);
392
356
  }
357
+ return $date;
393
358
  };
394
359
  this.buildBody = () => {
395
- const body = this.createElement('body');
396
- this.manuscriptNode.forEach((cFragment) => {
397
- const serializedNode = this.serializeNode(cFragment);
398
- body.append(...serializedNode.childNodes);
360
+ const body = this.getFirstChildOfType(schema_1.schema.nodes.body);
361
+ const $body = this.createElement('body');
362
+ body?.forEach((node) => {
363
+ $body.appendChild(this.serializeNode(node));
399
364
  });
400
- this.fixBody(body);
401
- return body;
365
+ return $body;
402
366
  };
403
- this.buildBack = (body) => {
404
- const back = this.createElement('back');
405
- this.moveSectionsToBack(back, body);
406
- const footnotesElements = this.document.querySelectorAll('sec > fn-group');
407
- for (const footnotesElement of footnotesElements) {
408
- const previousParent = footnotesElement.parentElement;
409
- back.appendChild(footnotesElement);
410
- if (previousParent) {
411
- const title = previousParent.querySelector('title');
412
- if (title) {
413
- footnotesElement.insertBefore(title, footnotesElement.firstElementChild);
414
- }
415
- if (!previousParent.childElementCount) {
416
- previousParent.remove();
417
- }
367
+ this.buildBack = () => {
368
+ const backmatter = this.getFirstChildOfType(schema_1.schema.nodes.backmatter);
369
+ const $back = this.createElement('back');
370
+ backmatter?.forEach((node) => {
371
+ $back.appendChild(this.serializeNode(node));
372
+ });
373
+ return $back;
374
+ };
375
+ this.findSection = (category, ...$roots) => {
376
+ for (const $root of $roots) {
377
+ const $match = $root.querySelector(`sec[sec-type="${category}"]`);
378
+ if ($match) {
379
+ return $match;
418
380
  }
419
381
  }
420
- let refList = this.document.querySelector('ref-list');
421
- if (!refList) {
422
- refList = this.createElement('ref-list');
382
+ };
383
+ this.findSections = (category, ...$roots) => $roots.flatMap(($root) => Array.from($root.querySelectorAll(`sec[sec-type="${category}"]`)));
384
+ this.routeBackmatterSections = ($body, $back) => {
385
+ const $footnoteSections = section_categories_1.FOOTNOTE_SECTION_CATEGORY_IDS.flatMap((category) => this.findSections(category, $body, $back).map(($section) => ({
386
+ $section,
387
+ category,
388
+ })));
389
+ let $fnGroup;
390
+ if ($footnoteSections.length) {
391
+ $fnGroup = this.createElement('fn-group');
392
+ for (const { $section, category } of $footnoteSections) {
393
+ $fnGroup.appendChild(this.sectionToFootnote($section, category));
394
+ }
423
395
  }
424
- back.appendChild(refList);
425
- const parser = new DOMParser();
426
- const [_, bibliography] = this.engine.makeBibliography();
427
- for (let i = 0; i < bibliography.length; i++) {
428
- const item = `<template xmlns:xlink="${xml_1.XLINK_NAMESPACE}">${(0, xml_1.sanitizeXmlString)(bibliography[i])}</template>`;
429
- const ref = parser.parseFromString(item, 'text/xml').querySelector('ref');
430
- if (ref) {
431
- refList.appendChild(ref);
396
+ const $appendices = this.findSections('appendices', $body, $back);
397
+ let $appGroup;
398
+ if ($appendices.length) {
399
+ $appGroup = this.createElement('app-group');
400
+ for (const $section of $appendices) {
401
+ const $app = this.createElement('app');
402
+ $app.appendChild($section);
403
+ $appGroup.appendChild($app);
432
404
  }
433
405
  }
434
- return back;
406
+ const $ackSection = this.findSection('acknowledgements', $body, $back);
407
+ let $ack;
408
+ if ($ackSection) {
409
+ $ack = this.createElement('ack');
410
+ $ack.append(...$ackSection.childNodes);
411
+ $ackSection.remove();
412
+ }
413
+ const $availabilitySection = this.findSection('availability', $body, $back);
414
+ const $ethicsSection = this.findSection('ethics-statement', $body, $back);
415
+ const $prepend = [
416
+ $appGroup,
417
+ $ack,
418
+ $availabilitySection,
419
+ $ethicsSection,
420
+ $fnGroup,
421
+ ].filter((el) => el != null);
422
+ if ($prepend.length) {
423
+ $back.prepend(...$prepend);
424
+ }
425
+ };
426
+ this.extractCoiStatement = ($back) => {
427
+ const $coiStatement = $back.querySelector('fn[fn-type="coi-statement"]');
428
+ if (!$coiStatement) {
429
+ return undefined;
430
+ }
431
+ const $fnGroup = $coiStatement.parentElement;
432
+ $coiStatement.remove();
433
+ if ($fnGroup && !$fnGroup.hasChildNodes()) {
434
+ $fnGroup.remove();
435
+ }
436
+ return $coiStatement;
437
+ };
438
+ this.buildRefList = () => {
439
+ const $refList = this.createElement('ref-list');
440
+ const [, bibliography] = this.engine.makeBibliography();
441
+ const parser = new DOMParser();
442
+ bibliography.forEach((item) => {
443
+ const fragment = `<template xmlns:xlink="${xml_1.XLINK_NAMESPACE}">${(0, xml_1.sanitizeXmlString)(item)}</template>`;
444
+ const $ref = parser
445
+ .parseFromString(fragment, 'text/xml')
446
+ .querySelector('ref');
447
+ if ($ref) {
448
+ $refList.appendChild($ref);
449
+ }
450
+ });
451
+ return $refList;
435
452
  };
436
453
  this.createElement = (tag, content, attrs) => {
437
- const el = this.document.createElement(tag);
454
+ const $element = this.document.createElement(tag);
438
455
  if (content) {
439
- el.textContent = content;
456
+ $element.textContent = content;
440
457
  }
441
458
  if (attrs) {
442
459
  Object.entries(attrs).forEach(([k, v]) => {
443
460
  if (v) {
444
- el.setAttribute(k, v);
461
+ $element.setAttribute(k, v);
445
462
  }
446
463
  });
447
464
  }
448
- return el;
465
+ return $element;
449
466
  };
450
- this.appendElement = (parent, tag, content, attrs) => {
451
- const el = this.createElement(tag, content, attrs);
452
- parent.appendChild(el);
453
- return el;
467
+ this.appendElement = ($parent, tag, content, attrs) => {
468
+ const $element = this.createElement(tag, content, attrs);
469
+ $parent.appendChild($element);
470
+ return $element;
454
471
  };
455
472
  this.createSerializer = () => {
456
473
  const nodes = {
457
- trans_abstract: (node) => {
458
- const attrs = {
459
- id: normalizeID(node.attrs.id),
460
- };
461
- if (node.attrs.lang) {
462
- attrs[`${xml_1.XML_NAMESPACE} lang`] = node.attrs.lang;
463
- }
464
- if (node.attrs.category) {
465
- attrs['sec-type'] = node.attrs.category;
466
- }
467
- return ['trans-abstract', attrs, 0];
468
- },
469
- trans_graphical_abstract: (node) => {
470
- const attrs = {
471
- id: normalizeID(node.attrs.id),
472
- };
473
- if (node.attrs.lang) {
474
- attrs[`${xml_1.XML_NAMESPACE} lang`] = node.attrs.lang;
475
- }
476
- if (node.attrs.category) {
477
- attrs['sec-type'] = node.attrs.category;
478
- }
479
- return ['trans-abstract', attrs, 0];
480
- },
474
+ trans_abstract: (node) => createTransAbstract(node),
475
+ trans_graphical_abstract: (node) => createTransAbstract(node),
481
476
  hero_image: () => '',
482
477
  headshot_grid: () => ['p', { 'content-type': 'headshots' }, 0],
483
478
  headshot_element: (node) => createImage(node),
484
479
  headshot_image: () => '',
485
480
  alt_text: (node) => {
486
481
  if (node.textContent) {
487
- const altText = this.createElement('alt-text');
488
- altText.textContent = node.textContent;
489
- return altText;
482
+ const $altText = this.createElement('alt-text');
483
+ $altText.textContent = node.textContent;
484
+ return $altText;
490
485
  }
491
486
  return '';
492
487
  },
493
488
  long_desc: (node) => {
494
489
  if (node.textContent) {
495
- const longDesc = this.createElement('long-desc');
496
- longDesc.textContent = node.textContent;
497
- return longDesc;
490
+ const $longDesc = this.createElement('long-desc');
491
+ $longDesc.textContent = node.textContent;
492
+ return $longDesc;
498
493
  }
499
494
  return '';
500
495
  },
501
- attachment: () => '',
496
+ attachment: (node) => {
497
+ const $selfUri = this.createElement('self-uri');
498
+ $selfUri.setAttribute('content-type', node.attrs.type);
499
+ $selfUri.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', node.attrs.href);
500
+ return $selfUri;
501
+ },
502
502
  attachments: () => '',
503
503
  image_element: (node) => createImage(node),
504
504
  embed: (node) => {
@@ -506,41 +506,45 @@ class JATSExporter {
506
506
  if (!href) {
507
507
  return '';
508
508
  }
509
- const mediaElement = this.createElement('media');
510
- mediaElement.setAttribute('id', normalizeID(id));
511
- mediaElement.setAttributeNS(xml_1.XLINK_NAMESPACE, 'show', 'embed');
512
- mediaElement.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', href);
509
+ const $media = this.createElement('media');
510
+ $media.setAttribute('id', normalizeID(id));
511
+ $media.setAttributeNS(xml_1.XLINK_NAMESPACE, 'show', 'embed');
512
+ $media.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', href);
513
513
  if (mimetype) {
514
- mediaElement.setAttribute('mimetype', node.attrs.mimetype);
514
+ $media.setAttribute('mimetype', node.attrs.mimetype);
515
515
  }
516
516
  if (mimeSubtype) {
517
- mediaElement.setAttribute('mime-subtype', node.attrs.mimeSubtype);
517
+ $media.setAttribute('mime-subtype', node.attrs.mimeSubtype);
518
518
  }
519
- appendLabels(mediaElement, node);
520
- this.appendChildNodeOfType(mediaElement, node, schema_1.schema.nodes.alt_text);
521
- this.appendChildNodeOfType(mediaElement, node, schema_1.schema.nodes.long_desc);
522
- this.appendCaption(mediaElement, node);
523
- return mediaElement;
519
+ appendLabels($media, node);
520
+ this.appendChildNodeOfType($media, node, schema_1.schema.nodes.alt_text);
521
+ this.appendChildNodeOfType($media, node, schema_1.schema.nodes.long_desc);
522
+ this.appendCaption($media, node);
523
+ return $media;
524
524
  },
525
525
  awards: () => ['funding-group', 0],
526
526
  award: (node) => {
527
527
  const awardGroup = node;
528
- const awardGroupElement = this.createElement('award-group');
529
- awardGroupElement.setAttribute('id', normalizeID(awardGroup.attrs.id));
530
- appendChildIfPresent(awardGroupElement, 'funding-source', awardGroup.attrs.source);
528
+ const $awardGroup = this.createElement('award-group');
529
+ $awardGroup.setAttribute('id', normalizeID(awardGroup.attrs.id));
530
+ appendChildIfPresent($awardGroup, 'funding-source', awardGroup.attrs.source);
531
531
  awardGroup.attrs.code
532
532
  ?.split(';')
533
- .forEach((code) => appendChildIfPresent(awardGroupElement, 'award-id', code));
534
- appendChildIfPresent(awardGroupElement, 'principal-award-recipient', awardGroup.attrs.recipient);
535
- return awardGroupElement;
533
+ .forEach((code) => appendChildIfPresent($awardGroup, 'award-id', code));
534
+ appendChildIfPresent($awardGroup, 'principal-award-recipient', awardGroup.attrs.recipient);
535
+ return $awardGroup;
536
536
  },
537
537
  box_element: (node) => createBoxElement(node),
538
538
  author_notes: () => '',
539
539
  corresp: () => '',
540
- title: () => '',
541
- alt_title: () => '',
540
+ title: () => ['article-title', 0],
541
+ alt_title: (node) => [
542
+ 'alt-title',
543
+ { 'alt-title-type': node.attrs.type },
544
+ 0,
545
+ ],
542
546
  alt_titles: () => '',
543
- subtitle: () => '',
547
+ subtitle: () => ['subtitle', 0],
544
548
  subtitles: () => '',
545
549
  text_block: (node) => nodes.paragraph(node),
546
550
  affiliations: () => '',
@@ -548,17 +552,31 @@ class JATSExporter {
548
552
  table_element_footer: (node) => node.childCount == 0
549
553
  ? ['table-wrap-foot', ['fn-group', ['fn', ['p']]]]
550
554
  : ['table-wrap-foot', 0],
551
- contributor: () => '',
552
- affiliation: () => '',
555
+ contributor: (node) => this.buildContributorElement(node),
556
+ affiliation: (node) => this.buildAffiliationElement(node),
553
557
  attribution: () => ['attrib', 0],
554
558
  bibliography_element: () => '',
555
559
  bibliography_item: () => '',
556
560
  comments: () => '',
557
- keyword_group: () => '',
561
+ keyword_group: (node) => {
562
+ const attrs = {};
563
+ if (node.attrs.type) {
564
+ attrs['kwd-group-type'] = node.attrs.type;
565
+ }
566
+ return ['kwd-group', attrs, 0];
567
+ },
558
568
  body: () => ['body', 0],
559
569
  abstracts: () => ['abstract', 0],
560
570
  backmatter: () => ['backmatter', 0],
561
- supplement: () => '',
571
+ supplement: (node) => {
572
+ const $supplementaryMaterial = this.createElement('supplementary-material');
573
+ $supplementaryMaterial.setAttribute('id', normalizeID(node.attrs.id));
574
+ $supplementaryMaterial.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', node.attrs.href ?? '');
575
+ $supplementaryMaterial.setAttribute('mimetype', node.attrs.mimeType ?? '');
576
+ $supplementaryMaterial.setAttribute('mime-subtype', node.attrs.mimeSubType ?? '');
577
+ this.appendCaption($supplementaryMaterial, node);
578
+ return $supplementaryMaterial;
579
+ },
562
580
  supplements: () => '',
563
581
  bibliography_section: () => '',
564
582
  blockquote_element: () => ['disp-quote', { 'content-type': 'quote' }, 0],
@@ -582,14 +600,14 @@ class JATSExporter {
582
600
  if (!rids.length) {
583
601
  return '';
584
602
  }
585
- const xref = this.createElement('xref');
586
- xref.setAttribute('ref-type', 'bibr');
587
- xref.setAttribute('rid', normalizeID(rids.join(' ')));
603
+ const $xref = this.createElement('xref');
604
+ $xref.setAttribute('ref-type', 'bibr');
605
+ $xref.setAttribute('rid', normalizeID(rids.join(' ')));
588
606
  const fragment = this.renderedCitations.get(node.attrs.id);
589
607
  if (fragment) {
590
- xref.innerHTML = fragment;
608
+ $xref.innerHTML = fragment;
591
609
  }
592
- return xref;
610
+ return $xref;
593
611
  },
594
612
  cross_reference: (node) => {
595
613
  const cross = node;
@@ -603,40 +621,41 @@ class JATSExporter {
603
621
  if (!target) {
604
622
  return text ?? '';
605
623
  }
606
- const xref = this.createElement('xref');
624
+ const $xref = this.createElement('xref');
607
625
  const type = chooseRefType(target.type);
608
626
  if (type) {
609
- xref.setAttribute('ref-type', type);
627
+ $xref.setAttribute('ref-type', type);
610
628
  }
611
- xref.setAttribute('rid', normalizeID(rids.join(' ')));
612
- xref.textContent = text ?? '';
613
- return xref;
629
+ $xref.setAttribute('rid', normalizeID(rids.join(' ')));
630
+ $xref.textContent = text ?? '';
631
+ return $xref;
614
632
  },
615
633
  doc: () => '',
616
634
  equation: (node) => {
617
635
  return node.attrs.contents ? this.createEquation(node) : '';
618
636
  },
619
637
  general_table_footnote: (node) => {
620
- const el = this.createElement('general-table-footnote');
621
- el.setAttribute('id', normalizeID(node.attrs.id));
622
- processChildNodes(el, node, schema_1.schema.nodes.general_table_footnote);
623
- return el;
638
+ const $fragment = this.document.createDocumentFragment();
639
+ node.forEach((child) => {
640
+ $fragment.appendChild(this.serializeNode(child));
641
+ });
642
+ return $fragment;
624
643
  },
625
644
  inline_equation: (node) => {
626
645
  if (!node.attrs.contents) {
627
646
  return '';
628
647
  }
629
- const eqElement = this.createElement('inline-formula');
648
+ const $inlineFormula = this.createElement('inline-formula');
630
649
  const equation = this.createEquation(node, true);
631
- eqElement.append(equation);
632
- return eqElement;
650
+ $inlineFormula.append(equation);
651
+ return $inlineFormula;
633
652
  },
634
653
  equation_element: (node) => {
635
- const eqElement = this.createElement('disp-formula');
636
- eqElement.setAttribute('id', normalizeID(node.attrs.id));
637
- appendLabels(eqElement, node);
638
- processChildNodes(eqElement, node, schema_1.schema.nodes.equation);
639
- return eqElement;
654
+ const $dispFormula = this.createElement('disp-formula');
655
+ $dispFormula.setAttribute('id', normalizeID(node.attrs.id));
656
+ appendLabels($dispFormula, node);
657
+ processChildNodes($dispFormula, node, schema_1.schema.nodes.equation);
658
+ return $dispFormula;
640
659
  },
641
660
  figure: (node) => createGraphic(node),
642
661
  figure_element: (node) => createFigureElement(node, node.type.schema.nodes.figure),
@@ -654,25 +673,39 @@ class JATSExporter {
654
673
  ? ['fn-group', { id: normalizeID(node.attrs.id) }, ['fn', ['p']]]
655
674
  : ['fn-group', { id: normalizeID(node.attrs.id) }, 0],
656
675
  footnotes_section: (node) => {
657
- const attrs = {
676
+ const $fnGroup = this.createElement('fn-group', undefined, {
658
677
  id: normalizeID(node.attrs.id),
659
- 'sec-type': 'endnotes',
660
- };
661
- return ['sec', attrs, 0];
678
+ });
679
+ const titleNode = this.getFirstChildOfType(schema_1.schema.nodes.section_title, node);
680
+ if (titleNode) {
681
+ $fnGroup.appendChild(this.serializeNode(titleNode));
682
+ }
683
+ const footnotesNode = this.getFirstChildOfType(schema_1.schema.nodes.footnotes_element, node);
684
+ if (footnotesNode?.childCount) {
685
+ footnotesNode.forEach((footnote) => {
686
+ $fnGroup.appendChild(this.serializeNode(footnote));
687
+ });
688
+ }
689
+ else {
690
+ const $fn = this.createElement('fn');
691
+ $fn.appendChild(this.createElement('p'));
692
+ $fnGroup.appendChild($fn);
693
+ }
694
+ return $fnGroup;
662
695
  },
663
696
  hard_break: () => '',
664
697
  highlight_marker: () => '',
665
698
  inline_footnote: (node) => {
666
699
  const rids = node.attrs.rids;
667
- const xref = this.createElement('xref');
668
- xref.setAttribute('ref-type', 'fn');
669
- xref.setAttribute('rid', normalizeID(rids.join(' ')));
670
- xref.textContent = rids
700
+ const $xref = this.createElement('xref');
701
+ $xref.setAttribute('ref-type', 'fn');
702
+ $xref.setAttribute('rid', normalizeID(rids.join(' ')));
703
+ $xref.textContent = rids
671
704
  .map((rid) => this.footnoteLabels.get(rid))
672
705
  .join(', ');
673
- return xref;
706
+ return $xref;
674
707
  },
675
- keyword: () => '',
708
+ keyword: () => ['kwd', 0],
676
709
  keywords_element: () => '',
677
710
  keywords: () => '',
678
711
  link: (node) => {
@@ -683,30 +716,30 @@ class JATSExporter {
683
716
  if (!node.attrs.href) {
684
717
  return text;
685
718
  }
686
- const linkNode = this.createElement('ext-link');
687
- linkNode.setAttribute('ext-link-type', 'uri');
688
- linkNode.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', node.attrs.href);
689
- linkNode.textContent = text;
719
+ const $extLink = this.createElement('ext-link');
720
+ $extLink.setAttribute('ext-link-type', 'uri');
721
+ $extLink.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', node.attrs.href);
722
+ $extLink.textContent = text;
690
723
  if (node.attrs.title) {
691
- linkNode.setAttributeNS(xml_1.XLINK_NAMESPACE, 'xlink:title', node.attrs.title);
724
+ $extLink.setAttributeNS(xml_1.XLINK_NAMESPACE, 'xlink:title', node.attrs.title);
692
725
  }
693
- return linkNode;
726
+ return $extLink;
694
727
  },
695
728
  list_item: () => ['list-item', 0],
696
729
  listing: (node) => {
697
- const code = this.createElement('code');
698
- code.setAttribute('id', normalizeID(node.attrs.id));
699
- code.setAttribute('language', node.attrs.languageKey);
700
- code.textContent = node.attrs.contents;
701
- return code;
730
+ const $code = this.createElement('code');
731
+ $code.setAttribute('id', normalizeID(node.attrs.id));
732
+ $code.setAttribute('language', node.attrs.languageKey);
733
+ $code.textContent = node.attrs.contents;
734
+ return $code;
702
735
  },
703
736
  listing_element: (node) => createFigureElement(node, node.type.schema.nodes.listing),
704
737
  manuscript: (node) => ['article', { id: normalizeID(node.attrs.id) }, 0],
705
738
  missing_figure: () => {
706
- const graphic = this.createElement('graphic');
707
- graphic.setAttribute('specific-use', 'MISSING');
708
- graphic.setAttributeNS(xml_1.XLINK_NAMESPACE, 'xlink:href', '');
709
- return graphic;
739
+ const $graphic = this.createElement('graphic');
740
+ $graphic.setAttribute('specific-use', 'MISSING');
741
+ $graphic.setAttributeNS(xml_1.XLINK_NAMESPACE, 'xlink:href', '');
742
+ return $graphic;
710
743
  },
711
744
  paragraph: (node) => {
712
745
  if (!node.childCount) {
@@ -741,15 +774,7 @@ class JATSExporter {
741
774
  }
742
775
  return '';
743
776
  },
744
- graphical_abstract_section: (node) => {
745
- const attrs = {
746
- id: normalizeID(node.attrs.id),
747
- };
748
- if (node.attrs.category) {
749
- attrs['sec-type'] = node.attrs.category;
750
- }
751
- return ['sec', attrs, 0];
752
- },
777
+ graphical_abstract_section: (node) => createAbstract(node),
753
778
  section: (node) => {
754
779
  const attrs = {
755
780
  id: normalizeID(node.attrs.id),
@@ -759,17 +784,18 @@ class JATSExporter {
759
784
  }
760
785
  return ['sec', attrs, 0];
761
786
  },
787
+ abstract: (node) => createAbstract(node),
762
788
  section_label: () => ['label', 0],
763
789
  section_title: () => ['title', 0],
764
790
  section_title_plain: () => ['title', 0],
765
791
  table: (node) => ['table', { id: normalizeID(node.attrs.id) }, 0],
766
792
  table_element: (node) => {
767
- const element = createTableElement(node);
768
- element.setAttribute('position', 'anchor');
793
+ const $tableWrap = createTableElement(node);
794
+ $tableWrap.setAttribute('position', 'anchor');
769
795
  if (node.attrs.type) {
770
- element.setAttribute('content-type', node.attrs.type);
796
+ $tableWrap.setAttribute('content-type', node.attrs.type);
771
797
  }
772
- return element;
798
+ return $tableWrap;
773
799
  },
774
800
  table_cell: (node) => [
775
801
  'td',
@@ -815,84 +841,99 @@ class JATSExporter {
815
841
  tracked_delete: () => ['del'],
816
842
  };
817
843
  this.serializer = new prosemirror_model_1.DOMSerializer(nodes, marks);
818
- const appendChildIfPresent = (parent, tagName, textContent) => {
844
+ const appendChildIfPresent = ($parent, tagName, textContent) => {
819
845
  if (!textContent) {
820
846
  return;
821
847
  }
822
- const element = this.createElement(tagName);
823
- element.textContent = textContent;
824
- parent.appendChild(element);
848
+ const $element = this.createElement(tagName);
849
+ $element.textContent = textContent;
850
+ $parent.appendChild($element);
825
851
  };
826
- const processChildNodes = (element, node, contentNodeType) => {
852
+ const processChildNodes = ($element, node, contentNodeType) => {
827
853
  node.forEach((childNode) => {
828
854
  if (childNode.type === contentNodeType) {
829
855
  if (childNode.attrs.id) {
830
- element.appendChild(this.serializeNode(childNode));
856
+ $element.appendChild(this.serializeNode(childNode));
831
857
  }
832
858
  }
833
859
  else if (childNode.type === node.type.schema.nodes.paragraph) {
834
- element.appendChild(this.serializeNode(childNode));
860
+ $element.appendChild(this.serializeNode(childNode));
835
861
  }
836
862
  else if (childNode.type === node.type.schema.nodes.missing_figure) {
837
- element.appendChild(this.serializeNode(childNode));
863
+ $element.appendChild(this.serializeNode(childNode));
838
864
  }
839
865
  });
840
866
  };
841
867
  const createElement = (node, nodeName) => {
842
- const element = this.createElement(nodeName);
843
- element.setAttribute('id', normalizeID(node.attrs.id));
844
- return element;
868
+ const $element = this.createElement(nodeName);
869
+ $element.setAttribute('id', normalizeID(node.attrs.id));
870
+ return $element;
845
871
  };
846
- const appendLabels = (element, node) => {
872
+ const appendLabels = ($element, node) => {
847
873
  if (this.labelTargets) {
848
874
  const target = this.labelTargets.get(node.attrs.id);
849
875
  if (target) {
850
- const label = this.createElement('label');
851
- label.textContent = target.label;
852
- element.appendChild(label);
876
+ const $label = this.createElement('label');
877
+ $label.textContent = target.label;
878
+ $element.appendChild($label);
853
879
  }
854
880
  }
855
881
  };
856
- const appendAttributions = (element, node) => {
882
+ const appendAttributions = ($element, node) => {
857
883
  if (node.attrs.attribution) {
858
- const attribution = this.createElement('attrib');
859
- attribution.textContent = node.attrs.attribution.literal;
860
- element.appendChild(attribution);
884
+ const $attrib = this.createElement('attrib');
885
+ $attrib.textContent = node.attrs.attribution.literal;
886
+ $element.appendChild($attrib);
861
887
  }
862
888
  };
863
- const appendTable = (element, node) => {
889
+ const appendTable = ($element, node) => {
864
890
  const tableNode = this.getFirstChildOfType(schema_1.schema.nodes.table, node);
865
891
  const colGroupNode = this.getFirstChildOfType(schema_1.schema.nodes.table_colgroup, node);
866
892
  if (!tableNode) {
867
893
  return;
868
894
  }
869
- const table = this.serializeNode(tableNode);
870
- const tbodyElement = this.createElement('tbody');
871
- while (table.firstChild) {
872
- const child = table.firstChild;
873
- table.removeChild(child);
874
- tbodyElement.appendChild(child);
895
+ const $table = this.serializeNode(tableNode);
896
+ const $tbody = this.createElement('tbody');
897
+ while ($table.firstChild) {
898
+ const $child = $table.firstChild;
899
+ $table.removeChild($child);
900
+ $tbody.appendChild($child);
875
901
  }
876
- table.appendChild(tbodyElement);
902
+ $table.appendChild($tbody);
903
+ this.normalizeTable($table);
877
904
  if (colGroupNode) {
878
- const colGroup = this.serializeNode(colGroupNode);
879
- table.insertBefore(colGroup, table.firstChild);
905
+ const $colGroup = this.serializeNode(colGroupNode);
906
+ $table.insertBefore($colGroup, $table.firstChild);
880
907
  }
881
- element.appendChild(table);
908
+ $element.appendChild($table);
882
909
  };
883
910
  const createBoxElement = (node) => {
884
- const element = createElement(node, 'boxed-text');
911
+ const $boxedText = createElement(node, 'boxed-text');
885
912
  if (node.attrs.type) {
886
- element.setAttribute('content-type', node.attrs.type);
913
+ $boxedText.setAttribute('content-type', node.attrs.type);
887
914
  }
888
- appendLabels(element, node);
915
+ appendLabels($boxedText, node);
889
916
  const child = node.firstChild;
890
917
  if (child?.type === schema_1.schema.nodes.caption_title) {
891
- this.appendCaption(element, node);
918
+ this.appendCaption($boxedText, node);
892
919
  }
893
- processChildNodes(element, node, node.type.schema.nodes.section);
894
- return element;
920
+ processChildNodes($boxedText, node, node.type.schema.nodes.section);
921
+ return $boxedText;
895
922
  };
923
+ const abstractTypeAttrs = (category) => category && category !== 'abstract' ? { 'abstract-type': category } : {};
924
+ const createAbstract = (node) => [
925
+ 'abstract',
926
+ abstractTypeAttrs(node.attrs.category),
927
+ 0,
928
+ ];
929
+ const createTransAbstract = (node) => [
930
+ 'trans-abstract',
931
+ {
932
+ [`${xml_1.XML_NAMESPACE} lang`]: node.attrs.lang ?? '',
933
+ ...abstractTypeAttrs(node.attrs.category),
934
+ },
935
+ 0,
936
+ ];
896
937
  const isChildOfNodeType = (targetID, type, descend = false) => {
897
938
  const nodes = this.getChildrenOfType(type);
898
939
  return nodes.some((node) => {
@@ -909,94 +950,94 @@ class JATSExporter {
909
950
  if (!graphicNode) {
910
951
  return '';
911
952
  }
912
- const graphicElement = createGraphic(graphicNode);
953
+ const $graphic = createGraphic(graphicNode);
913
954
  if (node.attrs.extLink) {
914
- const extLink = this.appendElement(graphicElement, 'ext-link');
915
- extLink.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', node.attrs.extLink);
955
+ const $extLink = this.appendElement($graphic, 'ext-link');
956
+ $extLink.setAttributeNS(xml_1.XLINK_NAMESPACE, 'href', node.attrs.extLink);
916
957
  }
917
- this.appendCaption(graphicElement, node);
918
- this.appendChildNodeOfType(graphicElement, node, schema_1.schema.nodes.alt_text);
919
- this.appendChildNodeOfType(graphicElement, node, schema_1.schema.nodes.long_desc);
920
- return graphicElement;
958
+ this.appendCaption($graphic, node);
959
+ this.appendChildNodeOfType($graphic, node, schema_1.schema.nodes.alt_text);
960
+ this.appendChildNodeOfType($graphic, node, schema_1.schema.nodes.long_desc);
961
+ return $graphic;
921
962
  };
922
963
  const createGraphic = (node) => {
923
- const graphic = this.createElement('graphic');
924
- graphic.setAttributeNS(xml_1.XLINK_NAMESPACE, 'xlink:href', node.attrs.src);
964
+ const $graphic = this.createElement('graphic');
965
+ $graphic.setAttributeNS(xml_1.XLINK_NAMESPACE, 'xlink:href', node.attrs.src);
925
966
  const hero = findParentHeroImage(node.attrs.id);
926
967
  if (hero) {
927
- graphic.setAttribute('content-type', hero.attrs.type || 'leading');
968
+ $graphic.setAttribute('content-type', hero.attrs.type || 'leading');
928
969
  }
929
970
  else if (!isChildOfNodeType(node.attrs.id, schema_1.schema.nodes.figure_element) &&
930
971
  node.attrs.type) {
931
- graphic.setAttribute('content-type', node.attrs.type);
972
+ $graphic.setAttribute('content-type', node.attrs.type);
932
973
  }
933
- return graphic;
974
+ return $graphic;
934
975
  };
935
976
  const createFigureElement = (node, contentNodeType) => {
936
- const element = createElement(node, 'fig');
977
+ const $fig = createElement(node, 'fig');
937
978
  const figNode = this.getFirstChildOfType(schema_1.schema.nodes.figure, node);
938
979
  const figType = figNode?.attrs.type;
939
980
  if (figType) {
940
- element.setAttribute('fig-type', figType);
981
+ $fig.setAttribute('fig-type', figType);
941
982
  }
942
- appendLabels(element, node);
943
- this.appendCaption(element, node);
944
- this.appendChildNodeOfType(element, node, schema_1.schema.nodes.alt_text);
945
- this.appendChildNodeOfType(element, node, schema_1.schema.nodes.long_desc);
946
- this.appendChildNodeOfType(element, node, node.type.schema.nodes.footnotes_element);
947
- processChildNodes(element, node, contentNodeType);
948
- appendAttributions(element, node);
983
+ appendLabels($fig, node);
984
+ this.appendCaption($fig, node);
985
+ this.appendChildNodeOfType($fig, node, schema_1.schema.nodes.alt_text);
986
+ this.appendChildNodeOfType($fig, node, schema_1.schema.nodes.long_desc);
987
+ this.appendChildNodeOfType($fig, node, node.type.schema.nodes.footnotes_element);
988
+ processChildNodes($fig, node, contentNodeType);
989
+ appendAttributions($fig, node);
949
990
  if ((0, transformer_1.isExecutableNodeType)(node.type)) {
950
- processExecutableNode(node, element);
991
+ processExecutableNode(node, $fig);
951
992
  }
952
- moveAltTextAndLongDescToGraphics(element);
953
- return element;
993
+ moveAltTextAndLongDescToGraphics($fig);
994
+ return $fig;
954
995
  };
955
- const moveAltTextAndLongDescToGraphics = (element) => {
956
- const altText = element.querySelector('alt-text');
957
- const longDesc = element.querySelector('long-desc');
958
- const graphics = element.querySelectorAll('graphic');
959
- if (graphics.length === 0) {
996
+ const moveAltTextAndLongDescToGraphics = ($element) => {
997
+ const $altText = $element.querySelector('alt-text');
998
+ const $longDesc = $element.querySelector('long-desc');
999
+ const $graphics = $element.querySelectorAll('graphic');
1000
+ if ($graphics.length === 0) {
960
1001
  return;
961
1002
  }
962
- graphics.forEach((graphic) => {
963
- if (longDesc) {
964
- graphic.prepend(longDesc.cloneNode(true));
1003
+ $graphics.forEach(($graphic) => {
1004
+ if ($longDesc) {
1005
+ $graphic.prepend($longDesc.cloneNode(true));
965
1006
  }
966
- if (altText) {
967
- graphic.prepend(altText.cloneNode(true));
1007
+ if ($altText) {
1008
+ $graphic.prepend($altText.cloneNode(true));
968
1009
  }
969
1010
  });
970
- altText?.remove();
971
- longDesc?.remove();
1011
+ $altText?.remove();
1012
+ $longDesc?.remove();
972
1013
  };
973
1014
  const createTableElement = (node) => {
974
1015
  const nodeName = 'table-wrap';
975
- const element = createElement(node, nodeName);
976
- appendLabels(element, node);
977
- this.appendCaption(element, node);
978
- this.appendChildNodeOfType(element, node, schema_1.schema.nodes.alt_text);
979
- this.appendChildNodeOfType(element, node, schema_1.schema.nodes.long_desc);
980
- appendTable(element, node);
981
- this.appendChildNodeOfType(element, node, node.type.schema.nodes.table_element_footer);
1016
+ const $tableWrap = createElement(node, nodeName);
1017
+ appendLabels($tableWrap, node);
1018
+ this.appendCaption($tableWrap, node);
1019
+ this.appendChildNodeOfType($tableWrap, node, schema_1.schema.nodes.alt_text);
1020
+ this.appendChildNodeOfType($tableWrap, node, schema_1.schema.nodes.long_desc);
1021
+ appendTable($tableWrap, node);
1022
+ this.appendChildNodeOfType($tableWrap, node, node.type.schema.nodes.table_element_footer);
982
1023
  if ((0, transformer_1.isExecutableNodeType)(node.type)) {
983
- processExecutableNode(node, element);
1024
+ processExecutableNode(node, $tableWrap);
984
1025
  }
985
- return element;
1026
+ return $tableWrap;
986
1027
  };
987
- const processExecutableNode = (node, element) => {
1028
+ const processExecutableNode = (node, $element) => {
988
1029
  const listingNode = this.getFirstChildOfType(schema_1.schema.nodes.listing, node);
989
1030
  if (listingNode) {
990
1031
  const { contents, languageKey } = listingNode.attrs;
991
1032
  if (contents && languageKey) {
992
- const listing = this.createElement('fig');
993
- listing.setAttribute('specific-use', 'source');
994
- element.appendChild(listing);
995
- const code = this.createElement('code');
996
- code.setAttribute('executable', 'true');
997
- code.setAttribute('language', languageKey);
998
- code.textContent = contents;
999
- listing.appendChild(code);
1033
+ const $fig = this.createElement('fig');
1034
+ $fig.setAttribute('specific-use', 'source');
1035
+ $element.appendChild($fig);
1036
+ const $code = this.createElement('code');
1037
+ $code.setAttribute('executable', 'true');
1038
+ $code.setAttribute('language', languageKey);
1039
+ $code.textContent = contents;
1040
+ $fig.appendChild($code);
1000
1041
  }
1001
1042
  }
1002
1043
  };
@@ -1004,201 +1045,187 @@ class JATSExporter {
1004
1045
  this.serializeNode = (node) => this.serializer.serializeNode(node, {
1005
1046
  document: this.document,
1006
1047
  });
1007
- this.buildContributors = (articleMeta) => {
1008
- const contributors = this.getChildrenOfType(schema_1.schema.nodes.contributor).sort(sortContributors);
1048
+ this.buildContributorGroup = (contributors) => {
1009
1049
  if (!contributors.length) {
1010
1050
  return;
1011
1051
  }
1012
- const labels = new Map();
1013
- const createLabel = (id) => {
1014
- let label = labels.get(id);
1015
- if (!label) {
1016
- label = labels.size + 1;
1017
- labels.set(id, label);
1052
+ const affiliationIDs = contributors.flatMap((n) => n.attrs.affiliationIDs);
1053
+ const affiliationOrder = new Map();
1054
+ affiliationIDs.forEach((id, index) => {
1055
+ if (!affiliationOrder.has(id)) {
1056
+ affiliationOrder.set(id, index);
1018
1057
  }
1019
- const sup = this.createElement('sup');
1020
- sup.textContent = String(label);
1021
- return sup;
1022
- };
1058
+ });
1059
+ const affiliations = this.getChildrenOfType(schema_1.schema.nodes.affiliation)
1060
+ .filter((a) => affiliationIDs.includes(a.attrs.id))
1061
+ .sort((a, b) => (affiliationOrder.get(a.attrs.id) ?? Infinity) -
1062
+ (affiliationOrder.get(b.attrs.id) ?? Infinity));
1023
1063
  const $contribGroup = this.createElement('contrib-group');
1024
1064
  $contribGroup.setAttribute('content-type', 'authors');
1025
- articleMeta.appendChild($contribGroup);
1026
1065
  contributors.forEach((contributor) => {
1027
- const $contrib = this.createElement('contrib');
1028
- $contrib.setAttribute('contrib-type', 'author');
1029
- $contrib.setAttribute('id', normalizeID(contributor.attrs.id));
1030
- if (contributor.attrs.isCorresponding) {
1031
- $contrib.setAttribute('corresp', 'yes');
1032
- }
1033
- if (contributor.attrs.role) {
1034
- this.appendElement($contrib, 'role', contributor.attrs.role);
1035
- }
1036
- if (contributor.attrs.ORCID) {
1037
- const contribIdAttrs = {
1038
- 'contrib-id-type': 'orcid',
1039
- };
1040
- if (contributor.attrs.isAuthenticated) {
1041
- contribIdAttrs['authenticated'] = 'true';
1042
- }
1043
- this.appendElement($contrib, 'contrib-id', contributor.attrs.ORCID, contribIdAttrs);
1044
- }
1045
- const $name = this.buildContributorName(contributor);
1046
- $contrib.appendChild($name);
1047
- if (contributor.attrs.suffix) {
1048
- this.appendElement($name, 'suffix', contributor.attrs.suffix);
1066
+ $contribGroup.appendChild(this.serializeNode(contributor));
1067
+ });
1068
+ affiliations.forEach((affiliation) => {
1069
+ $contribGroup.appendChild(this.serializeNode(affiliation));
1070
+ });
1071
+ return $contribGroup;
1072
+ };
1073
+ this.buildAuthorNotes = ($coiStatement) => {
1074
+ const authorNotes = this.getFirstChildOfType(schema_1.schema.nodes.author_notes);
1075
+ const $authorNotes = this.createElement('author-notes');
1076
+ if (authorNotes) {
1077
+ this.appendAuthorNotes($authorNotes, authorNotes);
1078
+ }
1079
+ if ($coiStatement) {
1080
+ $authorNotes.appendChild($coiStatement);
1081
+ }
1082
+ return $authorNotes.hasChildNodes() ? $authorNotes : undefined;
1083
+ };
1084
+ this.computeContributorLabels = (contributors) => {
1085
+ const labels = new Map();
1086
+ const assign = (id) => {
1087
+ if (!labels.has(id)) {
1088
+ labels.set(id, labels.size + 1);
1049
1089
  }
1050
- contributor.attrs.degrees?.forEach((degree) => {
1051
- this.appendElement($contrib, 'degrees', degree);
1052
- });
1053
- if (contributor.attrs.email) {
1054
- this.appendElement($contrib, 'email', contributor.attrs.email);
1090
+ };
1091
+ contributors.forEach((contributor) => {
1092
+ contributor.attrs.affiliationIDs?.forEach(assign);
1093
+ contributor.attrs.footnoteIDs?.forEach(assign);
1094
+ contributor.attrs.correspIDs?.forEach(assign);
1095
+ });
1096
+ return labels;
1097
+ };
1098
+ this.createLabelSup = (id) => {
1099
+ const $sup = this.createElement('sup');
1100
+ $sup.textContent = String(this.contributorLabels.get(id));
1101
+ return $sup;
1102
+ };
1103
+ this.buildContributorElement = (contributor) => {
1104
+ const $contrib = this.createElement('contrib');
1105
+ $contrib.setAttribute('contrib-type', 'author');
1106
+ $contrib.setAttribute('id', normalizeID(contributor.attrs.id));
1107
+ if (contributor.attrs.isCorresponding) {
1108
+ $contrib.setAttribute('corresp', 'yes');
1109
+ }
1110
+ if (contributor.attrs.role) {
1111
+ this.appendElement($contrib, 'role', contributor.attrs.role);
1112
+ }
1113
+ if (contributor.attrs.ORCID) {
1114
+ const contribIDAttrs = {
1115
+ 'contrib-id-type': 'orcid',
1116
+ };
1117
+ if (contributor.attrs.isAuthenticated) {
1118
+ contribIDAttrs['authenticated'] = 'true';
1055
1119
  }
1056
- contributor.attrs.affiliationIDs?.forEach((rid) => {
1057
- const $xref = this.appendElement($contrib, 'xref', '', {
1058
- 'ref-type': 'aff',
1059
- rid: normalizeID(rid),
1060
- });
1061
- $xref.appendChild(createLabel(rid));
1062
- });
1063
- contributor.attrs.footnoteIDs?.forEach((rid) => {
1064
- const $xref = this.appendElement($contrib, 'xref', '', {
1065
- 'ref-type': 'fn',
1066
- rid: normalizeID(rid),
1067
- });
1068
- $xref.appendChild(createLabel(rid));
1120
+ this.appendElement($contrib, 'contrib-id', contributor.attrs.ORCID, contribIDAttrs);
1121
+ }
1122
+ const $name = this.buildContributorName(contributor);
1123
+ $contrib.appendChild($name);
1124
+ if (contributor.attrs.suffix) {
1125
+ this.appendElement($name, 'suffix', contributor.attrs.suffix);
1126
+ }
1127
+ contributor.attrs.degrees?.forEach((degree) => {
1128
+ this.appendElement($contrib, 'degrees', degree);
1129
+ });
1130
+ if (contributor.attrs.email) {
1131
+ this.appendElement($contrib, 'email', contributor.attrs.email);
1132
+ }
1133
+ contributor.attrs.affiliationIDs?.forEach((rid) => {
1134
+ const $xref = this.appendElement($contrib, 'xref', '', {
1135
+ 'ref-type': 'aff',
1136
+ rid: normalizeID(rid),
1069
1137
  });
1070
- contributor.attrs.correspIDs?.forEach((rid) => {
1071
- const $xref = this.appendElement($contrib, 'xref', '', {
1072
- 'ref-type': 'corresp',
1073
- rid: normalizeID(rid),
1074
- });
1075
- $xref.appendChild(createLabel(rid));
1138
+ $xref.appendChild(this.createLabelSup(rid));
1139
+ });
1140
+ contributor.attrs.footnoteIDs?.forEach((rid) => {
1141
+ const $xref = this.appendElement($contrib, 'xref', '', {
1142
+ 'ref-type': 'fn',
1143
+ rid: normalizeID(rid),
1076
1144
  });
1077
- contributor.attrs.creditRoles?.forEach((credit) => {
1078
- const url = credit_roles_1.CreditRoleUrls.get(credit.vocabTerm);
1079
- if (!url) {
1080
- return;
1081
- }
1082
- this.appendElement($contrib, 'role', credit.vocabTerm, {
1083
- 'vocab-identifier': 'http://credit.niso.org/',
1084
- vocab: 'CRediT',
1085
- 'vocab-term': credit.vocabTerm,
1086
- 'vocab-term-identifier': url,
1087
- });
1145
+ $xref.appendChild(this.createLabelSup(rid));
1146
+ });
1147
+ contributor.attrs.correspIDs?.forEach((rid) => {
1148
+ const $xref = this.appendElement($contrib, 'xref', '', {
1149
+ 'ref-type': 'corresp',
1150
+ rid: normalizeID(rid),
1088
1151
  });
1089
- $contribGroup.appendChild($contrib);
1152
+ $xref.appendChild(this.createLabelSup(rid));
1090
1153
  });
1091
- const affiliationIDs = contributors.flatMap((n) => n.attrs.affiliationIDs);
1092
- const sortAffiliations = (a, b) => affiliationIDs.indexOf(a.attrs.id) - affiliationIDs.indexOf(b.attrs.id);
1093
- this.getChildrenOfType(schema_1.schema.nodes.affiliation)
1094
- .filter((a) => affiliationIDs.includes(a.attrs.id))
1095
- .sort(sortAffiliations)
1096
- .forEach((affiliation) => {
1097
- const $content = [];
1098
- if (affiliation.attrs.department) {
1099
- const $institution = this.createElement('institution', affiliation.attrs.department, {
1100
- 'content-type': 'dept',
1101
- });
1102
- $content.push($institution);
1103
- }
1104
- if (affiliation.attrs.institution) {
1105
- const $institution = this.createElement('institution', affiliation.attrs.institution);
1106
- $content.push($institution);
1107
- }
1108
- if (affiliation.attrs.addressLine1) {
1109
- const $addrLine = this.createElement('addr-line', affiliation.attrs.addressLine1);
1110
- $content.push($addrLine);
1111
- }
1112
- if (affiliation.attrs.addressLine2) {
1113
- const $addrLine = this.createElement('addr-line', affiliation.attrs.addressLine2);
1114
- $content.push($addrLine);
1115
- }
1116
- if (affiliation.attrs.addressLine3) {
1117
- const $addrLine = this.createElement('addr-line', affiliation.attrs.addressLine3);
1118
- $content.push($addrLine);
1119
- }
1120
- if (affiliation.attrs.city) {
1121
- const $city = this.createElement('city', affiliation.attrs.city);
1122
- $content.push($city);
1123
- }
1124
- if (affiliation.attrs.county) {
1125
- const $state = this.createElement('state', affiliation.attrs.county);
1126
- $content.push($state);
1127
- }
1128
- if (affiliation.attrs.country) {
1129
- const $country = this.createElement('country', affiliation.attrs.country);
1130
- $content.push($country);
1131
- }
1132
- if (affiliation.attrs.postCode) {
1133
- const $postalCode = this.createElement('postal-code', affiliation.attrs.postCode);
1134
- $content.push($postalCode);
1135
- }
1136
- const $aff = this.createElement('aff');
1137
- $aff.setAttribute('id', normalizeID(affiliation.attrs.id));
1138
- $contribGroup.appendChild($aff);
1139
- const label = labels.get(affiliation.attrs.id);
1140
- if (label) {
1141
- this.appendElement($aff, 'label', String(label));
1154
+ contributor.attrs.creditRoles?.forEach((credit) => {
1155
+ const url = credit_roles_1.CreditRoleUrls.get(credit.vocabTerm);
1156
+ if (!url) {
1157
+ return;
1142
1158
  }
1143
- $content.forEach((node, i) => {
1144
- if (i > 0) {
1145
- $aff.appendChild(this.document.createTextNode(', '));
1146
- }
1147
- $aff.appendChild(node);
1159
+ this.appendElement($contrib, 'role', credit.vocabTerm, {
1160
+ 'vocab-identifier': 'http://credit.niso.org/',
1161
+ vocab: 'CRediT',
1162
+ 'vocab-term': credit.vocabTerm,
1163
+ 'vocab-term-identifier': url,
1148
1164
  });
1149
1165
  });
1150
- const $authorNotes = this.createAuthorNotesElement(contributors);
1151
- if ($authorNotes) {
1152
- articleMeta.insertBefore($authorNotes, $contribGroup.nextSibling);
1166
+ return $contrib;
1167
+ };
1168
+ this.buildAffiliationElement = (affiliation) => {
1169
+ const $content = [];
1170
+ if (affiliation.attrs.department) {
1171
+ $content.push(this.createElement('institution', affiliation.attrs.department, {
1172
+ 'content-type': 'dept',
1173
+ }));
1174
+ }
1175
+ if (affiliation.attrs.institution) {
1176
+ $content.push(this.createElement('institution', affiliation.attrs.institution));
1177
+ }
1178
+ if (affiliation.attrs.addressLine1) {
1179
+ $content.push(this.createElement('addr-line', affiliation.attrs.addressLine1));
1180
+ }
1181
+ if (affiliation.attrs.addressLine2) {
1182
+ $content.push(this.createElement('addr-line', affiliation.attrs.addressLine2));
1183
+ }
1184
+ if (affiliation.attrs.addressLine3) {
1185
+ $content.push(this.createElement('addr-line', affiliation.attrs.addressLine3));
1186
+ }
1187
+ if (affiliation.attrs.city) {
1188
+ $content.push(this.createElement('city', affiliation.attrs.city));
1189
+ }
1190
+ if (affiliation.attrs.county) {
1191
+ $content.push(this.createElement('state', affiliation.attrs.county));
1192
+ }
1193
+ if (affiliation.attrs.country) {
1194
+ $content.push(this.createElement('country', affiliation.attrs.country));
1195
+ }
1196
+ if (affiliation.attrs.postCode) {
1197
+ $content.push(this.createElement('postal-code', affiliation.attrs.postCode));
1153
1198
  }
1199
+ const $aff = this.createElement('aff');
1200
+ $aff.setAttribute('id', normalizeID(affiliation.attrs.id));
1201
+ const label = this.contributorLabels.get(affiliation.attrs.id);
1202
+ if (label) {
1203
+ this.appendElement($aff, 'label', String(label));
1204
+ }
1205
+ $content.forEach((node, i) => {
1206
+ if (i > 0) {
1207
+ $aff.appendChild(this.document.createTextNode(', '));
1208
+ }
1209
+ $aff.appendChild(node);
1210
+ });
1211
+ return $aff;
1154
1212
  };
1155
1213
  this.buildContributorName = (contributor) => {
1156
1214
  const { given, family } = contributor.attrs;
1157
1215
  if (Boolean(given) !== Boolean(family)) {
1158
1216
  return this.createElement('string-name', given || family);
1159
1217
  }
1160
- const name = this.createElement('name');
1218
+ const $name = this.createElement('name');
1161
1219
  if (contributor.attrs.family) {
1162
- this.appendElement(name, 'surname', contributor.attrs.family);
1220
+ this.appendElement($name, 'surname', contributor.attrs.family);
1163
1221
  }
1164
1222
  if (contributor.attrs.given) {
1165
- this.appendElement(name, 'given-names', contributor.attrs.given);
1223
+ this.appendElement($name, 'given-names', contributor.attrs.given);
1166
1224
  }
1167
1225
  if (contributor.attrs.prefix) {
1168
- this.appendElement(name, 'prefix', contributor.attrs.prefix);
1169
- }
1170
- return name;
1171
- };
1172
- this.createAuthorNotesElement = (contributors) => {
1173
- const authorNotes = this.getFirstChildOfType(schema_1.schema.nodes.author_notes);
1174
- if (!authorNotes || !authorNotes.childCount) {
1175
- return;
1226
+ this.appendElement($name, 'prefix', contributor.attrs.prefix);
1176
1227
  }
1177
- const $authorNotes = this.createElement('author-notes');
1178
- const used = contributors.flatMap((c) => c.attrs.correspIDs);
1179
- authorNotes.descendants((node) => {
1180
- switch (node.type) {
1181
- case schema_1.schema.nodes.paragraph: {
1182
- const $p = this.writeParagraph(node);
1183
- $authorNotes.append($p);
1184
- break;
1185
- }
1186
- case schema_1.schema.nodes.footnote: {
1187
- const $fn = this.writeFootnote(node);
1188
- $authorNotes.append($fn);
1189
- break;
1190
- }
1191
- case schema_1.schema.nodes.corresp: {
1192
- if (used.includes(node.attrs.id)) {
1193
- const $corresp = this.writeCorresp(node);
1194
- $authorNotes.append($corresp);
1195
- }
1196
- break;
1197
- }
1198
- }
1199
- return false;
1200
- });
1201
- return $authorNotes.hasChildNodes() ? $authorNotes : undefined;
1228
+ return $name;
1202
1229
  };
1203
1230
  this.writeCorresp = (corresp) => {
1204
1231
  const $corresp = this.createElement('corresp');
@@ -1228,222 +1255,120 @@ class JATSExporter {
1228
1255
  $fn.innerHTML = content;
1229
1256
  return $fn;
1230
1257
  };
1231
- this.fixBody = (body) => {
1232
- this.manuscriptNode.descendants((node) => {
1233
- if (node.attrs.id) {
1234
- if ((0, transformer_1.isNodeType)(node, 'general_table_footnote')) {
1235
- const generalTableFootnote = body.querySelector(`#${normalizeID(node.attrs.id)}`);
1236
- if (generalTableFootnote) {
1237
- Array.from(generalTableFootnote.childNodes).forEach((cn) => {
1238
- generalTableFootnote.before(cn);
1239
- });
1240
- generalTableFootnote.remove();
1241
- }
1258
+ this.appendAuthorNotes = ($authorNotes, authorNotes) => {
1259
+ const correspIDs = new Set(this.getChildrenOfType(schema_1.schema.nodes.contributor).flatMap((contributor) => contributor.attrs.correspIDs));
1260
+ authorNotes.forEach((node) => {
1261
+ switch (node.type) {
1262
+ case schema_1.schema.nodes.paragraph: {
1263
+ $authorNotes.append(this.writeParagraph(node));
1264
+ break;
1242
1265
  }
1243
- if ((0, transformer_1.isNodeType)(node, 'table_element')) {
1244
- const tableElement = body.querySelector(`#${normalizeID(node.attrs.id)}`);
1245
- if (tableElement) {
1246
- for (const childNode of tableElement.childNodes) {
1247
- switch (childNode.nodeName) {
1248
- case 'caption': {
1249
- const label = tableElement.querySelector('label');
1250
- tableElement.insertBefore(childNode, label ? label.nextSibling : tableElement.firstChild);
1251
- break;
1252
- }
1253
- case 'table': {
1254
- this.fixTable(childNode);
1255
- break;
1256
- }
1257
- }
1258
- }
1266
+ case schema_1.schema.nodes.footnote: {
1267
+ $authorNotes.append(this.writeFootnote(node));
1268
+ break;
1269
+ }
1270
+ case schema_1.schema.nodes.corresp: {
1271
+ if (correspIDs.has(node.attrs.id)) {
1272
+ $authorNotes.append(this.writeCorresp(node));
1259
1273
  }
1274
+ break;
1260
1275
  }
1261
1276
  }
1262
1277
  });
1263
1278
  };
1264
- this.changeTag = (node, tag) => {
1265
- const clone = this.createElement(tag);
1266
- for (const attr of node.attributes) {
1267
- clone.setAttributeNS(null, attr.name, attr.value);
1279
+ this.buildKeywords = () => this.getChildrenOfType(schema_1.schema.nodes.keyword_group).map((group) => this.serializeNode(group));
1280
+ this.changeTag = ($node, tag) => {
1281
+ const $clone = this.createElement(tag);
1282
+ for (const attr of $node.attributes) {
1283
+ $clone.setAttributeNS(null, attr.name, attr.value);
1268
1284
  }
1269
- while (node.firstChild) {
1270
- clone.appendChild(node.firstChild);
1285
+ while ($node.firstChild) {
1286
+ $clone.appendChild($node.firstChild);
1271
1287
  }
1272
- node.replaceWith(clone);
1273
- return clone;
1288
+ $node.replaceWith($clone);
1289
+ return $clone;
1274
1290
  };
1275
- this.fixTable = (table) => {
1276
- let tbody;
1277
- Array.from(table.childNodes).forEach((child) => {
1278
- if (child instanceof Element && child.tagName.toLowerCase() === 'tbody') {
1279
- tbody = child;
1291
+ this.normalizeTable = ($table) => {
1292
+ let $tbody;
1293
+ Array.from($table.childNodes).forEach(($child) => {
1294
+ if ($child instanceof Element &&
1295
+ $child.tagName.toLowerCase() === 'tbody') {
1296
+ $tbody = $child;
1280
1297
  }
1281
1298
  });
1282
- if (!tbody) {
1299
+ if (!$tbody) {
1283
1300
  return;
1284
1301
  }
1285
- const tbodyRows = Array.from(tbody.childNodes);
1286
- const thead = this.createElement('thead');
1287
- tbodyRows.forEach((row, i) => {
1288
- const isRow = row instanceof Element && row.tagName.toLowerCase() === 'tr';
1302
+ const tbodyRows = Array.from($tbody.childNodes);
1303
+ const $thead = this.createElement('thead');
1304
+ tbodyRows.forEach(($row, i) => {
1305
+ const isRow = $row instanceof Element && $row.tagName.toLowerCase() === 'tr';
1289
1306
  if (isRow) {
1290
- const headerCell = row.querySelector('th[scope="col"], th[scope="colgroup"]');
1291
- if (i === 0 || headerCell) {
1292
- tbody?.removeChild(row);
1293
- const tableCells = row.querySelectorAll('td');
1294
- for (const td of tableCells) {
1295
- this.changeTag(td, 'th');
1307
+ const $headerCell = $row.querySelector('th[scope="col"], th[scope="colgroup"]');
1308
+ if (i === 0 || $headerCell) {
1309
+ $tbody?.removeChild($row);
1310
+ const $tableCells = $row.querySelectorAll('td');
1311
+ for (const $td of $tableCells) {
1312
+ this.changeTag($td, 'th');
1296
1313
  }
1297
- thead.appendChild(row);
1314
+ $thead.appendChild($row);
1298
1315
  }
1299
1316
  }
1300
1317
  });
1301
- if (thead.hasChildNodes()) {
1302
- table.insertBefore(thead, tbody);
1303
- }
1304
- };
1305
- this.unwrapBody = (body) => {
1306
- const container = body.querySelector(':scope > sec[sec-type="body"]');
1307
- if (!container) {
1308
- return;
1309
- }
1310
- const sections = container.querySelectorAll(':scope > sec');
1311
- sections.forEach((section) => {
1312
- body.appendChild(section.cloneNode(true));
1313
- });
1314
- body.removeChild(container);
1315
- };
1316
- this.removeBackContainer = (body) => {
1317
- const container = body.querySelector(':scope > sec[sec-type="backmatter"]');
1318
- if (!container) {
1319
- return;
1320
- }
1321
- body.removeChild(container);
1322
- };
1323
- this.moveAwards = (front, body) => {
1324
- const awardGroups = body.querySelectorAll(':scope > award-group');
1325
- if (!awardGroups.length) {
1326
- return;
1327
- }
1328
- const fundingGroup = this.createElement('funding-group');
1329
- awardGroups.forEach((award) => {
1330
- fundingGroup.appendChild(award);
1331
- });
1332
- const articleMeta = front.querySelector(':scope > article-meta');
1333
- if (articleMeta) {
1334
- const insertBeforeElement = articleMeta.querySelector(':scope > support-group, :scope > conference, :scope > counts, :scope > custom-meta-group');
1335
- insertBeforeElement
1336
- ? articleMeta.insertBefore(fundingGroup, insertBeforeElement)
1337
- : articleMeta.appendChild(fundingGroup);
1318
+ if ($thead.hasChildNodes()) {
1319
+ $table.insertBefore($thead, $tbody);
1338
1320
  }
1339
1321
  };
1340
- this.moveAbstracts = (front, body) => {
1341
- const abstractSections = this.getAbstractSections(body);
1342
- for (const abstractSection of abstractSections) {
1343
- const node = abstractSection.nodeName === 'trans-abstract'
1344
- ? this.createTransAbstractNode(abstractSection)
1345
- : this.createAbstractNode(abstractSection);
1346
- abstractSection.remove();
1347
- insertAbstractNode(front, node);
1348
- }
1349
- };
1350
- this.moveSectionsToBack = (back, body) => {
1351
- const availabilitySection = body.querySelector('sec[sec-type="availability"]');
1352
- if (availabilitySection) {
1353
- back.insertBefore(availabilitySection, back.firstChild);
1354
- }
1355
- const ethicsSection = body.querySelector('sec[sec-type="ethics-statement"]');
1356
- if (ethicsSection) {
1357
- back.appendChild(ethicsSection);
1358
- }
1359
- const section = body.querySelector('sec[sec-type="acknowledgements"]');
1360
- if (section) {
1361
- const ack = this.createElement('ack');
1362
- while (section.firstChild) {
1363
- ack.appendChild(section.firstChild);
1364
- }
1365
- if (section.parentNode) {
1366
- section.parentNode.removeChild(section);
1367
- }
1368
- back.insertBefore(ack, back.firstChild);
1369
- }
1370
- const appendicesSections = body.querySelectorAll('sec[sec-type="appendices"]');
1371
- if (appendicesSections) {
1372
- const appGroup = this.createElement('app-group');
1373
- appendicesSections.forEach((section) => {
1374
- if (section.parentNode) {
1375
- section.parentNode.removeChild(section);
1376
- }
1377
- const app = this.createElement('app');
1378
- app.appendChild(section);
1379
- appGroup.appendChild(app);
1380
- });
1381
- back.insertBefore(appGroup, back.firstChild);
1382
- }
1383
- const footNotes = [];
1384
- const footnoteCategories = [
1385
- 'con',
1386
- 'conflict',
1387
- 'deceased',
1388
- 'equal',
1389
- 'present-address',
1390
- 'presented-at',
1391
- 'previously-at',
1392
- 'supplementary-material',
1393
- 'supported-by',
1394
- 'financial-disclosure',
1395
- 'coi-statement',
1396
- ];
1397
- const sections = body.querySelectorAll('sec');
1398
- for (const currentSection of sections) {
1399
- const currentSectionType = currentSection.getAttribute('sec-type');
1400
- if (currentSectionType &&
1401
- footnoteCategories.indexOf(currentSectionType) >= 0) {
1402
- footNotes.push(this.sectionToFootnote(currentSection, currentSectionType));
1322
+ this.buildAbstracts = () => {
1323
+ const $abstracts = [];
1324
+ const abstractsNode = this.getFirstChildOfType(schema_1.schema.nodes.abstracts);
1325
+ abstractsNode?.forEach((child) => {
1326
+ const $abstract = this.serializeNode(child);
1327
+ if ($abstract.nodeName === 'abstract') {
1328
+ $abstract
1329
+ .querySelectorAll(':scope > title')
1330
+ .forEach((title) => title.remove());
1403
1331
  }
1404
- }
1405
- if (footNotes.length > 0) {
1406
- const fnGroup = this.createElement('fn-group');
1407
- fnGroup.append(...footNotes);
1408
- back.append(fnGroup);
1409
- }
1332
+ $abstracts.push($abstract);
1333
+ });
1334
+ return $abstracts;
1410
1335
  };
1411
- this.sectionToFootnote = (section, fnType) => {
1412
- const footNote = this.createElement('fn');
1413
- footNote.setAttribute('fn-type', fnType);
1414
- const title = section.querySelector('title');
1415
- if (title) {
1416
- const footNoteTitle = this.createElement('label');
1417
- footNoteTitle.textContent = title.textContent;
1418
- section.removeChild(title);
1419
- footNote.append(footNoteTitle);
1336
+ this.sectionToFootnote = ($section, fnType) => {
1337
+ const $fn = this.createElement('fn');
1338
+ $fn.setAttribute('fn-type', fnType);
1339
+ const $title = $section.querySelector(':scope > title');
1340
+ if ($title) {
1341
+ const $label = this.createElement('label');
1342
+ $label.textContent = $title.textContent;
1343
+ $section.removeChild($title);
1344
+ $fn.append($label);
1420
1345
  }
1421
- footNote.append(...section.children);
1422
- if (section.parentNode) {
1423
- section.parentNode.removeChild(section);
1346
+ $fn.append(...$section.children);
1347
+ if ($section.parentNode) {
1348
+ $section.parentNode.removeChild($section);
1424
1349
  }
1425
- return footNote;
1350
+ return $fn;
1426
1351
  };
1427
- this.moveFloatsGroup = (article) => {
1352
+ this.buildFloatsGroup = () => {
1428
1353
  const heroImage = this.getFirstChildOfType(schema_1.schema.nodes.hero_image);
1429
1354
  if (!heroImage) {
1430
1355
  return;
1431
1356
  }
1432
- const floatsGroup = this.createElement('floats-group');
1433
- let figure = null;
1357
+ const $floatsGroup = this.createElement('floats-group');
1358
+ let $graphic = null;
1434
1359
  heroImage.descendants((node) => {
1435
1360
  if (node.type === schema_1.schema.nodes.figure) {
1436
- figure = this.serializeNode(node);
1437
- floatsGroup.appendChild(figure);
1361
+ $graphic = this.serializeNode(node);
1362
+ $floatsGroup.appendChild($graphic);
1438
1363
  }
1439
1364
  else {
1440
- const serializedNode = this.serializeNode(node);
1441
- figure?.appendChild(serializedNode);
1365
+ const $serializedNode = this.serializeNode(node);
1366
+ $graphic?.appendChild($serializedNode);
1442
1367
  }
1443
1368
  return false;
1444
1369
  });
1445
- if (floatsGroup.children.length > 0) {
1446
- article.appendChild(floatsGroup);
1370
+ if ($floatsGroup.children.length > 0) {
1371
+ return $floatsGroup;
1447
1372
  }
1448
1373
  };
1449
1374
  }
@@ -1458,168 +1383,58 @@ class JATSExporter {
1458
1383
  }
1459
1384
  createEquation(node, isInline = false) {
1460
1385
  if (node.attrs.format === 'tex') {
1461
- const texMath = this.createElement('tex-math');
1462
- texMath.setAttribute('notation', 'LaTeX');
1463
- texMath.setAttribute('version', 'MathJax');
1386
+ const $texMath = this.createElement('tex-math');
1387
+ $texMath.setAttribute('notation', 'LaTeX');
1388
+ $texMath.setAttribute('version', 'MathJax');
1464
1389
  if (node.attrs.contents.includes('<![CDATA[')) {
1465
- texMath.innerHTML = node.attrs.contents;
1390
+ $texMath.innerHTML = node.attrs.contents;
1466
1391
  }
1467
1392
  else {
1468
- texMath.innerHTML = `<![CDATA[ ${node.attrs.contents} ]]>`;
1393
+ $texMath.innerHTML = `<![CDATA[ ${node.attrs.contents} ]]>`;
1469
1394
  }
1470
- return texMath;
1395
+ return $texMath;
1471
1396
  }
1472
1397
  else {
1473
1398
  const math = this.nodeFromJATS(node.attrs.contents);
1474
- const mathml = math;
1399
+ const $mathml = math;
1475
1400
  if (!isInline) {
1476
- mathml.setAttribute('id', normalizeID(node.attrs.id));
1477
- }
1478
- return mathml;
1479
- }
1480
- }
1481
- buildKeywords(articleMeta) {
1482
- const keywordGroups = this.getChildrenOfType(schema_1.schema.nodes.keyword_group);
1483
- keywordGroups.forEach((group) => {
1484
- const kwdGroup = this.createElement('kwd-group');
1485
- if (group.attrs.type) {
1486
- kwdGroup.setAttribute('kwd-group-type', group.attrs.type);
1487
- }
1488
- articleMeta.appendChild(kwdGroup);
1489
- group.content.forEach((keyword) => {
1490
- const kwd = this.createElement('kwd');
1491
- kwd.textContent = keyword.textContent;
1492
- kwdGroup.appendChild(kwd);
1493
- });
1494
- articleMeta.appendChild(kwdGroup);
1495
- });
1496
- }
1497
- getAbstractCategories(abstractsNode) {
1498
- const categories = [];
1499
- abstractsNode?.content.descendants((node) => {
1500
- categories.push(node.attrs.category);
1501
- return false;
1502
- });
1503
- return categories;
1504
- }
1505
- getAbstractSections(body) {
1506
- {
1507
- const abstractsNode = this.getFirstChildOfType(schema_1.schema.nodes.abstracts);
1508
- const abstractCategories = this.getAbstractCategories(abstractsNode);
1509
- const sections = Array.from(body.querySelectorAll(':scope > sec, :scope > trans-abstract'));
1510
- return sections.filter((section) => this.isAbstractSection(section, abstractCategories));
1511
- }
1512
- }
1513
- isAbstractSection(section, abstractCategories) {
1514
- const sectionType = section.getAttribute('sec-type');
1515
- return sectionType ? abstractCategories.includes(sectionType) : false;
1516
- }
1517
- createTransAbstractNode(transAbstract) {
1518
- const transAbstractNode = this.createElement('trans-abstract');
1519
- transAbstractNode.setAttributeNS(xml_1.XML_NAMESPACE, 'lang', transAbstract.getAttributeNS(xml_1.XML_NAMESPACE, 'lang') ?? '');
1520
- this.setAbstractType(transAbstractNode, transAbstract);
1521
- transAbstractNode.append(...transAbstract.childNodes);
1522
- return transAbstractNode;
1523
- }
1524
- createAbstractNode(abstractSection) {
1525
- const abstractNode = this.createElement('abstract');
1526
- for (const node of abstractSection.childNodes) {
1527
- if (node.nodeName !== 'title') {
1528
- abstractNode.appendChild(node.cloneNode(true));
1401
+ $mathml.setAttribute('id', normalizeID(node.attrs.id));
1529
1402
  }
1403
+ return $mathml;
1530
1404
  }
1531
- this.setAbstractType(abstractNode, abstractSection);
1532
- return abstractNode;
1533
1405
  }
1534
- setAbstractType(abstractNode, abstractSection) {
1535
- const sectionType = abstractSection.getAttribute('sec-type');
1536
- if (sectionType && sectionType !== 'abstract') {
1537
- const abstractType = sectionType.replace('abstract-', '');
1538
- abstractNode.setAttribute('abstract-type', abstractType);
1539
- }
1406
+ fillEmptyElements($article, selector, tagName = 'p') {
1407
+ const $empty = Array.from($article.querySelectorAll(selector)).filter(($el) => !$el.innerHTML);
1408
+ $empty.forEach(($element) => $element.appendChild(this.createElement(tagName)));
1540
1409
  }
1541
- moveCoiStatementToAuthorNotes(back, front) {
1542
- const fnGroups = back.querySelectorAll('fn-group');
1543
- fnGroups.forEach((fnGroup) => {
1544
- if (fnGroup) {
1545
- const coiStatement = fnGroup.querySelector('fn[fn-type="coi-statement"]');
1546
- if (coiStatement) {
1547
- const authorNotes = this.createElement('author-notes');
1548
- authorNotes.append(coiStatement);
1549
- const articleMeta = front.querySelector('article-meta');
1550
- if (articleMeta) {
1551
- const authorNoteEl = articleMeta.querySelector('author-notes');
1552
- if (authorNoteEl) {
1553
- authorNoteEl.append(...authorNotes.childNodes);
1554
- }
1555
- else {
1556
- const appendableSelectors = [
1557
- 'contrib-group',
1558
- 'title-group',
1559
- 'article-id',
1560
- ];
1561
- const appendable = [
1562
- ...articleMeta.querySelectorAll(appendableSelectors.join(', ')),
1563
- ];
1564
- for (let i = 0; i < appendableSelectors.length; i++) {
1565
- const sel = appendableSelectors[i];
1566
- const match = appendable.find((el) => el.matches(sel));
1567
- if (match) {
1568
- articleMeta.insertBefore(authorNotes, match.nextSibling);
1569
- break;
1570
- }
1571
- }
1572
- }
1573
- }
1574
- if (!fnGroup.hasChildNodes()) {
1575
- fnGroup.remove();
1576
- }
1577
- }
1578
- }
1579
- });
1580
- }
1581
- updateFootnoteTypes(front, body) {
1582
- const footnotes = [...front.querySelectorAll('fn').values()];
1583
- footnotes.push(...body.querySelectorAll('fn'));
1584
- footnotes.forEach((fn) => {
1585
- const fnType = fn.getAttribute('fn-type');
1586
- if (fnType) {
1587
- fn.setAttribute('fn-type', fnType);
1588
- }
1589
- });
1590
- }
1591
- fillEmptyElements(articleElement, selector, tagName = 'p') {
1592
- const emptyElements = Array.from(articleElement.querySelectorAll(selector)).filter((element) => !element.innerHTML);
1593
- emptyElements.forEach((element) => element.appendChild(this.createElement(tagName)));
1594
- }
1595
- addParagraphsToSections(articleElement) {
1596
- const sections = articleElement.querySelectorAll('sec');
1410
+ addParagraphsToSections($article) {
1411
+ const $sections = $article.querySelectorAll('sec, abstract');
1597
1412
  const TITLE_TAGS = new Set(['title', 'label', 'sec-meta']);
1598
- for (const section of sections) {
1599
- const hasContent = Array.from(section.children).some((child) => !TITLE_TAGS.has(child.tagName));
1413
+ for (const $section of $sections) {
1414
+ const hasContent = Array.from($section.children).some(($child) => !TITLE_TAGS.has($child.tagName));
1600
1415
  if (hasContent) {
1601
1416
  continue;
1602
1417
  }
1603
- const p = this.createElement('p');
1604
- const insertAfterElement = section.querySelector(':scope > title') ??
1605
- section.querySelector(':scope > label') ??
1606
- section.querySelector(':scope > sec-meta');
1607
- if (insertAfterElement) {
1608
- insertAfterElement.insertAdjacentElement('afterend', p);
1418
+ const $p = this.createElement('p');
1419
+ const $insertAfter = $section.querySelector(':scope > title') ??
1420
+ $section.querySelector(':scope > label') ??
1421
+ $section.querySelector(':scope > sec-meta');
1422
+ if ($insertAfter) {
1423
+ $insertAfter.insertAdjacentElement('afterend', $p);
1609
1424
  }
1610
1425
  else {
1611
- section.prepend(p);
1426
+ $section.prepend($p);
1612
1427
  }
1613
1428
  }
1614
1429
  }
1615
- fillEmptyFootnotes(articleElement) {
1616
- this.fillEmptyElements(articleElement, 'fn');
1430
+ fillEmptyFootnotes($article) {
1431
+ this.fillEmptyElements($article, 'fn');
1617
1432
  }
1618
- fillEmptyTableFooters(articleElement) {
1619
- this.fillEmptyElements(articleElement, 'table-wrap-foot');
1433
+ fillEmptyTableFooters($article) {
1434
+ this.fillEmptyElements($article, 'table-wrap-foot');
1620
1435
  }
1621
- fillEmptyListItem(articleElement) {
1622
- this.fillEmptyElements(articleElement, 'list-item');
1436
+ fillEmptyListItem($article) {
1437
+ this.fillEmptyElements($article, 'list-item');
1623
1438
  }
1624
1439
  }
1625
1440
  exports.JATSExporter = JATSExporter;