@amermathsoc/texml-to-html 15.1.0 → 15.2.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,20 @@
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.2.0](https://github.com/AmerMathSoc/texml-to-html/compare/v15.1.1...v15.2.0) (2024-03-29)
6
+
7
+
8
+ ### Features
9
+
10
+ * **formula.js:** mapAttributes() but from tex-math ([3e1f603](https://github.com/AmerMathSoc/texml-to-html/commit/3e1f603032d278fccc138bd20c2eda746a8f32e2)), closes [#441](https://github.com/AmerMathSoc/texml-to-html/issues/441)
11
+
12
+ ### [15.1.1](https://github.com/AmerMathSoc/texml-to-html/compare/v15.1.0...v15.1.1) (2023-12-13)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **mixed-citation.js:** fix HTML escaping ([4df1462](https://github.com/AmerMathSoc/texml-to-html/commit/4df14624280786ad870c4e0c2940275f6054eae8)), closes [#438](https://github.com/AmerMathSoc/texml-to-html/issues/438)
18
+
5
19
  ## [15.1.0](https://github.com/AmerMathSoc/texml-to-html/compare/v15.0.0...v15.1.0) (2023-11-23)
6
20
 
7
21
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # texml-to-html
2
2
 
3
- Converting texml-generated JATS/BITS-like XML to HTML.
3
+ Converting [texml](https://github.com/AmerMathSoc/texml)-generated JATS/BITS-like XML to HTML.
4
4
 
5
5
  ## Getting started
6
6
 
@@ -8,16 +8,365 @@ Converting texml-generated JATS/BITS-like XML to HTML.
8
8
 
9
9
  For a first test run, try [an example](./examples), e.g.,
10
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`
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
13
 
14
14
  ### Basic usage
15
15
 
16
16
  ```js
17
- import fs from 'fs';
18
- import path from 'path';
19
- import xml2html from '@amermathsoc/texml-to-html';
17
+ import fs from "fs";
18
+ import path from "path";
19
+ import xml2html from "@amermathsoc/texml-to-html";
20
20
 
21
- const article = xml2html(fs.readFileSync(path.resolve(process.argv[2])).toString()).window.document;
21
+ const article = xml2html(
22
+ fs.readFileSync(path.resolve(process.argv[2])).toString()
23
+ ).window.document;
22
24
  console.log(article.toString());
23
25
  ```
26
+
27
+ ## Overview
28
+
29
+ Our general strategy for elements and attributes is to follow allow-lists and discard everything else.
30
+
31
+ We primarily we are recursing through the input DOM, building the output DOM. Rarely, we deviate from this approach for practical reasons (e.g., for metadata extraction).
32
+
33
+ ### preserved elements
34
+
35
+ Some elements in texml's XML output have the same name (and purpose) as in HTML. We preserve them in the output:
36
+
37
+ - hr
38
+ - p
39
+ - pre
40
+ - sup
41
+ - sub
42
+ - table
43
+ - tbody
44
+ - thead
45
+ - th
46
+ - tr
47
+ - td
48
+
49
+ ### preseved attributes
50
+
51
+ Some attributes in texml's XML output have the same name (and purpose) in HTML.
52
+
53
+ As per our general strategy, we only preserve _some_ attributes on _some_ elements.
54
+
55
+ - attribute names that are preserved
56
+ - class
57
+ - id
58
+ - rowspan
59
+ - colspan
60
+ - hidden
61
+ - element names where those attributes are preserved
62
+ - all preserved elements (cf. above)
63
+ - abstract
64
+ - app
65
+ - boxed-text
66
+ - def-list
67
+ - def
68
+ - fig
69
+ - fn
70
+ - notes
71
+ - p
72
+ - preface
73
+ - ref-list
74
+ - sec-heading
75
+ - statement
76
+ - target
77
+ - term
78
+
79
+ ### `data-*` attributes
80
+
81
+ Beyond HTML element and attributes, texml-to-html stores data in custom `data-*` attributes. The following lists should help as a guide to understand the this structural information.
82
+
83
+ :warning: This list can easily fall out of date. It should be automated.
84
+
85
+ #### `data-*` attribute values and origin
86
+
87
+ - data-ams-doc
88
+ - titlepage
89
+ - subtitle
90
+ - article
91
+ - copyright-page
92
+ - copyright
93
+ - amsref
94
+ - paragraph
95
+ - subtitle
96
+ - sec-meta
97
+ - secheading
98
+ - {@specific-use} [sec, ack, front-matter-part, dedication, custom-meta]
99
+ - statement
100
+ - graphic
101
+ - inline-graphic
102
+ - math inline
103
+ - math block
104
+ - math tex [added in ams-html output]
105
+ - amsref
106
+ - stringname
107
+ - verse-group
108
+ - label
109
+ - title
110
+ - notes [on section, from XML notes element, cf. AmerMathSoc/texml-to-html#329]
111
+ - biblioentry [formerly role="doc-biblioentry" (deprecated)]
112
+ - tags - container for (duplicated) equation tags
113
+ - container is inside data-ams-doc="math block" elements
114
+ - ams-html uses these to generate the math panel DTs for equations
115
+ - data-ams-doc-contrib
116
+ - {@content-type} [expected: "authors", "editors", "translators", "contributors"]
117
+ - {@contrib-type} [expected: "author", "editor", "translator", "contributor"]
118
+ - {@contrib-type} name
119
+ - data-ams-doc-contrib-comment
120
+ - STRING
121
+ - data-ams-style
122
+ - {styled-content@style-type} [expected: sans-serif]
123
+ - roman
124
+ - sc
125
+ - monospace
126
+ - underline
127
+ - {disp-quote@specific-use}
128
+ - (inline-)graphic{@specific-use}
129
+ - {@style} [expected: theorem styles]
130
+ - boxed (from boxed-text)
131
+ - data-ams-ref
132
+ - {@ref-type} [expected: bibr, fn, disp-formula, sec, fig, table, algorithm, list, statement]
133
+ - notrid
134
+ - fn-return [added in ams-html output]
135
+ - data-ams-doc-level
136
+ - [0-9]
137
+ - data-ams-content-type
138
+ - {@content-type }
139
+ - { @notes-type } (for notes elements, cf. AmerMathSoc/texml-to-html#329)
140
+ - data-ams-specific-use
141
+ - {@specific-use}
142
+ - data-ams-qed-box
143
+ - BOOLEAN
144
+ - data-ams-position
145
+ - anchor
146
+ - data-ams-width
147
+ - graphic | inline-graphic @width
148
+ - data-ams-height
149
+ - graphic | inline-graphic @height
150
+ - data-ams-doc-alttitle
151
+ - alt-title (book only, for sectioning content only)
152
+
153
+ ##### downstream `data-*` attributes
154
+
155
+ While the vast majority of data attributes originate in texml-to-html, we have a few cases where downstream tooling introduces custom attributes.
156
+ We list the attribute names, the related tools and purpose:
157
+
158
+ - [deprecated] data-eqn-tag-#
159
+ - superceded by data-ams-tags (cf. earlier)
160
+ - ams-eqn-store used to use these numbered attribute names to store extracted equation tags for downstream use (e.g., ams-html math panel)
161
+ - [deprecated] data-ams-tags
162
+ - this attribute contained a stringified array of equation tag strings
163
+ - while initially generated by texml-to-html, ams-eqn-store overwrote them with rendered output if TeX was present in the strings.
164
+
165
+ <!-- NOTE move to another location if it grows enough -->
166
+
167
+ ##### `role` values
168
+
169
+ The following [ARIA-DPUB](https://w3c.github.io/dpub-aria/) role attribute values are used:
170
+
171
+ - doc-preface
172
+ - doc-bibliography
173
+ - doc-appendix
174
+ - doc-dedication
175
+ - doc-noteref
176
+ - doc-biblioref
177
+ - doc-footnote
178
+ - doc-chapter
179
+ - doc-abstract
180
+ - doc-toc
181
+ - doc-footnote
182
+ - doc-appendix
183
+
184
+ #### texml XML to `data-*` mappings
185
+
186
+ The following provide a list from the reverse point of view.
187
+
188
+ - book
189
+ - book-id[@book-id-type = 'publ_key'] => data-ams-doc="series"
190
+ - book-meta => data-ams-doc="titlepage"
191
+ - contains JSON blob with book-meta, collection-meta; cf. metadata section in this document
192
+ - book-back//ref-list => data-ams-doc-level="1
193
+ - book//sec/alt-title => data-ams-doc-alttitle
194
+ - article => data-ams-doc="article" [this is a somewhat messy part as we pick and choose, sort of reversely; much of it repeated on copyright page without data attributes]
195
+ - front => data-ams-doc="frontmatter" (some info in head element)
196
+ - contains JSON blob with (most of) journal-meta, article-meta; cf. metadata section in this document.
197
+ - the folllowing descendants create additional HTML (since they may contain tex-math):
198
+ - article-meta>title-group => passthrough
199
+ - notes => data-ams-doc=notes with data-ams-content-type (for @notes-type)
200
+ - abstract => via role
201
+ - kwd-group's => data-ams-doc with @vocab or "keywords"
202
+ - funding-group => data-ams-doc=funding-group
203
+ - styled-content => data-ams-style="{@style-type}"
204
+ - roman => data-ams-style="roman"
205
+ - or `\textrm{...}` (if inside `text`)
206
+ - sc => data-ams-style="sc"
207
+ - or `$\mathsc{...}$` (if inside text)
208
+ - monospace => data-ams-style="monospace"
209
+ - or `\texttt{...}` (if inside text)
210
+ - underline => data-ams-style="underline"
211
+ - disp-quote => data-ams-style="{@specific-use}"
212
+ - xref => data-ams-ref="{@ref-type}"
213
+ - xref[not(@rid)] => data-ams-ref="notrid" [but note on ref-type=fn, bibr]
214
+ - or `\xhref[@ref-type]{#@rid}{...}` (if inside disp/inline-formula)
215
+ - p//p => data-ams-doc="paragraph"
216
+ - fn/label => data-ams-doc="label"
217
+ - statement/secheading/title | statement/secheading/label => data-ams-doc="secheading"
218
+ - mixed-citation => data-ams-doc="biblioentry" [formerly role="doc-biblioentry" (deprecated)]
219
+ - sec | ack | front-matter-part | front-matter/dedication => data-ams-doc="{@specific-use}"
220
+ - [e.g., sec/title | app/title | sec/label | app/label | front-matter-part/title]
221
+ - subtitle => data-ams-doc="subtitle"
222
+ - sec-meta => data-ams-doc="sec-meta" + "data-ams-contributors" (json blob, cf. metadata section in this document) + "data-ams-byline" (pre-generated byline)
223
+ - label => data-ams-doc="label"
224
+ - title => data-ams-doc="title"
225
+ - abstract => role="doc-abstract">
226
+ - statement => data-ams-doc="statement"
227
+ - graphic | inline-graphic => data-ams-doc="{name()}" data-ams-style="{@specific-use}"
228
+ - graphic | inline-graphic @width => data-ams-width
229
+ - graphic | inline-graphic @height => data-ams-height.
230
+ - inline-formula => data-ams-doc="math inline" OR `$...$`
231
+ - within disp/inline-formula, inline-formula may appear inside text. In that case, we have to create TeX strings for MathJax to process, wrapped in `$`
232
+ - disp-formula => data-ams-doc="math block"
233
+ - disp-formula-group => data-ams-doc="statement" data-ams-content-type="disp-formula-group"
234
+ - raw-citation => data-ams-doc="amsref"
235
+ - string-name => data-ams-doc="stringname"
236
+ - verse-group => data-ams-doc="verse-group"
237
+ - boxed-text => div@data-ams-style="boxed"
238
+ - notes => section with data-ams-doc="notes"
239
+ - @notes-type => @data-ams-content-type (and role=dedication for dedications)
240
+ - use cases: dedication (articles), article and section notes (NOTI only), drm notice & epub note (books)
241
+ - attributes
242
+ - @disp-level => data-ams-doc-level [data-ams-doc-level is also added to some elements that lack disp-level]
243
+ - @content-type => data-ams-content-type
244
+ - @style => data-ams-style
245
+ - @specific-use => data-ams-specific-use [sometimes mapped to other attributes, e.g., style]
246
+ - @has-qed-box => data-ams-qed-box
247
+ - @position => data-ams-position
248
+ - @text-color, @background-color, @border-color => data-ams-style-color (as combined CSS declarations)
249
+ - currently appears on: boxed-text, styled-content
250
+
251
+ ### metadata handling
252
+
253
+ Publication metadata (both journal/series and article/book metadata) is primarily stored in a JSON blob in a `script` tag in the frontmatter (for articles) and titlepage (for books) sections respectively.
254
+
255
+ Section metadata (contributor metadata and pre-generated byline) is stored similarly as a json blob inside the `data-ams-contributors` attribute.
256
+
257
+ The relevant components in `texml-to-html` (i.e., `article-metadata-json.js`, `book-meta-json.js`, `sec-meta.js`) should provide a (hopefully easy enough) overview how the XML metadata is mapped and stored. The snapshots in the `test` folder should also be helpful, alongside any (example) article's JSON blob.
258
+
259
+ The following are commonly found metadata items:
260
+
261
+ For journal articles:
262
+
263
+ - `<article-meta>`
264
+ - `<contrib-group>` etc (cf. "contributors" below)
265
+ - `<self-uri>`
266
+ - `<title-group>` etc
267
+ - `<pub-date>` etc
268
+ - `<notes>`
269
+ - `<kwd-group>`
270
+ - MSC (using `<compound-kwd>` etc)
271
+ - article keywords (using `<kwd>`)
272
+ - `<related-article>` etc (correction/erratum forward/backward)
273
+ - `<custom-meta-group>` (communicated by, NOTI categories, NOTI titlepic)
274
+ - `<funding-group>` / `<funding-statement>`
275
+ - `<permissions>`
276
+ - `<copyright-statement>`
277
+ - `<history>` etc
278
+ - `<article-id>` etc
279
+ - `<abstract>` etc
280
+ - `<volume>`, `<issue>`
281
+ - `<journal-meta>`
282
+ - `<journal-id>`
283
+ - `<journal-title-group>` etc
284
+ - `<issn>` etc
285
+ - `<publisher>`
286
+ - `<self-uri>` etc
287
+
288
+ For books:
289
+
290
+ - `<collection-meta>`
291
+ - `<publisher>` etc
292
+ - `<title-group>`
293
+ - `<volume-in-collection>`
294
+ - `<volume-number>`
295
+ - `<custom-meta-group>` (for subseries)
296
+ `<book-meta>`
297
+ - `<book-id>` etc
298
+ - `<book-title-group>` etc
299
+ - `<book-volume-number>`
300
+ - `<publisher>`
301
+ - `<contrib-group>` etc (cf. "contributors" below)
302
+ - `<self-uri>`
303
+ - `<title-group>` etc
304
+ - `<pub-date>` etc
305
+ - `<notes>`
306
+ - `<issn>`
307
+ - `<isbn>`
308
+ - `<permissions>`
309
+ - `<front-matter>`
310
+ - `<toc>` etc
311
+ - `<notes>`
312
+ - `<preface>`
313
+ - `<front-matter-part>`
314
+
315
+ ### math mode
316
+
317
+ **Note.** There is some overlap with other sections of this document. Ensure that updates are consistent across the document.
318
+
319
+ For math mode, texml creates MathJax-optimized TeX strings that may contain XML markup; for content not supported by MathJax it falls back to SVG creation. This mix requires extra processing.
320
+
321
+ #### xref
322
+
323
+ Math mode output may contain xref elements. This gets turned into something like `\xhref[@ref-type]{#@rid}{...}`; the custom `xhref` MathJax macro works in both (MathJax's) text and math mode.
324
+
325
+ #### text inside math mode
326
+
327
+ In the case where math mode contains text mode, texml creates text elements possibly containing text XML markup. We turn this into MathJax-compatible TeX strings.
328
+
329
+ - `text` only appears inside tex-math; it is converted to `\\text{...}`
330
+ - `italic` creates `\textit{}`
331
+ - `bold` creates `\textbf{}`
332
+ - `roman` creats `\textrm{...}`
333
+ - `sc` creates `$\mathsc{...}$`
334
+ - `monospace` creates `\texttt{...}`
335
+ - `ext-link` creates `\href{}{}` as(which works in both text and math mode)
336
+
337
+ #### nested math mode
338
+
339
+ In the case where math mode is nested (math mode inside text mode inside math mode), we have to adjust our processing to create MathJax-compatible TeX strings.
340
+
341
+ - nested math mode can only appear within text and will only be inline-formula which is (essentially) converted to `$...$`
342
+
343
+ ### algorithm layout
344
+
345
+ **Note.** Since the markup and attributes are heavily scoped, we do not reproduce the attributes in other sections.
346
+
347
+ Texml creates pseudo-namespaced elements for algorithm layout (e.g., from algorithmicx pacakge).
348
+
349
+ We convert the markup to HTML custom elements with attributes. Further processing happens downstream to enable adequate styling.
350
+
351
+ - alg:algorithm => alg-algorithm
352
+ - \*alg:line => alg-line
353
+ - @lineno => alg-lineno (preceding sibling of alg-line)
354
+ - data-ams-alg-spanslineno (if first child is alg:require or alg:ensure)
355
+ - alg:block => alg-block
356
+ - data-ams-alg-blocklevel (calculated from nesting)
357
+ - alg:statement, alg:require, alg:ensure, alg:globals => alg-statement
358
+ - alg:comment => alg-comment
359
+ - pass through:
360
+ - alg:body
361
+ - alg:outputs
362
+ - alg:inputs
363
+ - alg:condition
364
+ - alg:if
365
+ - alg:elsif
366
+ - alg:else
367
+ - alg:for
368
+ - alg:forall
369
+ - alg:while
370
+ - alg:repeat
371
+ - alg:until
372
+ - alg:loop
@@ -14,6 +14,8 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ import mapAttributes from '../helpers/mapAttributes.js';
18
+
17
19
  /**
18
20
  * inline-formula and disp-formula element
19
21
  * @param {HTMLElement} htmlParentNode
@@ -38,12 +40,11 @@ export default function (htmlParentNode, xmlnode) {
38
40
  const span = this.createNode('span', '', {
39
41
  'data-ams-doc': `math ${mathMode}`
40
42
  });
43
+ mapAttributes(span, xmlnode.querySelector('tex-math'));
41
44
  htmlParentNode.appendChild(span);
42
45
  const hasLinkedTag = xmlnode.querySelector('target tag');
43
46
  const tagContainer = `<span hidden data-ams-doc="tags"></span>`; //NOTE we store copies of tags for easier re-use downstream (cf. tags.js) except for text equations
44
47
  if (hasLinkedTag) span.insertAdjacentHTML('afterbegin', tagContainer)
45
- if (mathMode === 'block' && xmlnode.querySelector('tex-math[has-qed-box]'))
46
- span.setAttribute('data-ams-qed-box', 'true');
47
48
  this.passThrough(span, xmlnode);
48
49
  // NOTE we (sometimes?) get extra whitespace from childnodes; needs test
49
50
  const text = span.innerHTML;
@@ -28,7 +28,7 @@ export default function (htmlParentNode, xmlnode) {
28
28
  this.passThrough(div, xmlnode);
29
29
  const rawCitation = xmlnode.parentNode.querySelector('raw-citation');
30
30
  if (!rawCitation) return;
31
- const code = this.createNode('code', rawCitation.textContent, {
31
+ const code = this.createNode('code', rawCitation.innerHTML, {
32
32
  'data-ams-doc': 'amsref'
33
33
  });
34
34
  div.appendChild(code);
@@ -77,7 +77,7 @@ export default function (htmlParentNode, xmlnode) {
77
77
  section.setAttribute('role', 'doc-introduction');
78
78
 
79
79
  // book appendices
80
- if (tagName === 'book-app-group') {
80
+ if (tagName === 'book-app-group' && xmlnode.querySelectorAll(':scope > book-app').length > 1) {
81
81
  // NOTE might become redundant if book-app-group gets specific-use=part
82
82
  section.setAttribute('role', 'doc-part');
83
83
  section.setAttribute('data-ams-doc', 'part');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amermathsoc/texml-to-html",
3
- "version": "15.1.0",
3
+ "version": "15.2.0",
4
4
  "type": "module",
5
5
  "description": "A NodeJS library for converting AMS-style JATS XML to HTML",
6
6
  "scripts": {
package/test/article.xml CHANGED
@@ -384,7 +384,7 @@
384
384
  <tex-math><text>Te#t </text><xref ref-type="fn" rid="fnid1">1</xref><fn id="fnid"><label>1</label><p><xref ref-type="fn" rid="fnid1">1</xref></p></fn><xref ref-type="other" rid="otherid1"></xref></tex-math>
385
385
  </inline-formula>
386
386
  </p>
387
- <disp-formula><tex-math has-qed-box="true"><xref ref-type="fn" rid="fnid2">2</xref><fn id="fnid2"><label/></fn><text>Start<xref ref-type="other" rid="otherid2">$</xref>End</text></tex-math></disp-formula>
387
+ <disp-formula><tex-math has-qed-box="true" specific-use="special"><xref ref-type="fn" rid="fnid2">2</xref><fn id="fnid2"><label/></fn><text>Start<xref ref-type="other" rid="otherid2">$</xref>End</text></tex-math></disp-formula>
388
388
  <disp-formula>
389
389
  <tex-math>
390
390
  <text><roman>roman#</roman> <sc>sc$</sc> <italic>italic_</italic> <bold>bold$</bold> <sans-serif>sans-serif&amp;</sans-serif> <monospace>monospace</monospace> <ext-link xlink:href="https://ext~">ext-link~</ext-link> inside text</text>
@@ -581,7 +581,7 @@
581
581
  <title>References</title>
582
582
  <ref id="ref">
583
583
  <label>Label</label>
584
- <raw-citation>Raw</raw-citation>
584
+ <raw-citation>Raw &lt;p</raw-citation>
585
585
  <mixed-citation>Mixed</mixed-citation>
586
586
  </ref>
587
587
  <ref id="refnolabel">
@@ -18,7 +18,7 @@ import { article } from './helper.js';
18
18
  import tape from 'tape';
19
19
 
20
20
  tape('inline-formula, disp-formula, tex-math', async function (t) {
21
- t.plan(18);
21
+ t.plan(19);
22
22
  const document = article;
23
23
  const inlineformula = document.querySelector(
24
24
  '#equations [data-ams-doc="math inline"]'
@@ -55,6 +55,11 @@ tape('inline-formula, disp-formula, tex-math', async function (t) {
55
55
  'true',
56
56
  'has-qed-box'
57
57
  );
58
+ t.equal(
59
+ displayformula.getAttribute('data-ams-specific-use'),
60
+ 'special',
61
+ 'disp-formula gets attributes from tex-math mapped'
62
+ );
58
63
  t.ok(
59
64
  displayformula.innerHTML.includes('\\xhref[fn]{#fnid2}{{}^{2}}'),
60
65
  'tex-math/xref@ref-type="fn"'
@@ -19,7 +19,7 @@ import { article } from './helper.js';
19
19
  import tape from 'tape';
20
20
 
21
21
  tape('Template: mixed-citation', async function(t) {
22
- t.plan(2);
22
+ t.plan(3);
23
23
  const document = article;
24
24
  const mixedCitation = document.querySelector(
25
25
  'dd > div[data-ams-doc="biblioentry"]'
@@ -29,4 +29,5 @@ tape('Template: mixed-citation', async function(t) {
29
29
  );
30
30
  t.ok(mixedCitation, 'Element mixed-citation');
31
31
  t.ok(rawCitation, 'Element raw-citation');
32
+ t.equal(rawCitation.innerHTML, 'Raw &lt;p', 'Raw citation escaping');
32
33
  });