@amermathsoc/texml-to-html 15.0.0 → 15.1.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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [15.1.0](https://github.com/AmerMathSoc/texml-to-html/compare/v15.0.0...v15.1.0) (2023-11-23)
6
+
7
+
8
+ ### Features
9
+
10
+ * **label.js:** preserve title and label ([5dae34f](https://github.com/AmerMathSoc/texml-to-html/commit/5dae34f1dfeaf8fa884303f7de2518b35d52c38b)), closes [#327](https://github.com/AmerMathSoc/texml-to-html/issues/327)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **package:** upgrade linkedom ([34ae8a7](https://github.com/AmerMathSoc/texml-to-html/commit/34ae8a7f48c2d6f2744e22c9f07aed4159a1f896))
16
+
5
17
  ## [15.0.0](https://github.com/AmerMathSoc/texml-to-html/compare/v14.0.1...v15.0.0) (2023-11-02)
6
18
 
7
19
 
package/README.md CHANGED
@@ -2,15 +2,22 @@
2
2
 
3
3
  Converting texml-generated JATS/BITS-like XML to HTML.
4
4
 
5
- The most basic use:
5
+ ## Getting started
6
+
7
+ ### Quick example
8
+
9
+ For a first test run, try [an example](./examples), e.g.,
10
+
11
+ * Install via npm: `$ npm i @amermathsoc/texml-to-html`
12
+ * Process a test file: `$ node node_modules/@amermathsoc/texml-to-html/examples/cli.js node_modules/@amermathsoc/texml-to-html/text/article.xml > htmlOutput.html`
13
+
14
+ ### Basic usage
6
15
 
7
16
  ```js
8
17
  import fs from 'fs';
9
18
  import path from 'path';
10
- import xml2html from './texml-to-html.js';
19
+ import xml2html from '@amermathsoc/texml-to-html';
11
20
 
12
21
  const article = xml2html(fs.readFileSync(path.resolve(process.argv[2])).toString()).window.document;
13
22
  console.log(article.toString());
14
23
  ```
15
-
16
- See also [the examples](./examples).
@@ -17,7 +17,7 @@
17
17
  import getParentLevel from '../helpers/getParentLevel.js';
18
18
 
19
19
  /**
20
- * label and title elements
20
+ * label and title elements. If both are present, expects label+title (not title+label)
21
21
  * @param {HTMLElement} htmlParentNode
22
22
  * @param {Element} xmlnode
23
23
  */
@@ -41,7 +41,8 @@ export default function (htmlParentNode, xmlnode) {
41
41
  this.passThrough(htmlParentNode, xmlnode);
42
42
  return;
43
43
  }
44
- // CASE fig, fig-group, table-wrap, table-wrap-group via caption
44
+ // CASE fig, fig-group, table-wrap, table-wrap-group via caption
45
+ // TODO: could we just return?
45
46
  if (
46
47
  xmlnode.parentNode.tagName === 'table-wrap' ||
47
48
  xmlnode.parentNode.tagName === 'table-wrap-group' ||
@@ -54,33 +55,33 @@ export default function (htmlParentNode, xmlnode) {
54
55
  // CASE label followed by a title -- we skip (and pull in the label later on when processing title)
55
56
  if (xmlnode.nextElementSibling?.tagName === 'title') return;
56
57
  // CASE empty label
57
- if (xmlnode.tagName === 'label' && xmlnode.innerHTML.trim() === '') return;
58
+ if (xmlnode.tagName === 'label' && xmlnode.innerHTML.trim() === '') return; // TODO: I think texml is better about this now (only found in mcl11)
58
59
 
59
60
  // complex cases
60
61
 
61
- // Decide output markup (h* or figcaption; wrapping header for subtitles)
62
+ // Decide container (h* or figcaption; wrapping header for subtitles)
62
63
  const isStatement = xmlnode.parentNode.tagName === 'statement';
63
64
  const isDispFormulaGroup = xmlnode.parentNode.tagName === 'disp-formula-group';
64
65
  const level = getParentLevel(htmlParentNode) + 1;
65
- const heading = (isStatement || isDispFormulaGroup) ? this.createNode('figcaption', '') : this.createNode(`h${level}`, '');
66
- htmlParentNode.appendChild(heading);
66
+ const container = (isStatement || isDispFormulaGroup) ? this.createNode('figcaption', '') : this.createNode(`h${level}`, '');
67
+ htmlParentNode.appendChild(container);
67
68
 
68
- // subtitle handling (assumes heading is not figcaption)
69
+ // subtitle handling (assumes container is not figcaption)
69
70
  const subtitleSibling = xmlnode.parentNode.querySelector(':scope>subtitle');
70
71
  if (subtitleSibling && subtitleSibling.innerHTML.trim() !== '') {
71
72
  // wrap heading in header
72
73
  const header = this.createNode('header');
73
74
  htmlParentNode.appendChild(header);
74
- header.appendChild(heading);
75
+ header.appendChild(container);
75
76
  // recurse subtitle
76
77
  this.recurseTheDom(header, subtitleSibling);
77
78
  }
78
79
 
79
80
  // Pull in label (if title+label and we're processing title)
80
81
  const previousSibling = xmlnode.previousElementSibling;
81
- if (previousSibling?.tagName === 'label' && previousSibling.innerHTML.trim() !== '') {
82
+ if (previousSibling?.tagName === 'label' && previousSibling.innerHTML.trim() !== '') { //TODO: empty label check unnecessary? cf. earlier TODO re empty label
82
83
  const labelSpan = this.createNode('span', '', { 'data-ams-doc': 'label' });
83
- heading.appendChild(labelSpan);
84
+ container.appendChild(labelSpan);
84
85
  this.passThrough(labelSpan, previousSibling);
85
86
  const labelSeparatorString = isStatement ? ' ' : '. ';
86
87
  labelSpan.insertAdjacentText('beforeend', labelSeparatorString);
@@ -90,12 +91,14 @@ export default function (htmlParentNode, xmlnode) {
90
91
  const altTitle = xmlnode.parentNode.querySelector(':scope>alt-title');
91
92
  const hasAltTitle = altTitle && altTitle.innerHTML !== xmlnode.innerHTML;
92
93
  if (this.isBook && hasAltTitle) {
93
- heading.setAttribute('data-ams-doc-alttitle', heading.textContent + altTitle.innerHTML); // NOTE assumes previousSibling handled first
94
+ container.setAttribute('data-ams-doc-alttitle', container.textContent + altTitle.innerHTML); // NOTE assumes previousSibling handled first
94
95
  }
95
96
 
96
- // recurse main node
97
- this.passThrough(heading, xmlnode);
97
+ // add main node and recurse
98
+ const actualSpan = this.createNode('span', '', { 'data-ams-doc': xmlnode.tagName });
99
+ container.appendChild(actualSpan);
100
+ this.passThrough(actualSpan, xmlnode);
98
101
 
99
- // faking TeX's punctutation-at-end logic
100
- if (isStatement) heading.insertAdjacentText('beforeend', heading.textContent.endsWith('.') ? ' ' : '. ');
102
+ // faking TeX's punctutation-at-end logic // TODO: should we move punctuation out of the title/label?
103
+ if (isStatement) container.insertAdjacentText('beforeend', container.textContent.endsWith('.') ? ' ' : '. ');
101
104
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amermathsoc/texml-to-html",
3
- "version": "15.0.0",
3
+ "version": "15.1.0",
4
4
  "type": "module",
5
5
  "description": "A NodeJS library for converting AMS-style JATS XML to HTML",
6
6
  "scripts": {
@@ -29,11 +29,11 @@
29
29
  },
30
30
  "homepage": "https://github.com/AmerMathSoc/texml-to-html#readme",
31
31
  "devDependencies": {
32
- "c8": "8.0.0",
33
- "standard-version": "9.3.2",
34
- "tape": "5.5.0"
32
+ "c8": "8.0.1",
33
+ "standard-version": "9.5.0",
34
+ "tape": "5.7.2"
35
35
  },
36
36
  "dependencies": {
37
- "linkedom": "0.14.20"
37
+ "linkedom": "0.16.4"
38
38
  }
39
39
  }
@@ -25,7 +25,7 @@ tape('Template: book-back/ref-list ', async function(t) {
25
25
  const bibl = document.querySelector('section#reflist[role="doc-bibliography"]');
26
26
  t.ok(bibl, 'ref-list becomes section with id, role, doc-level');
27
27
  const title = bibl.querySelector('h1');
28
- t.equal(title.outerHTML, '<h1>title</h1>', 'ref-list title as heading');
28
+ t.equal(title.outerHTML, '<h1><span data-ams-doc="title">title</span></h1>', 'ref-list title as heading');
29
29
  const list = bibl.querySelector('dl');
30
30
  t.ok(list, 'dl created for ref items');
31
31
  t.ok(document.querySelector('section#reflistchapter [role="doc-bibliography"] h2'), 'Chapter-level ref-list title gets h2')
@@ -23,5 +23,5 @@ tape('disp-formula-group', async function(t) {
23
23
  const groupChild = document.querySelector('#equations figure[data-ams-doc="statement"]#disp-formula-group');
24
24
  t.ok(groupChild, 'disp-formula-group as figure with data-ams-doc="statement" with id')
25
25
  t.equal(groupChild.firstElementChild.tagName, 'FIGCAPTION', 'disp-formula-group label');
26
- t.equal(groupChild.firstElementChild.innerHTML, 'Formula Group', 'disp-formula-group passes label along');
26
+ t.equal(groupChild.firstElementChild.innerHTML, '<span data-ams-doc="label">Formula Group</span>', 'disp-formula-group passes label along');
27
27
  });
@@ -29,14 +29,14 @@ tape('sec, app, front-matter-part, dedication, title, label', async function(t)
29
29
  t.ok(document.querySelector('#intro2[role="doc-introduction"]'), 'sec with matching title text to role doc-introduction');
30
30
  t.ok(document.querySelector('#ded[role="doc-dedication"]'), 'dedication to role doc-dedication');
31
31
 
32
- t.equal(document.querySelector('#subsec h3').innerHTML, 'Subsection', 'subsection without wrapping section gets correct level');
32
+ t.equal(document.querySelector('#subsec h3').innerHTML, '<span data-ams-doc="label">Subsection</span>', 'subsection without wrapping section gets correct level');
33
33
 
34
- t.equal(document.querySelector('#seclabeltitle h2').innerHTML, '<span data-ams-doc="label">Label. </span>Title', 'sec with label+title: heading level and content');
35
- t.equal(document.querySelector('#sectitle h2').innerHTML, 'Title', 'sec with title: heading level and content');
36
- t.equal(document.querySelector('#seclabel h2').innerHTML, 'Label', 'sec with label: heading level and content');
37
- t.equal(document.querySelector('#applabeltitle h2').innerHTML, '<span data-ams-doc="label">Label. </span>Title', 'app with label+title: heading level and content');
38
- t.equal(document.querySelector('#apptitle h2').innerHTML, 'Title', 'app with title: heading level and content');
39
- t.equal(document.querySelector('#applabel h2').innerHTML, 'Label', 'app with label: heading level and content');
34
+ t.equal(document.querySelector('#seclabeltitle h2').innerHTML, '<span data-ams-doc="label">Label. </span><span data-ams-doc="title">Title</span>', 'sec with label+title: heading level and content');
35
+ t.equal(document.querySelector('#sectitle h2').innerHTML, '<span data-ams-doc="title">Title</span>', 'sec with title: heading level and content');
36
+ t.equal(document.querySelector('#seclabel h2').innerHTML, '<span data-ams-doc="label">Label</span>', 'sec with label: heading level and content');
37
+ t.equal(document.querySelector('#applabeltitle h2').innerHTML, '<span data-ams-doc="label">Label. </span><span data-ams-doc="title">Title</span>', 'app with label+title: heading level and content');
38
+ t.equal(document.querySelector('#apptitle h2').innerHTML, '<span data-ams-doc="title">Title</span>', 'app with title: heading level and content');
39
+ t.equal(document.querySelector('#applabel h2').innerHTML, '<span data-ams-doc="label">Label</span>', 'app with label: heading level and content');
40
40
 
41
41
  t.ok(document.querySelector('#sectitle header p[data-ams-doc="subtitle"]'), 'sec with title: subtitle to p with data-ams-doc');
42
42
  t.ok(document.querySelector('#seclabel header p[data-ams-doc="subtitle"]'), 'sec with title: subtitle to p with data-ams-doc');
@@ -52,15 +52,15 @@ tape('sec, app, front-matter-part, dedication, title, label', async function(t)
52
52
  const document2 = book;
53
53
  t.ok(document2.querySelector('#chapter[role="doc-chapter"]'), 'sec with specific-use chapter to role doc-chapter'); // NOTE so far, chapters only occur in books but the xslt doesn't check for it
54
54
 
55
- t.equal(document2.querySelector('#seclabeltitle h2').innerHTML, '<span data-ams-doc="label">Label. </span>Title', 'sec with label+title: heading level and content');
56
- t.equal(document2.querySelector('#sectitle h2').innerHTML, 'Title', 'sec with title: heading level and content');
57
- t.equal(document2.querySelector('#seclabel h2').innerHTML, 'Label', 'sec with label: heading level and content');
58
- t.equal(document2.querySelector('#fmlabeltitle h1').innerHTML, '<span data-ams-doc="label">Label. </span>Title', 'front-matter-part with label+title: heading level and content');
59
- t.equal(document2.querySelector('#fmtitle h1').innerHTML, 'Title', 'front-matter-part with title: heading level and content');
60
- t.equal(document2.querySelector('#fmlabel h1').innerHTML, 'Label', 'front-matter-part with label: heading level and content');
61
- t.equal(document2.querySelector('#applabeltitle h1').innerHTML, '<span data-ams-doc="label">Label. </span>Title', 'app with label+title: heading level and content');
62
- t.equal(document2.querySelector('#apptitle h1').innerHTML, 'Title', 'app with title: heading level and content');
63
- t.equal(document2.querySelector('#applabel h1').innerHTML, 'Label', 'app with label: heading level and content');
55
+ t.equal(document2.querySelector('#seclabeltitle h2').innerHTML, '<span data-ams-doc="label">Label. </span><span data-ams-doc="title">Title</span>', 'sec with label+title: heading level and content');
56
+ t.equal(document2.querySelector('#sectitle h2').innerHTML, '<span data-ams-doc="title">Title</span>', 'sec with title: heading level and content');
57
+ t.equal(document2.querySelector('#seclabel h2').innerHTML, '<span data-ams-doc="label">Label</span>', 'sec with label: heading level and content');
58
+ t.equal(document2.querySelector('#fmlabeltitle h1').innerHTML, '<span data-ams-doc="label">Label. </span><span data-ams-doc="title">Title</span>', 'front-matter-part with label+title: heading level and content');
59
+ t.equal(document2.querySelector('#fmtitle h1').innerHTML, '<span data-ams-doc="title">Title</span>', 'front-matter-part with title: heading level and content');
60
+ t.equal(document2.querySelector('#fmlabel h1').innerHTML, '<span data-ams-doc="label">Label</span>', 'front-matter-part with label: heading level and content');
61
+ t.equal(document2.querySelector('#applabeltitle h1').innerHTML, '<span data-ams-doc="label">Label. </span><span data-ams-doc="title">Title</span>', 'app with label+title: heading level and content');
62
+ t.equal(document2.querySelector('#apptitle h1').innerHTML, '<span data-ams-doc="title">Title</span>', 'app with title: heading level and content');
63
+ t.equal(document2.querySelector('#applabel h1').innerHTML, '<span data-ams-doc="label">Label</span>', 'app with label: heading level and content');
64
64
 
65
65
  t.equal(document2.querySelector('#part').getAttribute('role'), 'doc-part', 'part role');
66
66
  t.ok(document2.querySelector('#inparttitle h2'), 'sec with title in chapter in part: heading level reduced');
@@ -25,7 +25,7 @@ tape('Template: sec-meta => contrib-group, author-comment, abstract/title', asyn
25
25
  const secMeta = section.querySelector('section[data-ams-doc="sec-meta"]');
26
26
  t.equal(secMeta.getAttribute('data-ams-contributors'), '{"null":[]}', 'book sec-meta');
27
27
  t.ok(section.querySelector('h1'), 'title becomes h1');
28
- t.equal(section.querySelector('h1').innerHTML, 'Title', 'title content in heading');
28
+ t.equal(section.querySelector('h1').innerHTML, '<span data-ams-doc="title">Title</span>', 'title content in heading');
29
29
  t.ok(section.querySelector('[role="doc-abstract"] h2'), 'abstract title becomes h2');
30
30
 
31
31
  const document2 = article;
@@ -29,7 +29,7 @@ tape('Template: statement, label, title', async function(t) {
29
29
  t.ok(statement1Heading, 'statement heading level in article');
30
30
  t.equal(
31
31
  statement1Heading.innerHTML,
32
- '<span data-ams-doc="label">Label 1 </span>Title 1. ',
32
+ '<span data-ams-doc="label">Label 1 </span><span data-ams-doc="title">Title 1</span>. ',
33
33
  'statement with label+title creates space before and period after title'
34
34
  );
35
35
  const statement2 = document.querySelector(
@@ -38,7 +38,7 @@ tape('Template: statement, label, title', async function(t) {
38
38
  t.ok(statement2, 'statement 2');
39
39
  t.equal(
40
40
  statement2.querySelector('figcaption').innerHTML,
41
- 'Label 2. ',
41
+ '<span data-ams-doc="label">Label 2</span>. ',
42
42
  'statement with label creates period after'
43
43
  );
44
44
  const statement3 = document.querySelector(
@@ -47,7 +47,7 @@ tape('Template: statement, label, title', async function(t) {
47
47
  t.ok(statement3, 'statement 3');
48
48
  t.equal(
49
49
  statement3.querySelector('figcaption').innerHTML,
50
- 'Title 3. ',
50
+ '<span data-ams-doc="title">Title 3</span>. ',
51
51
  'statement with title creates period after'
52
52
  );
53
53
  const statementTitlePeriod = document.querySelector(
@@ -55,7 +55,7 @@ tape('Template: statement, label, title', async function(t) {
55
55
  );
56
56
  t.equal(
57
57
  statementTitlePeriod.querySelector('figcaption').innerHTML,
58
- 'Title 4. ',
58
+ '<span data-ams-doc="title">Title 4.</span> ',
59
59
  'statement with title with period does not have extra period after'
60
60
  );
61
61
  const statement4 = document.querySelector(
@@ -64,7 +64,7 @@ tape('Template: statement, label, title', async function(t) {
64
64
  t.ok(statement4, 'statement 4');
65
65
  t.equal(
66
66
  statement4.querySelector('figcaption').innerHTML,
67
- 'Proof. ',
67
+ '<span data-ams-doc="title">Proof</span>. ',
68
68
  'title in proof statement now also gets extra period'
69
69
  );
70
70
  const statement5 = document.querySelector(
@@ -73,7 +73,7 @@ tape('Template: statement, label, title', async function(t) {
73
73
  t.ok(statement5, 'statement 5');
74
74
  t.equal(
75
75
  statement5.querySelector('figcaption').innerHTML,
76
- '<span data-ams-doc="label">Label 5 </span>Proof. ',
76
+ '<span data-ams-doc="label">Label 5 </span><span data-ams-doc="title">Proof</span>. ',
77
77
  'proof statement with label+title'
78
78
  );
79
79
  t.equal(
@@ -22,5 +22,5 @@ tape('Empty Labels should be stripped', async function(t) {
22
22
  t.plan(2);
23
23
  const document = article;
24
24
  t.equal(document.querySelector('#emptyLabel').innerHTML.trim(), '', 'Statement with no title and empty label');
25
- t.equal(document.querySelector('#titleEmptyLabel').innerHTML.trim(), '<figcaption>Title. </figcaption>', 'Statement with no title and empty label');
25
+ t.equal(document.querySelector('#titleEmptyLabel').innerHTML.trim(), '<figcaption><span data-ams-doc="title">Title</span>. </figcaption>', 'Statement with no title and empty label');
26
26
  });