@manuscripts/transform 1.2.3 → 1.2.4-LEAN-2066

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/cjs/jats/importer/jats-body-dom-parser.js +33 -1
  2. package/dist/cjs/jats/importer/jats-body-transformations.js +14 -0
  3. package/dist/cjs/jats/importer/jats-comments.js +24 -0
  4. package/dist/cjs/jats/importer/jats-front-parser.js +0 -25
  5. package/dist/cjs/jats/importer/parse-jats-article.js +2 -6
  6. package/dist/cjs/jats/jats-exporter.js +5 -7
  7. package/dist/cjs/lib/utils.js +12 -1
  8. package/dist/cjs/schema/index.js +2 -0
  9. package/dist/cjs/schema/nodes/keyword.js +1 -2
  10. package/dist/cjs/schema/nodes/keywords_element.js +2 -1
  11. package/dist/cjs/schema/nodes/keywords_group.js +48 -0
  12. package/dist/cjs/schema/nodes/keywords_section.js +1 -1
  13. package/dist/cjs/transformer/decode.js +29 -23
  14. package/dist/cjs/transformer/encode.js +6 -3
  15. package/dist/cjs/transformer/node-types.js +1 -0
  16. package/dist/cjs/transformer/object-types.js +2 -1
  17. package/dist/es/jats/importer/jats-body-dom-parser.js +33 -1
  18. package/dist/es/jats/importer/jats-body-transformations.js +14 -0
  19. package/dist/es/jats/importer/jats-comments.js +25 -1
  20. package/dist/es/jats/importer/jats-front-parser.js +1 -26
  21. package/dist/es/jats/importer/parse-jats-article.js +2 -6
  22. package/dist/es/jats/jats-exporter.js +5 -7
  23. package/dist/es/lib/utils.js +10 -0
  24. package/dist/es/schema/index.js +2 -0
  25. package/dist/es/schema/nodes/keyword.js +1 -2
  26. package/dist/es/schema/nodes/keywords_element.js +2 -1
  27. package/dist/es/schema/nodes/keywords_group.js +44 -0
  28. package/dist/es/schema/nodes/keywords_section.js +1 -1
  29. package/dist/es/transformer/decode.js +29 -23
  30. package/dist/es/transformer/encode.js +6 -3
  31. package/dist/es/transformer/node-types.js +1 -0
  32. package/dist/es/transformer/object-types.js +1 -0
  33. package/dist/types/jats/importer/jats-body-transformations.d.ts +1 -0
  34. package/dist/types/jats/importer/jats-front-parser.d.ts +6 -11
  35. package/dist/types/lib/utils.d.ts +1 -0
  36. package/dist/types/schema/nodes/keywords_group.d.ts +26 -0
  37. package/dist/types/schema/types.d.ts +1 -1
  38. package/dist/types/transformer/decode.d.ts +1 -0
  39. package/dist/types/transformer/object-types.d.ts +2 -1
  40. package/package.json +1 -1
@@ -505,6 +505,22 @@ const nodes = [
505
505
  };
506
506
  },
507
507
  },
508
+ {
509
+ tag: 'sec[sec-type="keywords"]',
510
+ node: 'keywords_section',
511
+ getAttrs: (node) => {
512
+ const element = node;
513
+ return {
514
+ id: element.getAttribute('id'),
515
+ category: (0, transformer_1.chooseSectionCategory)(element),
516
+ };
517
+ },
518
+ },
519
+ {
520
+ tag: 'kwd-group-list',
521
+ context: 'keywords_section/',
522
+ node: 'keywords_element',
523
+ },
508
524
  {
509
525
  tag: 'sec',
510
526
  node: 'section',
@@ -516,6 +532,22 @@ const nodes = [
516
532
  };
517
533
  },
518
534
  },
535
+ {
536
+ tag: 'kwd-group',
537
+ context: 'keywords_element/',
538
+ node: 'keywords_group',
539
+ getAttrs: (node) => {
540
+ const element = node;
541
+ return {
542
+ type: element.getAttribute('kwd-group-type'),
543
+ };
544
+ },
545
+ },
546
+ {
547
+ tag: 'kwd',
548
+ context: 'keywords_group//',
549
+ node: 'keyword',
550
+ },
519
551
  {
520
552
  tag: 'label',
521
553
  context: 'section/',
@@ -568,7 +600,7 @@ const nodes = [
568
600
  {
569
601
  tag: 'title',
570
602
  node: 'section_title',
571
- context: 'section/|footnotes_section/|bibliography_section/',
603
+ context: 'section/|footnotes_section/|bibliography_section/|keywords_section/',
572
604
  },
573
605
  {
574
606
  tag: 'title',
@@ -266,4 +266,18 @@ exports.jatsBodyTransformations = {
266
266
  paragraph === null || paragraph === void 0 ? void 0 : paragraph.replaceWith(parent);
267
267
  });
268
268
  },
269
+ moveKeywordsToBody(document, body, createElement) {
270
+ const keywordGroups = [...document.querySelectorAll('kwd-group')];
271
+ if (keywordGroups.length > 0) {
272
+ const section = createElement('sec');
273
+ section.setAttribute('sec-type', 'keywords');
274
+ const title = createElement('title');
275
+ title.textContent = 'Keywords';
276
+ section.append(title);
277
+ const kwdGroupsEl = createElement('kwd-group-list');
278
+ kwdGroupsEl.append(keywordGroups[0]);
279
+ section.append(kwdGroupsEl);
280
+ body.prepend(section);
281
+ }
282
+ },
269
283
  };
@@ -63,6 +63,21 @@ const insertToken = (doc, processingInstructionNode, authorQueriesMap) => {
63
63
  return parentElement.insertBefore(tokenNode, processingInstructionNode);
64
64
  }
65
65
  };
66
+ const extractCommentsFromKeywords = (tokens, model, authorQueriesMap) => {
67
+ var _a;
68
+ const commentAnnotations = [];
69
+ const name = model.name;
70
+ const filteredTokens = filterAndSortTokens(tokens, name);
71
+ let content = name;
72
+ for (const token of filteredTokens) {
73
+ content = name.replace(token, '');
74
+ const query = authorQueriesMap.get(token);
75
+ const commentAnnotation = (0, transformer_1.buildComment)((_a = model.containedGroup) !== null && _a !== void 0 ? _a : (0, uuid_1.v4)(), `${query}`, undefined, [(0, transformer_1.buildContribution)(DEFAULT_PROFILE_ID)], DEFAULT_ANNOTATION_COLOR);
76
+ model['name'] = content;
77
+ commentAnnotations.push(commentAnnotation);
78
+ }
79
+ return commentAnnotations;
80
+ };
66
81
  const createComments = (authorQueriesMap, manuscriptModels) => {
67
82
  const tokens = [...authorQueriesMap.keys()];
68
83
  const commentAnnotations = [];
@@ -71,10 +86,19 @@ const createComments = (authorQueriesMap, manuscriptModels) => {
71
86
  const comments = addCommentsFromMarkedProcessingInstructions(tokens, model, authorQueriesMap);
72
87
  commentAnnotations.push(...comments);
73
88
  }
89
+ else if ((0, transformer_1.isKeyword)(model)) {
90
+ const comments = extractCommentsFromKeywords(tokens, model, authorQueriesMap);
91
+ commentAnnotations.push(...comments);
92
+ }
74
93
  }
75
94
  return commentAnnotations;
76
95
  };
77
96
  exports.createComments = createComments;
97
+ function filterAndSortTokens(tokens, content) {
98
+ return tokens
99
+ .filter((token) => content.indexOf(token) >= 0)
100
+ .sort((a, b) => content.indexOf(a) - content.indexOf(b));
101
+ }
78
102
  const addCommentsFromMarkedProcessingInstructions = (tokens, model, authorQueriesMap) => {
79
103
  const commentAnnotations = [];
80
104
  for (const field of ['contents', 'caption', 'title']) {
@@ -68,31 +68,6 @@ exports.jatsFrontParser = {
68
68
  }
69
69
  return (0, jats_journal_meta_parser_1.parseJournalMeta)(journalMeta);
70
70
  },
71
- parseKeywords(keywordGroupNodes) {
72
- var _a, _b;
73
- if (!keywordGroupNodes) {
74
- return { groups: [], keywords: [] };
75
- }
76
- let keywordPriority = 1;
77
- const keywordGroups = { groups: [], keywords: [] };
78
- for (const keywordGroupNode of keywordGroupNodes) {
79
- const manuscriptKeywordGroup = (0, builders_1.buildKeywordGroup)({
80
- title: ((_a = keywordGroupNode.querySelector('title')) === null || _a === void 0 ? void 0 : _a.textContent) || undefined,
81
- label: ((_b = keywordGroupNode.querySelector('label')) === null || _b === void 0 ? void 0 : _b.textContent) || undefined,
82
- type: keywordGroupNode.getAttribute('kwd-group-type') || undefined,
83
- });
84
- keywordGroups.groups.push(manuscriptKeywordGroup);
85
- for (const keywordNode of keywordGroupNode.querySelectorAll('kwd')) {
86
- if (keywordNode.textContent) {
87
- const keyword = (0, builders_1.buildKeyword)(keywordNode.textContent);
88
- keyword.priority = keywordPriority++;
89
- keyword.containedGroup = manuscriptKeywordGroup._id;
90
- keywordGroups.keywords.push(keyword);
91
- }
92
- }
93
- }
94
- return keywordGroups;
95
- },
96
71
  parseDates(historyNode) {
97
72
  if (!historyNode) {
98
73
  return undefined;
@@ -54,9 +54,6 @@ const parseJATSFront = async (front) => {
54
54
  const manuscriptMeta = Object.assign({ title: title ? inlineContentsFromJATSTitle(title) : undefined, subtitle: subtitle ? inlineContentsFromJATSTitle(subtitle) : undefined, runningTitle: runningTitle
55
55
  ? inlineContentsFromJATSTitle(runningTitle)
56
56
  : undefined }, jats_front_parser_1.jatsFrontParser.parseCounts(articleMeta === null || articleMeta === void 0 ? void 0 : articleMeta.querySelector('counts')));
57
- const keywordGroupNodes = articleMeta === null || articleMeta === void 0 ? void 0 : articleMeta.querySelectorAll('kwd-group');
58
- const { keywords, groups: keywordGroups } = jats_front_parser_1.jatsFrontParser.parseKeywords(keywordGroupNodes);
59
- const manuscript_keywordIDs = keywords.length > 0 ? keywords.map((k) => k._id) : undefined;
60
57
  const { affiliations, affiliationIDs } = jats_front_parser_1.jatsFrontParser.parseAffiliationNodes([
61
58
  ...front.querySelectorAll('article-meta > contrib-group > aff'),
62
59
  ]);
@@ -73,14 +70,12 @@ const parseJATSFront = async (front) => {
73
70
  const supplements = jats_front_parser_1.jatsFrontParser.parseSupplements([
74
71
  ...front.querySelectorAll('article-meta > supplementary-material'),
75
72
  ]);
76
- const manuscript = Object.assign(Object.assign(Object.assign(Object.assign({}, (0, builders_1.buildManuscript)()), manuscriptMeta), { keywordIDs: manuscript_keywordIDs }), history);
73
+ const manuscript = Object.assign(Object.assign(Object.assign({}, (0, builders_1.buildManuscript)()), manuscriptMeta), history);
77
74
  return {
78
75
  models: generateModelIDs([
79
76
  manuscript,
80
- ...keywords,
81
77
  ...affiliations,
82
78
  ...authors,
83
- ...keywordGroups,
84
79
  ...footnotes,
85
80
  ...correspondingList,
86
81
  journal,
@@ -121,6 +116,7 @@ const parseJATSBody = (document, body, bibliographyItems, refModels, referenceId
121
116
  jats_body_transformations_1.jatsBodyTransformations.moveCaptionsToEnd(body);
122
117
  jats_body_transformations_1.jatsBodyTransformations.moveTableFooterToEnd(body);
123
118
  jats_body_transformations_1.jatsBodyTransformations.moveBlockNodesFromParagraph(document, body, createElement);
119
+ jats_body_transformations_1.jatsBodyTransformations.moveKeywordsToBody(document, body, createElement);
124
120
  const node = jats_body_dom_parser_1.jatsBodyDOMParser.parse(body);
125
121
  if (!node.firstChild) {
126
122
  throw new Error('No content was parsed from the JATS article body');
@@ -398,9 +398,7 @@ class JATSExporter {
398
398
  if (history.childElementCount) {
399
399
  articleMeta.appendChild(history);
400
400
  }
401
- if (manuscript.keywordIDs) {
402
- this.buildKeywords(articleMeta, manuscript.keywordIDs);
403
- }
401
+ this.buildKeywords(articleMeta);
404
402
  let countingElements = [];
405
403
  if (manuscript.genericCounts) {
406
404
  const elements = manuscript.genericCounts.map((el) => {
@@ -624,6 +622,7 @@ class JATSExporter {
624
622
  bibliography_element: () => '',
625
623
  bibliography_item: () => '',
626
624
  comment_list: () => '',
625
+ keywords_group: () => '',
627
626
  bibliography_section: (node) => [
628
627
  'ref-list',
629
628
  { id: normalizeID(node.attrs.id) },
@@ -1529,10 +1528,8 @@ class JATSExporter {
1529
1528
  return name;
1530
1529
  };
1531
1530
  }
1532
- buildKeywords(articleMeta, keywordIDs) {
1533
- const keywords = keywordIDs
1534
- .map((id) => this.modelMap.get(id))
1535
- .filter((model) => model && model.name);
1531
+ buildKeywords(articleMeta) {
1532
+ const keywords = [...this.modelMap.values()].filter((model) => model.objectType === json_schema_1.ObjectTypes.Keyword);
1536
1533
  const keywordGroups = new Map();
1537
1534
  keywords.forEach((keyword) => {
1538
1535
  const containedGroup = keyword.containedGroup || '';
@@ -1566,6 +1563,7 @@ class JATSExporter {
1566
1563
  kwd.textContent = keyword.name;
1567
1564
  kwdGroup.appendChild(kwd);
1568
1565
  }
1566
+ articleMeta.appendChild(kwdGroup);
1569
1567
  }
1570
1568
  }
1571
1569
  }
@@ -15,7 +15,8 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.findParentNodeClosestToPos = exports.isInGraphicalAbstractSection = exports.findNodePositions = exports.iterateChildren = void 0;
18
+ exports.findParentNodeClosestToPos = exports.isInBibliographySection = exports.isInGraphicalAbstractSection = exports.findNodePositions = exports.iterateChildren = void 0;
19
+ const bibliography_section_1 = require("../schema/nodes/bibliography_section");
19
20
  const graphical_abstract_section_1 = require("../schema/nodes/graphical_abstract_section");
20
21
  function* iterateChildren(node, recurse = false) {
21
22
  for (let i = 0; i < node.childCount; i++) {
@@ -50,6 +51,16 @@ const isInGraphicalAbstractSection = ($pos) => {
50
51
  return false;
51
52
  };
52
53
  exports.isInGraphicalAbstractSection = isInGraphicalAbstractSection;
54
+ const isInBibliographySection = ($pos) => {
55
+ for (let i = $pos.depth; i > 0; i--) {
56
+ const node = $pos.node(i);
57
+ if ((0, bibliography_section_1.isBibliographySectionNode)(node)) {
58
+ return true;
59
+ }
60
+ }
61
+ return false;
62
+ };
63
+ exports.isInBibliographySection = isInBibliographySection;
53
64
  const findParentNodeClosestToPos = ($pos, predicate) => {
54
65
  for (let i = $pos.depth; i > 0; i--) {
55
66
  const node = $pos.node(i);
@@ -59,6 +59,7 @@ const inline_equation_1 = require("./nodes/inline_equation");
59
59
  const inline_footnote_1 = require("./nodes/inline_footnote");
60
60
  const keyword_1 = require("./nodes/keyword");
61
61
  const keywords_element_1 = require("./nodes/keywords_element");
62
+ const keywords_group_1 = require("./nodes/keywords_group");
62
63
  const keywords_section_1 = require("./nodes/keywords_section");
63
64
  const link_1 = require("./nodes/link");
64
65
  const list_1 = require("./nodes/list");
@@ -173,6 +174,7 @@ exports.schema = new prosemirror_model_1.Schema({
173
174
  keyword: keyword_1.keyword,
174
175
  keywords_element: keywords_element_1.keywordsElement,
175
176
  keywords_section: keywords_section_1.keywordsSection,
177
+ keywords_group: keywords_group_1.keywordsGroup,
176
178
  link: link_1.link,
177
179
  list_item: list_1.listItem,
178
180
  listing: listing_1.listing,
@@ -25,8 +25,7 @@ exports.keyword = {
25
25
  dataTracked: { default: null },
26
26
  comments: { default: null },
27
27
  },
28
- inline: true,
29
- group: 'inline',
28
+ group: 'block',
30
29
  selectable: false,
31
30
  parseDOM: [
32
31
  {
@@ -18,11 +18,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.keywordsElement = void 0;
19
19
  exports.keywordsElement = {
20
20
  atom: true,
21
- content: 'keyword*',
21
+ content: 'keywords_group*',
22
22
  attrs: {
23
23
  id: { default: '' },
24
24
  contents: { default: '' },
25
25
  paragraphStyle: { default: '' },
26
+ type: { default: '' },
26
27
  dataTracked: { default: null },
27
28
  },
28
29
  group: 'block element',
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /*!
3
+ * © 2019 Atypon Systems LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.isKeywordsGroupNode = exports.keywordsGroup = void 0;
19
+ exports.keywordsGroup = {
20
+ content: 'keyword*',
21
+ attrs: {
22
+ id: { default: '' },
23
+ type: { default: '' },
24
+ dataTracked: { default: null },
25
+ },
26
+ group: 'block',
27
+ selectable: false,
28
+ parseDOM: [
29
+ {
30
+ tag: 'div.keywords',
31
+ },
32
+ ],
33
+ toDOM: (node) => {
34
+ const keywordsGroupNode = node;
35
+ return [
36
+ 'div',
37
+ {
38
+ id: keywordsGroupNode.attrs.id,
39
+ class: 'keywords',
40
+ spellcheck: 'false',
41
+ contenteditable: false,
42
+ },
43
+ 0,
44
+ ];
45
+ },
46
+ };
47
+ const isKeywordsGroupNode = (node) => node.type === node.type.schema.nodes.keywords_group;
48
+ exports.isKeywordsGroupNode = isKeywordsGroupNode;
@@ -17,7 +17,7 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.isKeywordsSectionNode = exports.keywordsSection = void 0;
19
19
  exports.keywordsSection = {
20
- content: 'section_title_plain (keywords_element | placeholder_element)',
20
+ content: 'section_title (keywords_element | placeholder_element)',
21
21
  attrs: {
22
22
  id: { default: '' },
23
23
  dataTracked: { default: null },
@@ -275,11 +275,11 @@ class Decoder {
275
275
  },
276
276
  [json_schema_1.ObjectTypes.KeywordsElement]: (data) => {
277
277
  const model = data;
278
- const keywords = this.getKeywords();
278
+ const keywordGroups = this.getKeywordGroups();
279
279
  return schema_1.schema.nodes.keywords_element.create({
280
280
  id: model._id,
281
281
  paragraphStyle: model.paragraphStyle,
282
- }, keywords);
282
+ }, keywordGroups);
283
283
  },
284
284
  [json_schema_1.ObjectTypes.Keyword]: (data) => {
285
285
  const model = data;
@@ -426,9 +426,7 @@ class Decoder {
426
426
  const elementNodes = elements
427
427
  .map(this.decode)
428
428
  .filter(exports.isManuscriptNode);
429
- const sectionTitle = isKeywordsSection
430
- ? 'section_title_plain'
431
- : 'section_title';
429
+ const sectionTitle = 'section_title';
432
430
  const sectionTitleNode = model.title
433
431
  ? this.parseContents(model.title, 'h1', this.getComments(model), {
434
432
  topNode: schema_1.schema.nodes[sectionTitle].create(),
@@ -572,19 +570,6 @@ class Decoder {
572
570
  id: (0, id_1.generateNodeID)(schema_1.schema.nodes.section),
573
571
  }));
574
572
  }
575
- const keywordsSection = getSections(this.modelMap).filter((section) => section.category === 'MPSectionCategory:keywords');
576
- const keywords = this.getKeywords();
577
- if (keywordsSection.length === 0 && keywords.length > 0) {
578
- const keywordsSection = schema_1.schema.nodes.keywords_section.createAndFill({
579
- id: (0, id_1.generateNodeID)(schema_1.schema.nodes.keywords_section),
580
- }, [
581
- schema_1.schema.nodes.section_title_plain.create({}, schema_1.schema.text('Keywords')),
582
- schema_1.schema.nodes.keywords_element.create({
583
- id: (0, id_1.generateNodeID)(schema_1.schema.nodes.keywords_element),
584
- }, keywords),
585
- ]);
586
- rootSectionNodes.unshift(keywordsSection);
587
- }
588
573
  const contents = rootSectionNodes;
589
574
  if (this.comments.size) {
590
575
  const comments = schema_1.schema.nodes.comment_list.createAndFill({
@@ -632,17 +617,16 @@ class Decoder {
632
617
  }
633
618
  return parser.parse(template.content.firstElementChild, options);
634
619
  };
635
- this.getKeywords = () => {
620
+ this.getKeywords = (id) => {
636
621
  const keywordsOfKind = [];
637
- for (const model of this.modelMap.values()) {
638
- const commentNodes = this.createCommentsNode(model);
639
- commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
622
+ const keywordsByGroup = [...this.modelMap.values()].filter((m) => m.objectType === json_schema_1.ObjectTypes.Keyword &&
623
+ m.containedGroup === id);
624
+ for (const model of keywordsByGroup) {
640
625
  if (isKeyword(model)) {
641
626
  const keyword = this.parseContents(model.name || '', 'div', this.getComments(model), {
642
627
  topNode: schema_1.schema.nodes.keyword.create({
643
628
  id: model._id,
644
629
  contents: model.name,
645
- comments: commentNodes.map((c) => c.attrs.id),
646
630
  }),
647
631
  });
648
632
  keywordsOfKind.push(keyword);
@@ -689,5 +673,27 @@ class Decoder {
689
673
  this.modelMap = modelMap;
690
674
  this.allowMissingElements = allowMissingElements;
691
675
  }
676
+ getKeywordGroups() {
677
+ const kwdGroupNodes = [];
678
+ const kwdGroupsModels = [
679
+ ...this.modelMap.values(),
680
+ ].filter((model) => model.objectType === json_schema_1.ObjectTypes.KeywordGroup);
681
+ if (kwdGroupsModels.length > 0) {
682
+ for (const kwdGroupModel of kwdGroupsModels) {
683
+ const keywords = this.getKeywords(kwdGroupModel._id);
684
+ const commentNodes = this.createCommentsNode(kwdGroupModel);
685
+ commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
686
+ const contents = [];
687
+ contents.push(...keywords);
688
+ const kwdGroupNode = schema_1.schema.nodes.keywords_group.create({
689
+ id: kwdGroupModel._id,
690
+ type: kwdGroupModel.type,
691
+ comments: commentNodes.map((c) => c.attrs.id),
692
+ }, contents);
693
+ kwdGroupNodes.push(kwdGroupNode);
694
+ }
695
+ }
696
+ return kwdGroupNodes;
697
+ }
692
698
  }
693
699
  exports.Decoder = Decoder;
@@ -401,18 +401,21 @@ const encoders = {
401
401
  SVGGlyphs: svgDefs(node.attrs.SVGRepresentation),
402
402
  }),
403
403
  keyword: (node, parent) => ({
404
- containerID: parent.attrs.id,
404
+ containedGroup: parent.attrs.id,
405
405
  name: keywordContents(node),
406
406
  }),
407
407
  keywords_element: (node) => ({
408
- contents: elementContents(node),
408
+ contents: '<div></div>',
409
409
  elementType: 'div',
410
410
  paragraphStyle: node.attrs.paragraphStyle || undefined,
411
411
  }),
412
+ keywords_group: (node) => ({
413
+ type: node.attrs.type,
414
+ }),
412
415
  keywords_section: (node, parent, path, priority) => ({
413
416
  category: (0, section_category_1.buildSectionCategory)(node),
414
417
  priority: priority.value++,
415
- title: inlineContentsOfNodeType(node, node.type.schema.nodes.section_title_plain),
418
+ title: inlineContentsOfNodeType(node, node.type.schema.nodes.section_title),
416
419
  path: path.concat([node.attrs.id]),
417
420
  elementIDs: childElements(node)
418
421
  .map((childNode) => childNode.attrs.id)
@@ -41,6 +41,7 @@ exports.nodeTypesMap = new Map([
41
41
  [schema_1.schema.nodes.keyword, json_schema_1.ObjectTypes.Keyword],
42
42
  [schema_1.schema.nodes.keywords_element, json_schema_1.ObjectTypes.KeywordsElement],
43
43
  [schema_1.schema.nodes.keywords_section, json_schema_1.ObjectTypes.Section],
44
+ [schema_1.schema.nodes.keywords_group, json_schema_1.ObjectTypes.KeywordGroup],
44
45
  [schema_1.schema.nodes.listing, json_schema_1.ObjectTypes.Listing],
45
46
  [schema_1.schema.nodes.listing_element, json_schema_1.ObjectTypes.ListingElement],
46
47
  [schema_1.schema.nodes.manuscript, json_schema_1.ObjectTypes.Manuscript],
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.isCommentAnnotation = exports.isUserProfile = exports.isTable = exports.isManuscript = exports.isFigure = exports.hasObjectType = exports.isManuscriptModel = exports.manuscriptObjects = exports.elementObjects = exports.ExtraObjectTypes = void 0;
18
+ exports.isKeyword = exports.isCommentAnnotation = exports.isUserProfile = exports.isTable = exports.isManuscript = exports.isFigure = exports.hasObjectType = exports.isManuscriptModel = exports.manuscriptObjects = exports.elementObjects = exports.ExtraObjectTypes = void 0;
19
19
  const json_schema_1 = require("@manuscripts/json-schema");
20
20
  var ExtraObjectTypes;
21
21
  (function (ExtraObjectTypes) {
@@ -55,3 +55,4 @@ exports.isManuscript = (0, exports.hasObjectType)(json_schema_1.ObjectTypes.Manu
55
55
  exports.isTable = (0, exports.hasObjectType)(json_schema_1.ObjectTypes.Table);
56
56
  exports.isUserProfile = (0, exports.hasObjectType)(json_schema_1.ObjectTypes.UserProfile);
57
57
  exports.isCommentAnnotation = (0, exports.hasObjectType)(json_schema_1.ObjectTypes.CommentAnnotation);
58
+ exports.isKeyword = (0, exports.hasObjectType)(json_schema_1.ObjectTypes.Keyword);
@@ -499,6 +499,22 @@ const nodes = [
499
499
  };
500
500
  },
501
501
  },
502
+ {
503
+ tag: 'sec[sec-type="keywords"]',
504
+ node: 'keywords_section',
505
+ getAttrs: (node) => {
506
+ const element = node;
507
+ return {
508
+ id: element.getAttribute('id'),
509
+ category: chooseSectionCategory(element),
510
+ };
511
+ },
512
+ },
513
+ {
514
+ tag: 'kwd-group-list',
515
+ context: 'keywords_section/',
516
+ node: 'keywords_element',
517
+ },
502
518
  {
503
519
  tag: 'sec',
504
520
  node: 'section',
@@ -510,6 +526,22 @@ const nodes = [
510
526
  };
511
527
  },
512
528
  },
529
+ {
530
+ tag: 'kwd-group',
531
+ context: 'keywords_element/',
532
+ node: 'keywords_group',
533
+ getAttrs: (node) => {
534
+ const element = node;
535
+ return {
536
+ type: element.getAttribute('kwd-group-type'),
537
+ };
538
+ },
539
+ },
540
+ {
541
+ tag: 'kwd',
542
+ context: 'keywords_group//',
543
+ node: 'keyword',
544
+ },
513
545
  {
514
546
  tag: 'label',
515
547
  context: 'section/',
@@ -562,7 +594,7 @@ const nodes = [
562
594
  {
563
595
  tag: 'title',
564
596
  node: 'section_title',
565
- context: 'section/|footnotes_section/|bibliography_section/',
597
+ context: 'section/|footnotes_section/|bibliography_section/|keywords_section/',
566
598
  },
567
599
  {
568
600
  tag: 'title',
@@ -263,4 +263,18 @@ export const jatsBodyTransformations = {
263
263
  paragraph === null || paragraph === void 0 ? void 0 : paragraph.replaceWith(parent);
264
264
  });
265
265
  },
266
+ moveKeywordsToBody(document, body, createElement) {
267
+ const keywordGroups = [...document.querySelectorAll('kwd-group')];
268
+ if (keywordGroups.length > 0) {
269
+ const section = createElement('sec');
270
+ section.setAttribute('sec-type', 'keywords');
271
+ const title = createElement('title');
272
+ title.textContent = 'Keywords';
273
+ section.append(title);
274
+ const kwdGroupsEl = createElement('kwd-group-list');
275
+ kwdGroupsEl.append(keywordGroups[0]);
276
+ section.append(kwdGroupsEl);
277
+ body.prepend(section);
278
+ }
279
+ },
266
280
  };
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { v4 as uuidv4 } from 'uuid';
17
- import { buildComment, buildContribution, isCommentAnnotation, isHighlightableModel, } from '../../transformer';
17
+ import { buildComment, buildContribution, isCommentAnnotation, isHighlightableModel, isKeyword, } from '../../transformer';
18
18
  const DEFAULT_ANNOTATION_COLOR = 'rgb(250, 224, 150)';
19
19
  const DEFAULT_PROFILE_ID = 'MPUserProfile:0000000000000000000000000000000000000001';
20
20
  export const parseProcessingInstruction = (node) => {
@@ -58,6 +58,21 @@ const insertToken = (doc, processingInstructionNode, authorQueriesMap) => {
58
58
  return parentElement.insertBefore(tokenNode, processingInstructionNode);
59
59
  }
60
60
  };
61
+ const extractCommentsFromKeywords = (tokens, model, authorQueriesMap) => {
62
+ var _a;
63
+ const commentAnnotations = [];
64
+ const name = model.name;
65
+ const filteredTokens = filterAndSortTokens(tokens, name);
66
+ let content = name;
67
+ for (const token of filteredTokens) {
68
+ content = name.replace(token, '');
69
+ const query = authorQueriesMap.get(token);
70
+ const commentAnnotation = buildComment((_a = model.containedGroup) !== null && _a !== void 0 ? _a : uuidv4(), `${query}`, undefined, [buildContribution(DEFAULT_PROFILE_ID)], DEFAULT_ANNOTATION_COLOR);
71
+ model['name'] = content;
72
+ commentAnnotations.push(commentAnnotation);
73
+ }
74
+ return commentAnnotations;
75
+ };
61
76
  export const createComments = (authorQueriesMap, manuscriptModels) => {
62
77
  const tokens = [...authorQueriesMap.keys()];
63
78
  const commentAnnotations = [];
@@ -66,9 +81,18 @@ export const createComments = (authorQueriesMap, manuscriptModels) => {
66
81
  const comments = addCommentsFromMarkedProcessingInstructions(tokens, model, authorQueriesMap);
67
82
  commentAnnotations.push(...comments);
68
83
  }
84
+ else if (isKeyword(model)) {
85
+ const comments = extractCommentsFromKeywords(tokens, model, authorQueriesMap);
86
+ commentAnnotations.push(...comments);
87
+ }
69
88
  }
70
89
  return commentAnnotations;
71
90
  };
91
+ function filterAndSortTokens(tokens, content) {
92
+ return tokens
93
+ .filter((token) => content.indexOf(token) >= 0)
94
+ .sort((a, b) => content.indexOf(a) - content.indexOf(b));
95
+ }
72
96
  const addCommentsFromMarkedProcessingInstructions = (tokens, model, authorQueriesMap) => {
73
97
  const commentAnnotations = [];
74
98
  for (const field of ['contents', 'caption', 'title']) {
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import debug from 'debug';
17
- import { buildAffiliation, buildBibliographicName, buildContributor, buildCorresp, buildFootnote, buildKeyword, buildKeywordGroup, buildSupplementaryMaterial, } from '../../transformer/builders';
17
+ import { buildAffiliation, buildBibliographicName, buildContributor, buildCorresp, buildFootnote, buildSupplementaryMaterial, } from '../../transformer/builders';
18
18
  import { parseJournalMeta } from './jats-journal-meta-parser';
19
19
  const warn = debug('manuscripts-transform');
20
20
  const XLINK_NAMESPACE = 'http://www.w3.org/1999/xlink';
@@ -62,31 +62,6 @@ export const jatsFrontParser = {
62
62
  }
63
63
  return parseJournalMeta(journalMeta);
64
64
  },
65
- parseKeywords(keywordGroupNodes) {
66
- var _a, _b;
67
- if (!keywordGroupNodes) {
68
- return { groups: [], keywords: [] };
69
- }
70
- let keywordPriority = 1;
71
- const keywordGroups = { groups: [], keywords: [] };
72
- for (const keywordGroupNode of keywordGroupNodes) {
73
- const manuscriptKeywordGroup = buildKeywordGroup({
74
- title: ((_a = keywordGroupNode.querySelector('title')) === null || _a === void 0 ? void 0 : _a.textContent) || undefined,
75
- label: ((_b = keywordGroupNode.querySelector('label')) === null || _b === void 0 ? void 0 : _b.textContent) || undefined,
76
- type: keywordGroupNode.getAttribute('kwd-group-type') || undefined,
77
- });
78
- keywordGroups.groups.push(manuscriptKeywordGroup);
79
- for (const keywordNode of keywordGroupNode.querySelectorAll('kwd')) {
80
- if (keywordNode.textContent) {
81
- const keyword = buildKeyword(keywordNode.textContent);
82
- keyword.priority = keywordPriority++;
83
- keyword.containedGroup = manuscriptKeywordGroup._id;
84
- keywordGroups.keywords.push(keyword);
85
- }
86
- }
87
- }
88
- return keywordGroups;
89
- },
90
65
  parseDates(historyNode) {
91
66
  if (!historyNode) {
92
67
  return undefined;
@@ -51,9 +51,6 @@ export const parseJATSFront = async (front) => {
51
51
  const manuscriptMeta = Object.assign({ title: title ? inlineContentsFromJATSTitle(title) : undefined, subtitle: subtitle ? inlineContentsFromJATSTitle(subtitle) : undefined, runningTitle: runningTitle
52
52
  ? inlineContentsFromJATSTitle(runningTitle)
53
53
  : undefined }, jatsFrontParser.parseCounts(articleMeta === null || articleMeta === void 0 ? void 0 : articleMeta.querySelector('counts')));
54
- const keywordGroupNodes = articleMeta === null || articleMeta === void 0 ? void 0 : articleMeta.querySelectorAll('kwd-group');
55
- const { keywords, groups: keywordGroups } = jatsFrontParser.parseKeywords(keywordGroupNodes);
56
- const manuscript_keywordIDs = keywords.length > 0 ? keywords.map((k) => k._id) : undefined;
57
54
  const { affiliations, affiliationIDs } = jatsFrontParser.parseAffiliationNodes([
58
55
  ...front.querySelectorAll('article-meta > contrib-group > aff'),
59
56
  ]);
@@ -70,14 +67,12 @@ export const parseJATSFront = async (front) => {
70
67
  const supplements = jatsFrontParser.parseSupplements([
71
68
  ...front.querySelectorAll('article-meta > supplementary-material'),
72
69
  ]);
73
- const manuscript = Object.assign(Object.assign(Object.assign(Object.assign({}, buildManuscript()), manuscriptMeta), { keywordIDs: manuscript_keywordIDs }), history);
70
+ const manuscript = Object.assign(Object.assign(Object.assign({}, buildManuscript()), manuscriptMeta), history);
74
71
  return {
75
72
  models: generateModelIDs([
76
73
  manuscript,
77
- ...keywords,
78
74
  ...affiliations,
79
75
  ...authors,
80
- ...keywordGroups,
81
76
  ...footnotes,
82
77
  ...correspondingList,
83
78
  journal,
@@ -116,6 +111,7 @@ export const parseJATSBody = (document, body, bibliographyItems, refModels, refe
116
111
  jatsBodyTransformations.moveCaptionsToEnd(body);
117
112
  jatsBodyTransformations.moveTableFooterToEnd(body);
118
113
  jatsBodyTransformations.moveBlockNodesFromParagraph(document, body, createElement);
114
+ jatsBodyTransformations.moveKeywordsToBody(document, body, createElement);
119
115
  const node = jatsBodyDOMParser.parse(body);
120
116
  if (!node.firstChild) {
121
117
  throw new Error('No content was parsed from the JATS article body');
@@ -391,9 +391,7 @@ export class JATSExporter {
391
391
  if (history.childElementCount) {
392
392
  articleMeta.appendChild(history);
393
393
  }
394
- if (manuscript.keywordIDs) {
395
- this.buildKeywords(articleMeta, manuscript.keywordIDs);
396
- }
394
+ this.buildKeywords(articleMeta);
397
395
  let countingElements = [];
398
396
  if (manuscript.genericCounts) {
399
397
  const elements = manuscript.genericCounts.map((el) => {
@@ -617,6 +615,7 @@ export class JATSExporter {
617
615
  bibliography_element: () => '',
618
616
  bibliography_item: () => '',
619
617
  comment_list: () => '',
618
+ keywords_group: () => '',
620
619
  bibliography_section: (node) => [
621
620
  'ref-list',
622
621
  { id: normalizeID(node.attrs.id) },
@@ -1522,10 +1521,8 @@ export class JATSExporter {
1522
1521
  return name;
1523
1522
  };
1524
1523
  }
1525
- buildKeywords(articleMeta, keywordIDs) {
1526
- const keywords = keywordIDs
1527
- .map((id) => this.modelMap.get(id))
1528
- .filter((model) => model && model.name);
1524
+ buildKeywords(articleMeta) {
1525
+ const keywords = [...this.modelMap.values()].filter((model) => model.objectType === ObjectTypes.Keyword);
1529
1526
  const keywordGroups = new Map();
1530
1527
  keywords.forEach((keyword) => {
1531
1528
  const containedGroup = keyword.containedGroup || '';
@@ -1559,6 +1556,7 @@ export class JATSExporter {
1559
1556
  kwd.textContent = keyword.name;
1560
1557
  kwdGroup.appendChild(kwd);
1561
1558
  }
1559
+ articleMeta.appendChild(kwdGroup);
1562
1560
  }
1563
1561
  }
1564
1562
  }
@@ -13,6 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import { isBibliographySectionNode } from '../schema/nodes/bibliography_section';
16
17
  import { isGraphicalAbstractSectionNode } from '../schema/nodes/graphical_abstract_section';
17
18
  export function* iterateChildren(node, recurse = false) {
18
19
  for (let i = 0; i < node.childCount; i++) {
@@ -44,6 +45,15 @@ export const isInGraphicalAbstractSection = ($pos) => {
44
45
  }
45
46
  return false;
46
47
  };
48
+ export const isInBibliographySection = ($pos) => {
49
+ for (let i = $pos.depth; i > 0; i--) {
50
+ const node = $pos.node(i);
51
+ if (isBibliographySectionNode(node)) {
52
+ return true;
53
+ }
54
+ }
55
+ return false;
56
+ };
47
57
  export const findParentNodeClosestToPos = ($pos, predicate) => {
48
58
  for (let i = $pos.depth; i > 0; i--) {
49
59
  const node = $pos.node(i);
@@ -42,6 +42,7 @@ import { inlineEquation } from './nodes/inline_equation';
42
42
  import { inlineFootnote } from './nodes/inline_footnote';
43
43
  import { keyword } from './nodes/keyword';
44
44
  import { keywordsElement } from './nodes/keywords_element';
45
+ import { keywordsGroup } from './nodes/keywords_group';
45
46
  import { keywordsSection } from './nodes/keywords_section';
46
47
  import { link } from './nodes/link';
47
48
  import { bulletList, listItem, orderedList } from './nodes/list';
@@ -156,6 +157,7 @@ export const schema = new Schema({
156
157
  keyword,
157
158
  keywords_element: keywordsElement,
158
159
  keywords_section: keywordsSection,
160
+ keywords_group: keywordsGroup,
159
161
  link,
160
162
  list_item: listItem,
161
163
  listing,
@@ -22,8 +22,7 @@ export const keyword = {
22
22
  dataTracked: { default: null },
23
23
  comments: { default: null },
24
24
  },
25
- inline: true,
26
- group: 'inline',
25
+ group: 'block',
27
26
  selectable: false,
28
27
  parseDOM: [
29
28
  {
@@ -15,11 +15,12 @@
15
15
  */
16
16
  export const keywordsElement = {
17
17
  atom: true,
18
- content: 'keyword*',
18
+ content: 'keywords_group*',
19
19
  attrs: {
20
20
  id: { default: '' },
21
21
  contents: { default: '' },
22
22
  paragraphStyle: { default: '' },
23
+ type: { default: '' },
23
24
  dataTracked: { default: null },
24
25
  },
25
26
  group: 'block element',
@@ -0,0 +1,44 @@
1
+ /*!
2
+ * © 2019 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export const keywordsGroup = {
17
+ content: 'keyword*',
18
+ attrs: {
19
+ id: { default: '' },
20
+ type: { default: '' },
21
+ dataTracked: { default: null },
22
+ },
23
+ group: 'block',
24
+ selectable: false,
25
+ parseDOM: [
26
+ {
27
+ tag: 'div.keywords',
28
+ },
29
+ ],
30
+ toDOM: (node) => {
31
+ const keywordsGroupNode = node;
32
+ return [
33
+ 'div',
34
+ {
35
+ id: keywordsGroupNode.attrs.id,
36
+ class: 'keywords',
37
+ spellcheck: 'false',
38
+ contenteditable: false,
39
+ },
40
+ 0,
41
+ ];
42
+ },
43
+ };
44
+ export const isKeywordsGroupNode = (node) => node.type === node.type.schema.nodes.keywords_group;
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  export const keywordsSection = {
17
- content: 'section_title_plain (keywords_element | placeholder_element)',
17
+ content: 'section_title (keywords_element | placeholder_element)',
18
18
  attrs: {
19
19
  id: { default: '' },
20
20
  dataTracked: { default: null },
@@ -265,11 +265,11 @@ export class Decoder {
265
265
  },
266
266
  [ObjectTypes.KeywordsElement]: (data) => {
267
267
  const model = data;
268
- const keywords = this.getKeywords();
268
+ const keywordGroups = this.getKeywordGroups();
269
269
  return schema.nodes.keywords_element.create({
270
270
  id: model._id,
271
271
  paragraphStyle: model.paragraphStyle,
272
- }, keywords);
272
+ }, keywordGroups);
273
273
  },
274
274
  [ObjectTypes.Keyword]: (data) => {
275
275
  const model = data;
@@ -416,9 +416,7 @@ export class Decoder {
416
416
  const elementNodes = elements
417
417
  .map(this.decode)
418
418
  .filter(isManuscriptNode);
419
- const sectionTitle = isKeywordsSection
420
- ? 'section_title_plain'
421
- : 'section_title';
419
+ const sectionTitle = 'section_title';
422
420
  const sectionTitleNode = model.title
423
421
  ? this.parseContents(model.title, 'h1', this.getComments(model), {
424
422
  topNode: schema.nodes[sectionTitle].create(),
@@ -562,19 +560,6 @@ export class Decoder {
562
560
  id: generateNodeID(schema.nodes.section),
563
561
  }));
564
562
  }
565
- const keywordsSection = getSections(this.modelMap).filter((section) => section.category === 'MPSectionCategory:keywords');
566
- const keywords = this.getKeywords();
567
- if (keywordsSection.length === 0 && keywords.length > 0) {
568
- const keywordsSection = schema.nodes.keywords_section.createAndFill({
569
- id: generateNodeID(schema.nodes.keywords_section),
570
- }, [
571
- schema.nodes.section_title_plain.create({}, schema.text('Keywords')),
572
- schema.nodes.keywords_element.create({
573
- id: generateNodeID(schema.nodes.keywords_element),
574
- }, keywords),
575
- ]);
576
- rootSectionNodes.unshift(keywordsSection);
577
- }
578
563
  const contents = rootSectionNodes;
579
564
  if (this.comments.size) {
580
565
  const comments = schema.nodes.comment_list.createAndFill({
@@ -622,17 +607,16 @@ export class Decoder {
622
607
  }
623
608
  return parser.parse(template.content.firstElementChild, options);
624
609
  };
625
- this.getKeywords = () => {
610
+ this.getKeywords = (id) => {
626
611
  const keywordsOfKind = [];
627
- for (const model of this.modelMap.values()) {
628
- const commentNodes = this.createCommentsNode(model);
629
- commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
612
+ const keywordsByGroup = [...this.modelMap.values()].filter((m) => m.objectType === ObjectTypes.Keyword &&
613
+ m.containedGroup === id);
614
+ for (const model of keywordsByGroup) {
630
615
  if (isKeyword(model)) {
631
616
  const keyword = this.parseContents(model.name || '', 'div', this.getComments(model), {
632
617
  topNode: schema.nodes.keyword.create({
633
618
  id: model._id,
634
619
  contents: model.name,
635
- comments: commentNodes.map((c) => c.attrs.id),
636
620
  }),
637
621
  });
638
622
  keywordsOfKind.push(keyword);
@@ -679,4 +663,26 @@ export class Decoder {
679
663
  this.modelMap = modelMap;
680
664
  this.allowMissingElements = allowMissingElements;
681
665
  }
666
+ getKeywordGroups() {
667
+ const kwdGroupNodes = [];
668
+ const kwdGroupsModels = [
669
+ ...this.modelMap.values(),
670
+ ].filter((model) => model.objectType === ObjectTypes.KeywordGroup);
671
+ if (kwdGroupsModels.length > 0) {
672
+ for (const kwdGroupModel of kwdGroupsModels) {
673
+ const keywords = this.getKeywords(kwdGroupModel._id);
674
+ const commentNodes = this.createCommentsNode(kwdGroupModel);
675
+ commentNodes.forEach((c) => this.comments.set(c.attrs.id, c));
676
+ const contents = [];
677
+ contents.push(...keywords);
678
+ const kwdGroupNode = schema.nodes.keywords_group.create({
679
+ id: kwdGroupModel._id,
680
+ type: kwdGroupModel.type,
681
+ comments: commentNodes.map((c) => c.attrs.id),
682
+ }, contents);
683
+ kwdGroupNodes.push(kwdGroupNode);
684
+ }
685
+ }
686
+ return kwdGroupNodes;
687
+ }
682
688
  }
@@ -393,18 +393,21 @@ const encoders = {
393
393
  SVGGlyphs: svgDefs(node.attrs.SVGRepresentation),
394
394
  }),
395
395
  keyword: (node, parent) => ({
396
- containerID: parent.attrs.id,
396
+ containedGroup: parent.attrs.id,
397
397
  name: keywordContents(node),
398
398
  }),
399
399
  keywords_element: (node) => ({
400
- contents: elementContents(node),
400
+ contents: '<div></div>',
401
401
  elementType: 'div',
402
402
  paragraphStyle: node.attrs.paragraphStyle || undefined,
403
403
  }),
404
+ keywords_group: (node) => ({
405
+ type: node.attrs.type,
406
+ }),
404
407
  keywords_section: (node, parent, path, priority) => ({
405
408
  category: buildSectionCategory(node),
406
409
  priority: priority.value++,
407
- title: inlineContentsOfNodeType(node, node.type.schema.nodes.section_title_plain),
410
+ title: inlineContentsOfNodeType(node, node.type.schema.nodes.section_title),
408
411
  path: path.concat([node.attrs.id]),
409
412
  elementIDs: childElements(node)
410
413
  .map((childNode) => childNode.attrs.id)
@@ -38,6 +38,7 @@ export const nodeTypesMap = new Map([
38
38
  [schema.nodes.keyword, ObjectTypes.Keyword],
39
39
  [schema.nodes.keywords_element, ObjectTypes.KeywordsElement],
40
40
  [schema.nodes.keywords_section, ObjectTypes.Section],
41
+ [schema.nodes.keywords_group, ObjectTypes.KeywordGroup],
41
42
  [schema.nodes.listing, ObjectTypes.Listing],
42
43
  [schema.nodes.listing_element, ObjectTypes.ListingElement],
43
44
  [schema.nodes.manuscript, ObjectTypes.Manuscript],
@@ -50,3 +50,4 @@ export const isManuscript = hasObjectType(ObjectTypes.Manuscript);
50
50
  export const isTable = hasObjectType(ObjectTypes.Table);
51
51
  export const isUserProfile = hasObjectType(ObjectTypes.UserProfile);
52
52
  export const isCommentAnnotation = hasObjectType(ObjectTypes.CommentAnnotation);
53
+ export const isKeyword = hasObjectType(ObjectTypes.Keyword);
@@ -28,4 +28,5 @@ export declare const jatsBodyTransformations: {
28
28
  moveTableFooterToEnd(body: Element): void;
29
29
  moveFloatsGroupToBody(doc: Document, body: Element, createElement: (tagName: string) => HTMLElement): void;
30
30
  moveBlockNodesFromParagraph(doc: Document, body: Element, createElement: (tagName: string) => HTMLElement): void;
31
+ moveKeywordsToBody(document: Document, body: Element, createElement: (tagName: string) => HTMLElement): void;
31
32
  };
@@ -13,8 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { Journal, Keyword, KeywordGroup } from '@manuscripts/json-schema';
17
- import { Build } from '../../transformer/builders';
16
+ import { Journal } from '@manuscripts/json-schema';
18
17
  export declare const jatsFrontParser: {
19
18
  parseCounts(counts: Element | null | undefined): {
20
19
  wordCount: number | undefined;
@@ -28,10 +27,6 @@ export declare const jatsFrontParser: {
28
27
  }[] | undefined;
29
28
  } | undefined;
30
29
  parseJournal(journalMeta: Element | null): Partial<Journal>;
31
- parseKeywords(keywordGroupNodes?: NodeListOf<Element> | null): {
32
- groups: Build<KeywordGroup>[];
33
- keywords: Build<Keyword>[];
34
- };
35
30
  parseDates(historyNode: Element | null): {
36
31
  acceptanceDate?: number | undefined;
37
32
  correctionDate?: number | undefined;
@@ -40,18 +35,18 @@ export declare const jatsFrontParser: {
40
35
  revisionReceiveDate?: number | undefined;
41
36
  receiveDate?: number | undefined;
42
37
  } | undefined;
43
- parseSupplements(supplementNodes: Element[] | null): Build<import("@manuscripts/json-schema").Supplement>[];
38
+ parseSupplements(supplementNodes: Element[] | null): import("../../transformer/builders").Build<import("@manuscripts/json-schema").Supplement>[];
44
39
  parseAffiliationNodes(affiliationNodes: Element[]): {
45
- affiliations: Build<import("@manuscripts/json-schema").Affiliation>[];
40
+ affiliations: import("../../transformer/builders").Build<import("@manuscripts/json-schema").Affiliation>[];
46
41
  affiliationIDs: Map<string, string>;
47
42
  };
48
43
  parseFootnoteNodes(footnoteNodes: Element[]): {
49
- footnotes: Build<import("@manuscripts/json-schema").Footnote>[];
44
+ footnotes: import("../../transformer/builders").Build<import("@manuscripts/json-schema").Footnote>[];
50
45
  footnoteIDs: Map<string, string>;
51
46
  };
52
47
  parseCorrespNodes(correspNodes: Element[]): {
53
- correspondingList: Build<import("@manuscripts/json-schema").Corresponding>[];
48
+ correspondingList: import("../../transformer/builders").Build<import("@manuscripts/json-schema").Corresponding>[];
54
49
  correspondingIDs: Map<string, string>;
55
50
  };
56
- parseAuthorNodes(authorNodes: Element[], affiliationIDs: Map<string, string>, footnoteIDs: Map<string, string>, correspondingIDs: Map<string, string>): Build<import("@manuscripts/json-schema").Contributor>[];
51
+ parseAuthorNodes(authorNodes: Element[], affiliationIDs: Map<string, string>, footnoteIDs: Map<string, string>, correspondingIDs: Map<string, string>): import("../../transformer/builders").Build<import("@manuscripts/json-schema").Contributor>[];
57
52
  };
@@ -18,6 +18,7 @@ import { ManuscriptEditorState, ManuscriptNode } from '../schema';
18
18
  export declare function iterateChildren(node: ManuscriptNode, recurse?: boolean): Iterable<ManuscriptNode>;
19
19
  export declare const findNodePositions: (state: ManuscriptEditorState, predicate: (node: ManuscriptNode) => boolean) => number[];
20
20
  export declare const isInGraphicalAbstractSection: ($pos: ResolvedPos) => boolean;
21
+ export declare const isInBibliographySection: ($pos: ResolvedPos) => boolean;
21
22
  export declare const findParentNodeClosestToPos: ($pos: ResolvedPos, predicate: (node: ProsemirrorNode) => boolean) => {
22
23
  pos: number;
23
24
  start: number;
@@ -0,0 +1,26 @@
1
+ /*!
2
+ * © 2019 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { NodeSpec } from 'prosemirror-model';
17
+ import { ManuscriptNode } from '../types';
18
+ interface Attrs {
19
+ id: string;
20
+ }
21
+ export interface KeywordsGroupNode extends ManuscriptNode {
22
+ attrs: Attrs;
23
+ }
24
+ export declare const keywordsGroup: NodeSpec;
25
+ export declare const isKeywordsGroupNode: (node: ManuscriptNode) => node is KeywordsGroupNode;
26
+ export {};
@@ -17,7 +17,7 @@ import { Fragment, Mark as ProsemirrorMark, MarkType, Node as ProsemirrorNode, N
17
17
  import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
18
18
  import { EditorView, NodeView } from 'prosemirror-view';
19
19
  export type Marks = 'bold' | 'code' | 'italic' | 'smallcaps' | 'strikethrough' | 'styled' | 'subscript' | 'superscript' | 'underline' | 'tracked_insert' | 'tracked_delete';
20
- export type Nodes = 'attribution' | 'bibliography_item' | 'bibliography_element' | 'bibliography_section' | 'blockquote_element' | 'bullet_list' | 'caption' | 'caption_title' | 'comment' | 'comment_list' | 'citation' | 'cross_reference' | 'doc' | 'equation' | 'equation_element' | 'figcaption' | 'figure' | 'graphical_abstract_section' | 'figure_element' | 'footnote' | 'footnotes_element' | 'footnotes_section' | 'hard_break' | 'highlight_marker' | 'inline_equation' | 'inline_footnote' | 'keyword' | 'keywords_element' | 'keywords_section' | 'link' | 'list_item' | 'listing' | 'listing_element' | 'manuscript' | 'missing_figure' | 'ordered_list' | 'paragraph' | 'placeholder' | 'placeholder_element' | 'pullquote_element' | 'section' | 'section_label' | 'section_title' | 'section_title_plain' | 'table' | 'table_body' | 'table_cell' | 'table_element' | 'table_row' | 'table_colgroup' | 'table_col' | 'text' | 'toc_element' | 'toc_section';
20
+ export type Nodes = 'attribution' | 'bibliography_item' | 'bibliography_element' | 'bibliography_section' | 'blockquote_element' | 'bullet_list' | 'caption' | 'caption_title' | 'comment' | 'comment_list' | 'citation' | 'cross_reference' | 'doc' | 'equation' | 'equation_element' | 'figcaption' | 'figure' | 'graphical_abstract_section' | 'figure_element' | 'footnote' | 'footnotes_element' | 'footnotes_section' | 'hard_break' | 'highlight_marker' | 'inline_equation' | 'inline_footnote' | 'keyword' | 'keywords_element' | 'keywords_group' | 'keywords_section' | 'link' | 'list_item' | 'listing' | 'listing_element' | 'manuscript' | 'missing_figure' | 'ordered_list' | 'paragraph' | 'placeholder' | 'placeholder_element' | 'pullquote_element' | 'section' | 'section_label' | 'section_title' | 'section_title_plain' | 'table' | 'table_body' | 'table_cell' | 'table_element' | 'table_row' | 'table_colgroup' | 'table_col' | 'text' | 'toc_element' | 'toc_section';
21
21
  export type ManuscriptSchema = Schema<Nodes, Marks>;
22
22
  export type ManuscriptEditorState = EditorState;
23
23
  export type ManuscriptEditorView = EditorView;
@@ -37,4 +37,5 @@ export declare class Decoder {
37
37
  private getKeywords;
38
38
  private getManuscriptID;
39
39
  private getFigcaption;
40
+ private getKeywordGroups;
40
41
  }
@@ -13,7 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { CommentAnnotation, Figure, Manuscript, Model, ObjectTypes, Table } from '@manuscripts/json-schema';
16
+ import { CommentAnnotation, Figure, Keyword, Manuscript, Model, ObjectTypes, Table } from '@manuscripts/json-schema';
17
17
  import { ManuscriptModel, UserProfileWithAvatar } from './models';
18
18
  export declare enum ExtraObjectTypes {
19
19
  PlaceholderElement = "MPPlaceholderElement"
@@ -27,3 +27,4 @@ export declare const isManuscript: (model: Model) => model is Manuscript;
27
27
  export declare const isTable: (model: Model) => model is Table;
28
28
  export declare const isUserProfile: (model: Model) => model is UserProfileWithAvatar;
29
29
  export declare const isCommentAnnotation: (model: Model) => model is CommentAnnotation;
30
+ export declare const isKeyword: (model: Model) => model is Keyword;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manuscripts/transform",
3
3
  "description": "ProseMirror transformer for Manuscripts applications",
4
- "version": "1.2.3",
4
+ "version": "1.2.4-LEAN-2066",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-transform",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",