@manuscripts/transform 4.5.7 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/jats/__tests__/jats-exporter.test.js +50 -20
- package/dist/cjs/jats/__tests__/jats-importer.test.js +26 -0
- package/dist/cjs/jats/__tests__/jats-roundtrip.test.js +6 -1
- package/dist/cjs/jats/exporter/JATSExporterBase.js +118 -0
- package/dist/cjs/jats/exporter/jats-exporter.js +227 -349
- package/dist/cjs/jats/exporter/lib.js +62 -0
- package/dist/cjs/jats/importer/jats-dom-parser.js +11 -3
- package/dist/cjs/schema/index.js +4 -1
- package/dist/cjs/schema/migration/migration-scripts/4.5.9.js +30 -0
- package/dist/cjs/schema/migration/migration-scripts/index.js +3 -1
- package/dist/cjs/schema/nodes/bibliography_element.js +0 -1
- package/dist/cjs/schema/nodes/bio.js +24 -0
- package/dist/cjs/schema/nodes/contributor.js +1 -2
- package/dist/cjs/schema/nodes/keywords_element.js +0 -1
- package/dist/cjs/types.js +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/es/jats/__tests__/jats-exporter.test.js +47 -20
- package/dist/es/jats/__tests__/jats-importer.test.js +26 -0
- package/dist/es/jats/__tests__/jats-roundtrip.test.js +3 -1
- package/dist/es/jats/exporter/JATSExporterBase.js +114 -0
- package/dist/es/jats/exporter/jats-exporter.js +201 -319
- package/dist/es/jats/exporter/lib.js +55 -0
- package/dist/es/jats/importer/jats-dom-parser.js +11 -3
- package/dist/es/schema/index.js +4 -1
- package/dist/es/schema/migration/migration-scripts/4.5.9.js +28 -0
- package/dist/es/schema/migration/migration-scripts/index.js +3 -1
- package/dist/es/schema/nodes/bibliography_element.js +0 -1
- package/dist/es/schema/nodes/bio.js +20 -0
- package/dist/es/schema/nodes/contributor.js +1 -2
- package/dist/es/schema/nodes/keywords_element.js +0 -1
- package/dist/es/types.js +1 -1
- package/dist/es/version.js +1 -1
- package/dist/types/jats/exporter/JATSExporterBase.d.ts +35 -0
- package/dist/types/jats/exporter/jats-exporter.d.ts +23 -19
- package/dist/types/jats/exporter/lib.d.ts +23 -0
- package/dist/types/schema/index.d.ts +2 -1
- package/dist/types/schema/migration/migration-scripts/4.5.9.d.ts +8 -0
- package/dist/types/schema/migration/migration-scripts/index.d.ts +3 -2
- package/dist/types/schema/nodes/bio.d.ts +10 -0
- package/dist/types/schema/types.d.ts +1 -1
- package/dist/types/types.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +4 -4
- package/src/jats/__tests__/__fixtures__/jats-import.xml +7 -0
- package/src/jats/__tests__/__snapshots__/jats-importer.test.ts.snap +272 -151
- package/src/jats/__tests__/__snapshots__/jats-roundtrip.test.ts.snap +2 -2
- package/src/jats/__tests__/jats-exporter.test.ts +60 -20
- package/src/jats/__tests__/jats-importer.test.ts +37 -0
- package/src/jats/__tests__/jats-roundtrip.test.ts +3 -1
- package/src/jats/exporter/JATSExporterBase.ts +164 -0
- package/src/jats/exporter/jats-exporter.ts +217 -386
- package/src/jats/exporter/lib.ts +69 -0
- package/src/jats/importer/jats-dom-parser.ts +15 -5
- package/src/schema/index.ts +4 -1
- package/src/schema/migration/migration-scripts/4.5.9.ts +62 -0
- package/src/schema/migration/migration-scripts/index.ts +3 -1
- package/src/schema/nodes/bibliography_element.ts +0 -1
- package/src/schema/nodes/bio.ts +48 -0
- package/src/schema/nodes/contributor.ts +1 -2
- package/src/schema/nodes/keywords_element.ts +0 -1
- package/src/schema/types.ts +1 -0
- package/src/types.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* ©
|
|
2
|
+
* © 2026 Atypon Systems LLC
|
|
3
3
|
*
|
|
4
4
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
* you may not use this file except in compliance with the License.
|
|
@@ -15,70 +15,24 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import * as Citeproc from 'citeproc';
|
|
17
17
|
import { DOMSerializer } from 'prosemirror-model';
|
|
18
|
-
import { findChildrenByAttr
|
|
19
|
-
import serializeToXML from 'w3c-xmlserializer';
|
|
18
|
+
import { findChildrenByAttr } from 'prosemirror-utils';
|
|
20
19
|
import { buildCiteprocCitation } from '../../lib/citeproc';
|
|
21
20
|
import { CreditRoleUrls } from '../../lib/credit-roles';
|
|
22
21
|
import { generateFootnoteLabels } from '../../lib/footnotes';
|
|
23
22
|
import { FOOTNOTE_SECTION_CATEGORY_IDS } from '../../lib/section-categories';
|
|
24
23
|
import { sanitizeXmlString, XLINK_NAMESPACE, XML_NAMESPACE, } from '../../lib/xml';
|
|
25
|
-
import { isBibliographyItemNode, isCitationNode,
|
|
24
|
+
import { isBibliographyItemNode, isCitationNode, schema, } from '../../schema';
|
|
26
25
|
import { isExecutableNodeType } from '../../transformer';
|
|
27
26
|
import { initJats, jatsVariableWrapper } from './citeproc';
|
|
28
27
|
import { selectVersionIDs } from './jats-versions';
|
|
29
28
|
import { buildTargets } from './labels';
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
increment: (field) => {
|
|
35
|
-
const value = counts.get(field);
|
|
36
|
-
const newValue = value === undefined ? 1 : value + 1;
|
|
37
|
-
counts.set(field, newValue);
|
|
38
|
-
return newValue;
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
const createDefaultIDGenerator = () => {
|
|
43
|
-
const counter = createCounter();
|
|
44
|
-
return async (element) => {
|
|
45
|
-
const value = String(counter.increment(element.nodeName));
|
|
46
|
-
return `${element.localName}-${value}`;
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
const chooseRefType = (type) => {
|
|
50
|
-
switch (type) {
|
|
51
|
-
case schema.nodes.figure:
|
|
52
|
-
case schema.nodes.figure_element:
|
|
53
|
-
return 'fig';
|
|
54
|
-
case schema.nodes.footnote:
|
|
55
|
-
return 'fn';
|
|
56
|
-
case schema.nodes.table:
|
|
57
|
-
case schema.nodes.table_element:
|
|
58
|
-
return 'table';
|
|
59
|
-
case schema.nodes.section:
|
|
60
|
-
case schema.nodes.abstract:
|
|
61
|
-
return 'sec';
|
|
62
|
-
case schema.nodes.equation:
|
|
63
|
-
case schema.nodes.equation_element:
|
|
64
|
-
return 'disp-formula';
|
|
65
|
-
case schema.nodes.supplement:
|
|
66
|
-
return 'supplementary-material';
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
export class JATSExporter {
|
|
29
|
+
import { JATSExporterBase } from './JATSExporterBase';
|
|
30
|
+
import { chooseRefType, createDefaultIDGenerator, normalizeID } from './lib';
|
|
31
|
+
export class JATSExporter extends JATSExporterBase {
|
|
70
32
|
constructor() {
|
|
71
|
-
|
|
33
|
+
super(...arguments);
|
|
72
34
|
this.contributorLabels = new Map();
|
|
73
|
-
this.
|
|
74
|
-
this.manuscriptNode.descendants((node) => {
|
|
75
|
-
const type = node.type;
|
|
76
|
-
const nodes = this.nodesMap.get(type) ?? [];
|
|
77
|
-
nodes.push(node);
|
|
78
|
-
this.nodesMap.set(type, nodes);
|
|
79
|
-
});
|
|
80
|
-
};
|
|
81
|
-
this.serializeToJATS = async (manuscriptNode, options) => {
|
|
35
|
+
this.export = async (manuscriptNode, options) => {
|
|
82
36
|
this.manuscriptNode = manuscriptNode;
|
|
83
37
|
this.populateNodesMap();
|
|
84
38
|
this.initCiteprocEngine(options);
|
|
@@ -109,7 +63,7 @@ export class JATSExporter {
|
|
|
109
63
|
$article.appendChild($floatsGroup);
|
|
110
64
|
}
|
|
111
65
|
await this.rewriteIDs();
|
|
112
|
-
return
|
|
66
|
+
return this.document;
|
|
113
67
|
};
|
|
114
68
|
this.appendChildNodeOfType = ($element, node, type) => {
|
|
115
69
|
const childNode = this.getFirstChildOfType(type, node);
|
|
@@ -400,7 +354,7 @@ export class JATSExporter {
|
|
|
400
354
|
const [, bibliography] = this.engine.makeBibliography();
|
|
401
355
|
const parser = new DOMParser();
|
|
402
356
|
bibliography.forEach((item) => {
|
|
403
|
-
const fragment = `<
|
|
357
|
+
const fragment = `<wrapper xmlns:xlink="${XLINK_NAMESPACE}">${sanitizeXmlString(item)}</wrapper>`;
|
|
404
358
|
const $ref = parser
|
|
405
359
|
.parseFromString(fragment, 'text/xml')
|
|
406
360
|
.querySelector('ref');
|
|
@@ -410,32 +364,13 @@ export class JATSExporter {
|
|
|
410
364
|
});
|
|
411
365
|
return $refList;
|
|
412
366
|
};
|
|
413
|
-
this.createElement = (tag, content, attrs) => {
|
|
414
|
-
const $element = this.document.createElement(tag);
|
|
415
|
-
if (content) {
|
|
416
|
-
$element.textContent = content;
|
|
417
|
-
}
|
|
418
|
-
if (attrs) {
|
|
419
|
-
Object.entries(attrs).forEach(([k, v]) => {
|
|
420
|
-
if (v) {
|
|
421
|
-
$element.setAttribute(k, v);
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
return $element;
|
|
426
|
-
};
|
|
427
|
-
this.appendElement = ($parent, tag, content, attrs) => {
|
|
428
|
-
const $element = this.createElement(tag, content, attrs);
|
|
429
|
-
$parent.appendChild($element);
|
|
430
|
-
return $element;
|
|
431
|
-
};
|
|
432
367
|
this.createSerializer = () => {
|
|
433
368
|
const nodes = {
|
|
434
|
-
trans_abstract: (node) => createTransAbstract(node),
|
|
435
|
-
trans_graphical_abstract: (node) => createTransAbstract(node),
|
|
369
|
+
trans_abstract: (node) => this.createTransAbstract(node),
|
|
370
|
+
trans_graphical_abstract: (node) => this.createTransAbstract(node),
|
|
436
371
|
hero_image: () => '',
|
|
437
372
|
headshot_grid: () => ['p', { 'content-type': 'headshots' }, 0],
|
|
438
|
-
headshot_element: (node) => createImage(node),
|
|
373
|
+
headshot_element: (node) => this.createImage(node),
|
|
439
374
|
headshot_image: () => '',
|
|
440
375
|
alt_text: (node) => {
|
|
441
376
|
if (node.textContent) {
|
|
@@ -460,7 +395,7 @@ export class JATSExporter {
|
|
|
460
395
|
return $selfUri;
|
|
461
396
|
},
|
|
462
397
|
attachments: () => '',
|
|
463
|
-
image_element: (node) => createImage(node),
|
|
398
|
+
image_element: (node) => this.createImage(node),
|
|
464
399
|
embed: (node) => {
|
|
465
400
|
const { id, href, mimetype, mimeSubtype, extLinks } = node.attrs;
|
|
466
401
|
if (!href) {
|
|
@@ -476,7 +411,7 @@ export class JATSExporter {
|
|
|
476
411
|
if (mimeSubtype) {
|
|
477
412
|
$media.setAttribute('mime-subtype', node.attrs.mimeSubtype);
|
|
478
413
|
}
|
|
479
|
-
appendLabels($media, node);
|
|
414
|
+
this.appendLabels($media, node);
|
|
480
415
|
this.appendChildNodeOfType($media, node, schema.nodes.alt_text);
|
|
481
416
|
this.appendChildNodeOfType($media, node, schema.nodes.long_desc);
|
|
482
417
|
this.appendCaption($media, node);
|
|
@@ -503,14 +438,14 @@ export class JATSExporter {
|
|
|
503
438
|
const awardGroup = node;
|
|
504
439
|
const $awardGroup = this.createElement('award-group');
|
|
505
440
|
$awardGroup.setAttribute('id', normalizeID(awardGroup.attrs.id));
|
|
506
|
-
appendChildIfPresent($awardGroup, 'funding-source', awardGroup.attrs.source);
|
|
441
|
+
this.appendChildIfPresent($awardGroup, 'funding-source', awardGroup.attrs.source);
|
|
507
442
|
awardGroup.attrs.code
|
|
508
443
|
?.split(';')
|
|
509
|
-
.forEach((code) => appendChildIfPresent($awardGroup, 'award-id', code));
|
|
510
|
-
appendChildIfPresent($awardGroup, 'principal-award-recipient', awardGroup.attrs.recipient);
|
|
444
|
+
.forEach((code) => this.appendChildIfPresent($awardGroup, 'award-id', code));
|
|
445
|
+
this.appendChildIfPresent($awardGroup, 'principal-award-recipient', awardGroup.attrs.recipient);
|
|
511
446
|
return $awardGroup;
|
|
512
447
|
},
|
|
513
|
-
box_element: (node) => createBoxElement(node),
|
|
448
|
+
box_element: (node) => this.createBoxElement(node),
|
|
514
449
|
author_notes: () => '',
|
|
515
450
|
corresp: () => '',
|
|
516
451
|
title: () => ['article-title', 0],
|
|
@@ -529,6 +464,7 @@ export class JATSExporter {
|
|
|
529
464
|
? ['table-wrap-foot', ['fn-group', ['fn', ['p']]]]
|
|
530
465
|
: ['table-wrap-foot', 0],
|
|
531
466
|
contributor: (node) => this.buildContributorElement(node),
|
|
467
|
+
bio: () => ['bio', 0],
|
|
532
468
|
affiliation: (node) => this.buildAffiliationElement(node),
|
|
533
469
|
attribution: () => ['attrib', 0],
|
|
534
470
|
bibliography_element: () => '',
|
|
@@ -550,7 +486,7 @@ export class JATSExporter {
|
|
|
550
486
|
$supplementaryMaterial.setAttributeNS(XLINK_NAMESPACE, 'href', node.attrs.href ?? '');
|
|
551
487
|
$supplementaryMaterial.setAttribute('mimetype', node.attrs.mimeType ?? '');
|
|
552
488
|
$supplementaryMaterial.setAttribute('mime-subtype', node.attrs.mimeSubType ?? '');
|
|
553
|
-
appendLabels($supplementaryMaterial, node);
|
|
489
|
+
this.appendLabels($supplementaryMaterial, node);
|
|
554
490
|
this.appendCaption($supplementaryMaterial, node);
|
|
555
491
|
return $supplementaryMaterial;
|
|
556
492
|
},
|
|
@@ -636,12 +572,12 @@ export class JATSExporter {
|
|
|
636
572
|
equation_element: (node) => {
|
|
637
573
|
const $dispFormula = this.createElement('disp-formula');
|
|
638
574
|
$dispFormula.setAttribute('id', normalizeID(node.attrs.id));
|
|
639
|
-
appendLabels($dispFormula, node);
|
|
640
|
-
processChildNodes($dispFormula, node, schema.nodes.equation);
|
|
575
|
+
this.appendLabels($dispFormula, node);
|
|
576
|
+
this.processChildNodes($dispFormula, node, schema.nodes.equation);
|
|
641
577
|
return $dispFormula;
|
|
642
578
|
},
|
|
643
|
-
figure: (node) => createGraphic(node),
|
|
644
|
-
figure_element: (node) => createFigureElement(node, node.type.schema.nodes.figure),
|
|
579
|
+
figure: (node) => this.createGraphic(node),
|
|
580
|
+
figure_element: (node) => this.createFigureElement(node, node.type.schema.nodes.figure),
|
|
645
581
|
footnote: (node) => {
|
|
646
582
|
const attrs = {};
|
|
647
583
|
if (node.attrs.id) {
|
|
@@ -716,7 +652,7 @@ export class JATSExporter {
|
|
|
716
652
|
$code.textContent = node.attrs.contents;
|
|
717
653
|
return $code;
|
|
718
654
|
},
|
|
719
|
-
listing_element: (node) => createFigureElement(node, node.type.schema.nodes.listing),
|
|
655
|
+
listing_element: (node) => this.createFigureElement(node, node.type.schema.nodes.listing),
|
|
720
656
|
manuscript: (node) => ['article', { id: normalizeID(node.attrs.id) }, 0],
|
|
721
657
|
missing_figure: () => {
|
|
722
658
|
const $graphic = this.createElement('graphic');
|
|
@@ -753,11 +689,11 @@ export class JATSExporter {
|
|
|
753
689
|
quote_image: (node) => {
|
|
754
690
|
const img = node;
|
|
755
691
|
if (img.attrs.src) {
|
|
756
|
-
return createGraphic(node);
|
|
692
|
+
return this.createGraphic(node);
|
|
757
693
|
}
|
|
758
694
|
return '';
|
|
759
695
|
},
|
|
760
|
-
graphical_abstract_section: (node) => createAbstract(node),
|
|
696
|
+
graphical_abstract_section: (node) => this.createAbstract(node),
|
|
761
697
|
section: (node) => {
|
|
762
698
|
const attrs = {
|
|
763
699
|
id: normalizeID(node.attrs.id),
|
|
@@ -767,13 +703,13 @@ export class JATSExporter {
|
|
|
767
703
|
}
|
|
768
704
|
return ['sec', attrs, 0];
|
|
769
705
|
},
|
|
770
|
-
abstract: (node) => createAbstract(node),
|
|
706
|
+
abstract: (node) => this.createAbstract(node),
|
|
771
707
|
section_label: () => ['label', 0],
|
|
772
708
|
section_title: () => ['title', 0],
|
|
773
709
|
section_title_plain: () => ['title', 0],
|
|
774
710
|
table: (node) => ['table', { id: normalizeID(node.attrs.id) }, 0],
|
|
775
711
|
table_element: (node) => {
|
|
776
|
-
const $tableWrap = createTableElement(node);
|
|
712
|
+
const $tableWrap = this.createTableElement(node);
|
|
777
713
|
$tableWrap.setAttribute('position', 'anchor');
|
|
778
714
|
if (node.attrs.type) {
|
|
779
715
|
$tableWrap.setAttribute('content-type', node.attrs.type);
|
|
@@ -824,210 +760,7 @@ export class JATSExporter {
|
|
|
824
760
|
tracked_delete: () => ['del'],
|
|
825
761
|
};
|
|
826
762
|
this.serializer = new DOMSerializer(nodes, marks);
|
|
827
|
-
const appendChildIfPresent = ($parent, tagName, textContent) => {
|
|
828
|
-
if (!textContent) {
|
|
829
|
-
return;
|
|
830
|
-
}
|
|
831
|
-
const $element = this.createElement(tagName);
|
|
832
|
-
$element.textContent = textContent;
|
|
833
|
-
$parent.appendChild($element);
|
|
834
|
-
};
|
|
835
|
-
const processChildNodes = ($element, node, contentNodeType) => {
|
|
836
|
-
node.forEach((childNode) => {
|
|
837
|
-
if (childNode.type === contentNodeType) {
|
|
838
|
-
if (childNode.attrs.id) {
|
|
839
|
-
$element.appendChild(this.serializeNode(childNode));
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
else if (childNode.type === node.type.schema.nodes.paragraph) {
|
|
843
|
-
$element.appendChild(this.serializeNode(childNode));
|
|
844
|
-
}
|
|
845
|
-
else if (childNode.type === node.type.schema.nodes.missing_figure) {
|
|
846
|
-
$element.appendChild(this.serializeNode(childNode));
|
|
847
|
-
}
|
|
848
|
-
});
|
|
849
|
-
};
|
|
850
|
-
const createElement = (node, nodeName) => {
|
|
851
|
-
const $element = this.createElement(nodeName);
|
|
852
|
-
$element.setAttribute('id', normalizeID(node.attrs.id));
|
|
853
|
-
return $element;
|
|
854
|
-
};
|
|
855
|
-
const appendLabels = ($element, node) => {
|
|
856
|
-
if (this.labelTargets) {
|
|
857
|
-
const target = this.labelTargets.get(node.attrs.id);
|
|
858
|
-
if (target) {
|
|
859
|
-
const $label = this.createElement('label');
|
|
860
|
-
$label.textContent = target.label;
|
|
861
|
-
$element.appendChild($label);
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
};
|
|
865
|
-
const appendAttributions = ($element, node) => {
|
|
866
|
-
if (node.attrs.attribution) {
|
|
867
|
-
const $attrib = this.createElement('attrib');
|
|
868
|
-
$attrib.textContent = node.attrs.attribution.literal;
|
|
869
|
-
$element.appendChild($attrib);
|
|
870
|
-
}
|
|
871
|
-
};
|
|
872
|
-
const appendTable = ($element, node) => {
|
|
873
|
-
const tableNode = this.getFirstChildOfType(schema.nodes.table, node);
|
|
874
|
-
const colGroupNode = this.getFirstChildOfType(schema.nodes.table_colgroup, node);
|
|
875
|
-
if (!tableNode) {
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
const $table = this.serializeNode(tableNode);
|
|
879
|
-
const $tbody = this.createElement('tbody');
|
|
880
|
-
while ($table.firstChild) {
|
|
881
|
-
const $child = $table.firstChild;
|
|
882
|
-
$table.removeChild($child);
|
|
883
|
-
$tbody.appendChild($child);
|
|
884
|
-
}
|
|
885
|
-
$table.appendChild($tbody);
|
|
886
|
-
this.normalizeTable($table);
|
|
887
|
-
if (colGroupNode) {
|
|
888
|
-
const $colGroup = this.serializeNode(colGroupNode);
|
|
889
|
-
$table.insertBefore($colGroup, $table.firstChild);
|
|
890
|
-
}
|
|
891
|
-
$element.appendChild($table);
|
|
892
|
-
};
|
|
893
|
-
const createBoxElement = (node) => {
|
|
894
|
-
const $boxedText = createElement(node, 'boxed-text');
|
|
895
|
-
if (node.attrs.type) {
|
|
896
|
-
$boxedText.setAttribute('content-type', node.attrs.type);
|
|
897
|
-
}
|
|
898
|
-
appendLabels($boxedText, node);
|
|
899
|
-
const child = node.firstChild;
|
|
900
|
-
if (child?.type === schema.nodes.caption_title) {
|
|
901
|
-
this.appendCaption($boxedText, node);
|
|
902
|
-
}
|
|
903
|
-
processChildNodes($boxedText, node, node.type.schema.nodes.section);
|
|
904
|
-
return $boxedText;
|
|
905
|
-
};
|
|
906
|
-
const abstractTypeAttrs = (category) => category && category !== 'abstract' ? { 'abstract-type': category } : {};
|
|
907
|
-
const createAbstract = (node) => [
|
|
908
|
-
'abstract',
|
|
909
|
-
abstractTypeAttrs(node.attrs.category),
|
|
910
|
-
0,
|
|
911
|
-
];
|
|
912
|
-
const createTransAbstract = (node) => [
|
|
913
|
-
'trans-abstract',
|
|
914
|
-
{
|
|
915
|
-
[`${XML_NAMESPACE} lang`]: node.attrs.lang ?? '',
|
|
916
|
-
...abstractTypeAttrs(node.attrs.category),
|
|
917
|
-
},
|
|
918
|
-
0,
|
|
919
|
-
];
|
|
920
|
-
const isChildOfNodeType = (targetID, type, descend = false) => {
|
|
921
|
-
const nodes = this.getChildrenOfType(type);
|
|
922
|
-
return nodes.some((node) => {
|
|
923
|
-
const result = findChildrenByAttr(node, (attrs) => attrs.id === targetID, descend)[0];
|
|
924
|
-
return !!result;
|
|
925
|
-
});
|
|
926
|
-
};
|
|
927
|
-
const findParentHeroImage = (targetID) => {
|
|
928
|
-
const heroes = this.getChildrenOfType(schema.nodes.hero_image);
|
|
929
|
-
return heroes.find((hero) => !!findChildrenByAttr(hero, (attrs) => attrs.id === targetID)[0]);
|
|
930
|
-
};
|
|
931
|
-
const createImage = (node) => {
|
|
932
|
-
const graphicNode = node.content.firstChild;
|
|
933
|
-
if (!graphicNode) {
|
|
934
|
-
return '';
|
|
935
|
-
}
|
|
936
|
-
const $graphic = createGraphic(graphicNode);
|
|
937
|
-
if (node.attrs.extLink) {
|
|
938
|
-
const $extLink = this.appendElement($graphic, 'ext-link');
|
|
939
|
-
$extLink.setAttributeNS(XLINK_NAMESPACE, 'href', node.attrs.extLink);
|
|
940
|
-
}
|
|
941
|
-
this.appendCaption($graphic, node);
|
|
942
|
-
this.appendChildNodeOfType($graphic, node, schema.nodes.alt_text);
|
|
943
|
-
this.appendChildNodeOfType($graphic, node, schema.nodes.long_desc);
|
|
944
|
-
return $graphic;
|
|
945
|
-
};
|
|
946
|
-
const createGraphic = (node) => {
|
|
947
|
-
const $graphic = this.createElement('graphic');
|
|
948
|
-
$graphic.setAttributeNS(XLINK_NAMESPACE, 'xlink:href', node.attrs.src);
|
|
949
|
-
const hero = findParentHeroImage(node.attrs.id);
|
|
950
|
-
if (hero) {
|
|
951
|
-
$graphic.setAttribute('content-type', hero.attrs.type || 'leading');
|
|
952
|
-
}
|
|
953
|
-
else if (!isChildOfNodeType(node.attrs.id, schema.nodes.figure_element) &&
|
|
954
|
-
node.attrs.type) {
|
|
955
|
-
$graphic.setAttribute('content-type', node.attrs.type);
|
|
956
|
-
}
|
|
957
|
-
return $graphic;
|
|
958
|
-
};
|
|
959
|
-
const createFigureElement = (node, contentNodeType) => {
|
|
960
|
-
const $fig = createElement(node, 'fig');
|
|
961
|
-
const figNode = this.getFirstChildOfType(schema.nodes.figure, node);
|
|
962
|
-
const figType = figNode?.attrs.type;
|
|
963
|
-
if (figType) {
|
|
964
|
-
$fig.setAttribute('fig-type', figType);
|
|
965
|
-
}
|
|
966
|
-
appendLabels($fig, node);
|
|
967
|
-
this.appendCaption($fig, node);
|
|
968
|
-
this.appendChildNodeOfType($fig, node, schema.nodes.alt_text);
|
|
969
|
-
this.appendChildNodeOfType($fig, node, schema.nodes.long_desc);
|
|
970
|
-
this.appendChildNodeOfType($fig, node, node.type.schema.nodes.footnotes_element);
|
|
971
|
-
processChildNodes($fig, node, contentNodeType);
|
|
972
|
-
appendAttributions($fig, node);
|
|
973
|
-
if (isExecutableNodeType(node.type)) {
|
|
974
|
-
processExecutableNode(node, $fig);
|
|
975
|
-
}
|
|
976
|
-
moveAltTextAndLongDescToGraphics($fig);
|
|
977
|
-
return $fig;
|
|
978
|
-
};
|
|
979
|
-
const moveAltTextAndLongDescToGraphics = ($element) => {
|
|
980
|
-
const $altText = $element.querySelector('alt-text');
|
|
981
|
-
const $longDesc = $element.querySelector('long-desc');
|
|
982
|
-
const $graphics = $element.querySelectorAll('graphic');
|
|
983
|
-
if ($graphics.length === 0) {
|
|
984
|
-
return;
|
|
985
|
-
}
|
|
986
|
-
$graphics.forEach(($graphic) => {
|
|
987
|
-
if ($longDesc) {
|
|
988
|
-
$graphic.prepend($longDesc.cloneNode(true));
|
|
989
|
-
}
|
|
990
|
-
if ($altText) {
|
|
991
|
-
$graphic.prepend($altText.cloneNode(true));
|
|
992
|
-
}
|
|
993
|
-
});
|
|
994
|
-
$altText?.remove();
|
|
995
|
-
$longDesc?.remove();
|
|
996
|
-
};
|
|
997
|
-
const createTableElement = (node) => {
|
|
998
|
-
const nodeName = 'table-wrap';
|
|
999
|
-
const $tableWrap = createElement(node, nodeName);
|
|
1000
|
-
appendLabels($tableWrap, node);
|
|
1001
|
-
this.appendCaption($tableWrap, node);
|
|
1002
|
-
this.appendChildNodeOfType($tableWrap, node, schema.nodes.alt_text);
|
|
1003
|
-
this.appendChildNodeOfType($tableWrap, node, schema.nodes.long_desc);
|
|
1004
|
-
appendTable($tableWrap, node);
|
|
1005
|
-
this.appendChildNodeOfType($tableWrap, node, node.type.schema.nodes.table_element_footer);
|
|
1006
|
-
if (isExecutableNodeType(node.type)) {
|
|
1007
|
-
processExecutableNode(node, $tableWrap);
|
|
1008
|
-
}
|
|
1009
|
-
return $tableWrap;
|
|
1010
|
-
};
|
|
1011
|
-
const processExecutableNode = (node, $element) => {
|
|
1012
|
-
const listingNode = this.getFirstChildOfType(schema.nodes.listing, node);
|
|
1013
|
-
if (listingNode) {
|
|
1014
|
-
const { contents, languageKey } = listingNode.attrs;
|
|
1015
|
-
if (contents && languageKey) {
|
|
1016
|
-
const $fig = this.createElement('fig');
|
|
1017
|
-
$fig.setAttribute('specific-use', 'source');
|
|
1018
|
-
$element.appendChild($fig);
|
|
1019
|
-
const $code = this.createElement('code');
|
|
1020
|
-
$code.setAttribute('executable', 'true');
|
|
1021
|
-
$code.setAttribute('language', languageKey);
|
|
1022
|
-
$code.textContent = contents;
|
|
1023
|
-
$fig.appendChild($code);
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
};
|
|
1027
763
|
};
|
|
1028
|
-
this.serializeNode = (node) => this.serializer.serializeNode(node, {
|
|
1029
|
-
document: this.document,
|
|
1030
|
-
});
|
|
1031
764
|
this.buildContributorGroup = (contributors) => {
|
|
1032
765
|
if (!contributors.length) {
|
|
1033
766
|
return;
|
|
@@ -1146,6 +879,10 @@ export class JATSExporter {
|
|
|
1146
879
|
'vocab-term-identifier': url,
|
|
1147
880
|
});
|
|
1148
881
|
});
|
|
882
|
+
const bio = this.getFirstChildOfType(schema.nodes.bio, contributor);
|
|
883
|
+
if (bio?.childCount) {
|
|
884
|
+
$contrib.appendChild(this.serializeNode(bio));
|
|
885
|
+
}
|
|
1149
886
|
return $contrib;
|
|
1150
887
|
};
|
|
1151
888
|
this.buildAffiliationElement = (affiliation) => {
|
|
@@ -1260,17 +997,6 @@ export class JATSExporter {
|
|
|
1260
997
|
});
|
|
1261
998
|
};
|
|
1262
999
|
this.buildKeywords = () => this.getChildrenOfType(schema.nodes.keyword_group).map((group) => this.serializeNode(group));
|
|
1263
|
-
this.changeTag = ($node, tag) => {
|
|
1264
|
-
const $clone = this.createElement(tag);
|
|
1265
|
-
for (const attr of $node.attributes) {
|
|
1266
|
-
$clone.setAttributeNS(null, attr.name, attr.value);
|
|
1267
|
-
}
|
|
1268
|
-
while ($node.firstChild) {
|
|
1269
|
-
$clone.appendChild($node.firstChild);
|
|
1270
|
-
}
|
|
1271
|
-
$node.replaceWith($clone);
|
|
1272
|
-
return $clone;
|
|
1273
|
-
};
|
|
1274
1000
|
this.normalizeTable = ($table) => {
|
|
1275
1001
|
let $tbody;
|
|
1276
1002
|
Array.from($table.childNodes).forEach(($child) => {
|
|
@@ -1355,14 +1081,174 @@ export class JATSExporter {
|
|
|
1355
1081
|
}
|
|
1356
1082
|
};
|
|
1357
1083
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1084
|
+
appendLabels($element, node) {
|
|
1085
|
+
if (this.labelTargets) {
|
|
1086
|
+
const target = this.labelTargets.get(node.attrs.id);
|
|
1087
|
+
if (target) {
|
|
1088
|
+
const $label = this.createElement('label');
|
|
1089
|
+
$label.textContent = target.label;
|
|
1090
|
+
$element.appendChild($label);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
appendAttributions($element, node) {
|
|
1095
|
+
if (node.attrs.attribution) {
|
|
1096
|
+
const $attrib = this.createElement('attrib');
|
|
1097
|
+
$attrib.textContent = node.attrs.attribution.literal;
|
|
1098
|
+
$element.appendChild($attrib);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
appendTable($element, node) {
|
|
1102
|
+
const tableNode = this.getFirstChildOfType(schema.nodes.table, node);
|
|
1103
|
+
const colGroupNode = this.getFirstChildOfType(schema.nodes.table_colgroup, node);
|
|
1104
|
+
if (!tableNode) {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
const $table = this.serializeNode(tableNode);
|
|
1108
|
+
const $tbody = this.createElement('tbody');
|
|
1109
|
+
while ($table.firstChild) {
|
|
1110
|
+
const $child = $table.firstChild;
|
|
1111
|
+
$table.removeChild($child);
|
|
1112
|
+
$tbody.appendChild($child);
|
|
1113
|
+
}
|
|
1114
|
+
$table.appendChild($tbody);
|
|
1115
|
+
this.normalizeTable($table);
|
|
1116
|
+
if (colGroupNode) {
|
|
1117
|
+
const $colGroup = this.serializeNode(colGroupNode);
|
|
1118
|
+
$table.insertBefore($colGroup, $table.firstChild);
|
|
1119
|
+
}
|
|
1120
|
+
$element.appendChild($table);
|
|
1360
1121
|
}
|
|
1361
|
-
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1122
|
+
createBoxElement(node) {
|
|
1123
|
+
const $boxedText = this.createElementWithID(node, 'boxed-text');
|
|
1124
|
+
if (node.attrs.type) {
|
|
1125
|
+
$boxedText.setAttribute('content-type', node.attrs.type);
|
|
1126
|
+
}
|
|
1127
|
+
this.appendLabels($boxedText, node);
|
|
1128
|
+
const child = node.firstChild;
|
|
1129
|
+
if (child?.type === schema.nodes.caption_title) {
|
|
1130
|
+
this.appendCaption($boxedText, node);
|
|
1131
|
+
}
|
|
1132
|
+
this.processChildNodes($boxedText, node, node.type.schema.nodes.section);
|
|
1133
|
+
return $boxedText;
|
|
1134
|
+
}
|
|
1135
|
+
abstractTypeAttrs(category) {
|
|
1136
|
+
return category && category !== 'abstract'
|
|
1137
|
+
? { 'abstract-type': category }
|
|
1138
|
+
: {};
|
|
1139
|
+
}
|
|
1140
|
+
createAbstract(node) {
|
|
1141
|
+
return ['abstract', this.abstractTypeAttrs(node.attrs.category), 0];
|
|
1142
|
+
}
|
|
1143
|
+
createTransAbstract(node) {
|
|
1144
|
+
return [
|
|
1145
|
+
'trans-abstract',
|
|
1146
|
+
{
|
|
1147
|
+
[`${XML_NAMESPACE} lang`]: node.attrs.lang ?? '',
|
|
1148
|
+
...this.abstractTypeAttrs(node.attrs.category),
|
|
1149
|
+
},
|
|
1150
|
+
0,
|
|
1151
|
+
];
|
|
1152
|
+
}
|
|
1153
|
+
createGraphic(node) {
|
|
1154
|
+
const $graphic = this.createElement('graphic');
|
|
1155
|
+
$graphic.setAttributeNS(XLINK_NAMESPACE, 'xlink:href', node.attrs.src);
|
|
1156
|
+
const findParentHeroImage = (targetID) => {
|
|
1157
|
+
const heroes = this.getChildrenOfType(schema.nodes.hero_image);
|
|
1158
|
+
return heroes.find((hero) => !!findChildrenByAttr(hero, (attrs) => attrs.id === targetID)[0]);
|
|
1159
|
+
};
|
|
1160
|
+
const hero = findParentHeroImage(node.attrs.id);
|
|
1161
|
+
if (hero) {
|
|
1162
|
+
$graphic.setAttribute('content-type', hero.attrs.type || 'leading');
|
|
1163
|
+
}
|
|
1164
|
+
else if (!this.isChildOfNodeType(node.attrs.id, schema.nodes.figure_element) &&
|
|
1165
|
+
node.attrs.type) {
|
|
1166
|
+
$graphic.setAttribute('content-type', node.attrs.type);
|
|
1167
|
+
}
|
|
1168
|
+
return $graphic;
|
|
1169
|
+
}
|
|
1170
|
+
createImage(node) {
|
|
1171
|
+
const graphicNode = node.content.firstChild;
|
|
1172
|
+
if (!graphicNode) {
|
|
1173
|
+
return '';
|
|
1174
|
+
}
|
|
1175
|
+
const $graphic = this.createGraphic(graphicNode);
|
|
1176
|
+
if (node.attrs.extLink) {
|
|
1177
|
+
const $extLink = this.appendElement($graphic, 'ext-link');
|
|
1178
|
+
$extLink.setAttributeNS(XLINK_NAMESPACE, 'href', node.attrs.extLink);
|
|
1179
|
+
}
|
|
1180
|
+
this.appendCaption($graphic, node);
|
|
1181
|
+
this.appendChildNodeOfType($graphic, node, schema.nodes.alt_text);
|
|
1182
|
+
this.appendChildNodeOfType($graphic, node, schema.nodes.long_desc);
|
|
1183
|
+
return $graphic;
|
|
1184
|
+
}
|
|
1185
|
+
createFigureElement(node, contentNodeType) {
|
|
1186
|
+
const $fig = this.createElementWithID(node, 'fig');
|
|
1187
|
+
const figNode = this.getFirstChildOfType(schema.nodes.figure, node);
|
|
1188
|
+
const figType = figNode?.attrs.type;
|
|
1189
|
+
if (figType) {
|
|
1190
|
+
$fig.setAttribute('fig-type', figType);
|
|
1191
|
+
}
|
|
1192
|
+
this.appendLabels($fig, node);
|
|
1193
|
+
this.appendCaption($fig, node);
|
|
1194
|
+
this.appendChildNodeOfType($fig, node, schema.nodes.alt_text);
|
|
1195
|
+
this.appendChildNodeOfType($fig, node, schema.nodes.long_desc);
|
|
1196
|
+
this.appendChildNodeOfType($fig, node, node.type.schema.nodes.footnotes_element);
|
|
1197
|
+
this.processChildNodes($fig, node, contentNodeType);
|
|
1198
|
+
this.appendAttributions($fig, node);
|
|
1199
|
+
if (isExecutableNodeType(node.type)) {
|
|
1200
|
+
this.processExecutableNode(node, $fig);
|
|
1201
|
+
}
|
|
1202
|
+
this.moveAltTextAndLongDescToGraphics($fig);
|
|
1203
|
+
return $fig;
|
|
1204
|
+
}
|
|
1205
|
+
moveAltTextAndLongDescToGraphics($element) {
|
|
1206
|
+
const $altText = $element.querySelector('alt-text');
|
|
1207
|
+
const $longDesc = $element.querySelector('long-desc');
|
|
1208
|
+
const $graphics = $element.querySelectorAll('graphic');
|
|
1209
|
+
if ($graphics.length === 0) {
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
$graphics.forEach(($graphic) => {
|
|
1213
|
+
if ($longDesc) {
|
|
1214
|
+
$graphic.prepend($longDesc.cloneNode(true));
|
|
1215
|
+
}
|
|
1216
|
+
if ($altText) {
|
|
1217
|
+
$graphic.prepend($altText.cloneNode(true));
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
$altText?.remove();
|
|
1221
|
+
$longDesc?.remove();
|
|
1222
|
+
}
|
|
1223
|
+
createTableElement(node) {
|
|
1224
|
+
const nodeName = 'table-wrap';
|
|
1225
|
+
const $tableWrap = this.createElementWithID(node, nodeName);
|
|
1226
|
+
this.appendLabels($tableWrap, node);
|
|
1227
|
+
this.appendCaption($tableWrap, node);
|
|
1228
|
+
this.appendChildNodeOfType($tableWrap, node, schema.nodes.alt_text);
|
|
1229
|
+
this.appendChildNodeOfType($tableWrap, node, schema.nodes.long_desc);
|
|
1230
|
+
this.appendTable($tableWrap, node);
|
|
1231
|
+
this.appendChildNodeOfType($tableWrap, node, node.type.schema.nodes.table_element_footer);
|
|
1232
|
+
if (isExecutableNodeType(node.type)) {
|
|
1233
|
+
this.processExecutableNode(node, $tableWrap);
|
|
1234
|
+
}
|
|
1235
|
+
return $tableWrap;
|
|
1236
|
+
}
|
|
1237
|
+
processExecutableNode(node, $element) {
|
|
1238
|
+
const listingNode = this.getFirstChildOfType(schema.nodes.listing, node);
|
|
1239
|
+
if (listingNode) {
|
|
1240
|
+
const { contents, languageKey } = listingNode.attrs;
|
|
1241
|
+
if (contents && languageKey) {
|
|
1242
|
+
const $fig = this.createElement('fig');
|
|
1243
|
+
$fig.setAttribute('specific-use', 'source');
|
|
1244
|
+
$element.appendChild($fig);
|
|
1245
|
+
const $code = this.createElement('code');
|
|
1246
|
+
$code.setAttribute('executable', 'true');
|
|
1247
|
+
$code.setAttribute('language', languageKey);
|
|
1248
|
+
$code.textContent = contents;
|
|
1249
|
+
$fig.appendChild($code);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1366
1252
|
}
|
|
1367
1253
|
createEquation(node, isInline = false) {
|
|
1368
1254
|
if (node.attrs.format === 'tex') {
|
|
@@ -1386,10 +1272,6 @@ export class JATSExporter {
|
|
|
1386
1272
|
return $mathml;
|
|
1387
1273
|
}
|
|
1388
1274
|
}
|
|
1389
|
-
fillEmptyElements($article, selector, tagName = 'p') {
|
|
1390
|
-
const $empty = Array.from($article.querySelectorAll(selector)).filter(($el) => !$el.innerHTML);
|
|
1391
|
-
$empty.forEach(($element) => $element.appendChild(this.createElement(tagName)));
|
|
1392
|
-
}
|
|
1393
1275
|
addParagraphsToSections($article) {
|
|
1394
1276
|
const $sections = $article.querySelectorAll('sec, abstract');
|
|
1395
1277
|
const TITLE_TAGS = new Set(['title', 'label', 'sec-meta']);
|