diff --git a/PUBLISHING.md b/PUBLISHING.md new file mode 100644 index 00000000..b6ec6c6e --- /dev/null +++ b/PUBLISHING.md @@ -0,0 +1,28 @@ +# Generating a PDF from ecmarkup + +In order to produce a PDF, the front matter `title`, `shortname`, `version`, and `date` are **mandatory**. If generating a final annual edition, date should reflect the date of the Ecma GA which will ratify the Standard. For example: + +``` +title: ECMAScript® 2024 Language Specification +shortname: ECMA-262 +version: 15th Edition +date: 2024-06-25 +``` + +To generate markup for use in PDF conversion, make sure to include the options `--assets`, `--assets-dir`, and `--printable`. If you have images and styles to include, make sure to move them into your assets directory before running `ecmarkup`. For example: + +```shell +mkdir -p out && +mv images out && +mv print.css out && +ecmarkup --assets external --assets-dir out --printable spec.html out/index.html +``` + +Then, from your spec's working directory, run [`prince`](https://www.princexml.com/) to generate your PDF. + +```shell +cd path/to/spec +prince --script ./node_modules/ecmarkup/js/print.js out/index.html -o path/to/output.pdf +``` + +This has been extensively tested with Prince 15. Earlier and later editions not guaranteed. \ No newline at end of file diff --git a/css/elements.css b/css/elements.css index 94347e74..7987be55 100644 --- a/css/elements.css +++ b/css/elements.css @@ -100,7 +100,7 @@ body { font-size: 18px; line-height: 1.5; font-family: - IBM Plex Serif, + 'IBM Plex Serif', serif; font-variant-numeric: slashed-zero; padding: 0; @@ -119,6 +119,8 @@ body { padding-bottom: 1em; } +h1.shortname { display: none; } + body.oldtoc { margin: 0 auto; } @@ -159,7 +161,7 @@ span.e-user-code::before { vertical-align: middle; text-transform: uppercase; font-family: - IBM Plex Sans, + 'IBM Plex Sans', sans-serif; font-weight: 900; font-size: x-small; @@ -184,8 +186,8 @@ span.e-user-code::before { code { font-weight: bold; font-family: - Comic Code, - IBM Plex Mono, + 'Comic Code', + 'IBM Plex Mono', monospace; white-space: pre; } @@ -256,7 +258,7 @@ var.referenced6 { emu-const { font-family: - IBM Plex Sans, + 'IBM Plex Sans', sans-serif; font-variant: small-caps; text-transform: uppercase; @@ -458,7 +460,7 @@ emu-rhs emu-nt { emu-t { display: inline-block; font-family: - IBM Plex Mono, + 'IBM Plex Mono', monospace; font-weight: bold; white-space: nowrap; @@ -495,7 +497,7 @@ emu-params, emu-opt { margin-right: 1ex; font-family: - IBM Plex Mono, + 'IBM Plex Mono', monospace; } @@ -511,7 +513,7 @@ emu-opt { emu-gprose { font-size: 0.9em; font-family: - IBM Plex Sans, + 'IBM Plex Sans', sans-serif; } @@ -780,6 +782,10 @@ emu-table td { background: #fff; } +td[colspan]:not([colspan="1"]), th[colspan]:not([colspan="1"]) { + text-align: center; +} + /* Note: the left content edges of table.lightweight-table >tbody >tr >td and div.display line up. */ table.lightweight-table { @@ -925,7 +931,7 @@ tr.del > td { height: 18px; font-size: 12px; margin: 0 5px 0 10px; - font-family: IBM Plex Sans; + font-family: 'IBM Plex Sans', sans-serif; } #menu-pins .unpin-all:hover { background: #ddd; @@ -950,7 +956,7 @@ tr.del > td { align-self: center; } -#menu-pins-list > li:before, +#menu-pins-list > li::before, #menu-pins-list > li > .unpin { flex-shrink: 0; flex-grow: 0; @@ -960,7 +966,7 @@ tr.del > td { background: none; border: none; } -#menu-pins-list > li:before, +#menu-pins-list > li::before, #menu-pins-list > li > .unpin:hover { background: #ccc; } @@ -974,7 +980,7 @@ tr.del > td { color: #bb1212; } -#menu-pins-list > li:before { +#menu-pins-list > li::before { content: counter(pins-counter); counter-increment: pins-counter; font-size: 16px; @@ -1152,7 +1158,7 @@ a.menu-pane-header-production { overflow-y: auto; } -li.menu-search-result-clause:before { +li.menu-search-result-clause::before { content: 'clause'; width: 40px; display: inline-block; @@ -1161,7 +1167,7 @@ li.menu-search-result-clause:before { color: #666; font-size: 75%; } -li.menu-search-result-op:before { +li.menu-search-result-op::before { content: 'op'; width: 40px; display: inline-block; @@ -1171,7 +1177,7 @@ li.menu-search-result-op:before { font-size: 75%; } -li.menu-search-result-prod:before { +li.menu-search-result-prod::before { content: 'prod'; width: 40px; display: inline-block; @@ -1181,7 +1187,7 @@ li.menu-search-result-prod:before { font-size: 75%; } -li.menu-search-result-term:before { +li.menu-search-result-term::before { content: 'term'; width: 40px; display: inline-block; @@ -1211,10 +1217,10 @@ li.menu-search-result-term:before { white-space: nowrap; } -#menu-trace-list li .secnum:after { +#menu-trace-list li .secnum::after { content: ' '; } -#menu-trace-list li:before { +#menu-trace-list li::before { content: counter(item) ' '; background-color: #222; counter-increment: item; @@ -1301,8 +1307,8 @@ li.menu-search-result-term:before { text-decoration: underline; } -.toolbox:after, -.toolbox:before { +.toolbox::after, +.toolbox::before { top: 100%; left: 15px; border: solid transparent; @@ -1313,13 +1319,13 @@ li.menu-search-result-term:before { pointer-events: none; } -.toolbox:after { +.toolbox::after { border-color: rgba(0, 0, 0, 0); border-top-color: #ddd; border-width: 10px; margin-left: -10px; } -.toolbox:before { +.toolbox::before { border-color: rgba(204, 204, 204, 0); border-top-color: #aaa; border-width: 12px; @@ -1358,7 +1364,7 @@ li.menu-search-result-term:before { display: flex; } -#references-pane-close:after { +#references-pane-close::after { content: '\2716'; float: right; cursor: pointer; @@ -1373,10 +1379,18 @@ li.menu-search-result-term:before { padding-right: 5px; } +emu-normative-optional::before { + display: block; + color: #884400; + content: "NORMATIVE OPTIONAL"; +} + +emu-normative-optional, [normative-optional], [deprecated], [legacy] { border-left: 5px solid #ff6600; + display: block; padding: 0.5em; background: #ffeedd; } @@ -1430,3 +1444,22 @@ li.menu-search-result-term:before { background-color: #eee; box-shadow: inset 0 -1px 0 #ccc; } + +#metadata-block { + margin: 4em 0; + padding: 10px; + border: 1px solid #ee8421; +} + +#metadata-block h1 { + font-size: 1.5em; + margin-top: 0; +} +#metadata-block > ul { + list-style-type: none; + margin: 0; padding: 0; +} + +#ecma-logo { + width: 500px; +} diff --git a/css/print.css b/css/print.css index 7f3b1238..7c772636 100644 --- a/css/print.css +++ b/css/print.css @@ -1,12 +1,213 @@ +@font-face { + font-family: "Arial Plus"; + src: local("Arial"); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+30; + font-style: normal; + font-weight: 400; + src: local(IBM Plex Mono Regular), local(IBMPlexMono-Regular); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+221e; + font-style: normal; + font-weight: 400; + src: local("DejaVu Math TeX Gyre"); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+30; + font-style: normal; + font-weight: 700; + src: local(IBM Plex Mono Bold), local(IBMPlexMono-Bold); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+221e; + font-style: normal; + font-weight: 700; + src: local("DejaVu Math TeX Gyre"); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+30; + font-style: italic; + font-weight: 400; + src: local(IBM Plex Mono Italic), local(IBMPlexMono-Italic); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+221e; + font-style: italic; + font-weight: 400; + src: local("DejaVu Math TeX Gyre"); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+30; + font-style: italic; + font-weight: 700; + src: local(IBM Plex Mono Bold Italic), local(IBMPlexMono-BoldItalic); +} + +@font-face { + font-family: "Arial Plus"; + unicode-range: U+221e; + font-style: italic; + font-weight: 700; + src: local("DejaVu Math TeX Gyre"); +} + +:root { + --page-number-style: decimal; +} + +@page { + size: A4; + margin-top: 28mm; + margin-bottom: 20mm; + margin-inside: 19mm; + margin-outside: 13mm; + -prince-page-fill: prefer-fill; + + /* Uncomment when producing WIP versions of final standards */ + /* + @prince-overlay { + color: rgba(0,0,0,0.15); + content: "WORK IN PROGRESS"; + font-family: Arial; + font-weight: bolder; + font-size: 100pt; + transform: rotate(-60deg); + } + */ + + @bottom-left { + font-family: Arial; + } + + @bottom-right { + font-family: Arial; + } +} + +@page :verso { + @top-left { + content: url('../img/ecma-header.svg'); + padding-top: 5mm; + } + + @bottom-left { + content: counter(page, var(--page-number-style)); + font-size: 10pt; + } + + @bottom-right { + content: '© Ecma International ' string(year, first); + font-size: 8pt; + } +} + +@page :recto { + @top-right { + content: url('../img/ecma-header.svg'); + padding-top: 5mm; + } + + @bottom-left { + content: '© Ecma International ' string(year, first); + font-size: 8pt; + } + + @bottom-right { + content: counter(page, var(--page-number-style)); + font-size: 10pt; + } +} + +@page :first, :nth(2) { + margin: 0; + + @top-left { + content: none; + } + + @top-right { + content: none; + } + + @bottom-left { + content: none; + } + + @bottom-right { + content: none; + } +} + +@page toc, copyright, intro { + --page-number-style: lower-roman; +} + +@page :blank { + @bottom-left { + content: none; + } + + @bottom-right { + content: none; + } +} + +@page front-cover { + background-image: url('../img/print-front-cover.svg'); +} + +@page inside-cover { + background-image: url('../img/print-inside-cover.svg'); +} + +@page front-cover, inside-cover { + background-color: transparent; + background-position: center; + background-repeat: no-repeat; + margin: 0; + padding: 0; + page-break-after: always; +} + +html, body { + background-color: initial; +} + body { - font-family: Arial, Helvetica, sans-serif; + font-family: 'Arial Plus', Arial, Helvetica, sans-serif, "DejaVu Math TeX Gyre", Symbola, monospace; font-size: 10pt; - background: #fff; color: #000; + line-height: 1.15; } -.title { +h1, h2, h3, h4, h5, h6 { -prince-bookmark-level: none } + +.copyright-notice + h1.title { + break-before: recto; + color: black; + counter-reset: page 1; + display: block; + font-size: 15pt; font-family: Verdana; + font-weight: bold; + margin-bottom: 2.5ex; + margin-top: initial; } p { @@ -24,6 +225,62 @@ h1 { line-height: 1.4; } +emu-alg { + display: block; /* Can't render block elements inside inline elements. */ +} + +emu-alg li { + orphans: 2; + widows: 2; +} + +emu-note, +emu-note p, +emu-table tr, +emu-table th, +emu-table td, +pre, +h1, +emu-production, +emu-figure:has(> figure > img) figure, +#metadata-block { + break-inside: avoid; + border: unset; +} + +p:has(+ .math-display), +emu-table thead, +h1, +figcaption, +emu-alg > ol > li:first-child, +emu-grammar:has(+ emu-alg), +figcaption:has(+ emu-table) { + break-after: avoid-page; +} + +emu-alg ol li:last-child { + break-before: avoid; + break-after: initial; /* it's okay to break after the last item in a list, even if it's also the first item in the list */ +} + +emu-normative-optional emu-clause[id] { + margin-top: 0; +} + +emu-normative-optional emu-alg > ol { + margin-bottom: 0; +} + +emu-note { + gap: initial; + justify-content: space-between; +} + +emu-note .note { + font-size: 9pt; + min-width: 4.5em; +} + emu-note p, emu-table td p { text-align: left; @@ -31,23 +288,206 @@ emu-table td p { overflow: hidden; } +emu-nt, emu-t { + display: initial; +} + +emu-production.inline { + text-align: left; +} + +emu-production.inline emu-nt { + display: inline; +} + +emu-intro { + page: intro; +} + +emu-intro, emu-clause, emu-annex { + margin-top: 4ex; +} + +emu-clause p:first-of-type { + margin-bottom: 0; + orphans: 3; +} + +emu-clause > p:first-of-type { + break-after: avoid-page; +} + +emu-clause p:last-child { + break-after: auto; + margin-bottom: 0; +} + +emu-clause > p:only-of-type { + break-after: auto; +} + +emu-clause > p:first-of-type + emu-alg { + break-before: avoid; +} + +emu-intro emu-intro, emu-clause emu-clause, emu-annex emu-annex { + margin-top: 3.5ex; +} + +emu-intro emu-intro emu-intro, +emu-clause emu-clause emu-clause, +emu-annex emu-annex emu-annex, +emu-intro emu-intro emu-intro emu-intro, +emu-clause emu-clause emu-clause emu-clause, +emu-annex emu-annex emu-annex emu-annex, +emu-intro emu-intro emu-intro emu-intro emu-intro, +emu-clause emu-clause emu-clause emu-clause emu-clause, +emu-annex emu-annex emu-annex emu-annex emu-annex { + margin-top: 3.2ex; +} + +emu-intro h1, emu-clause h1 , emu-annex h1 { + break-after: avoid; + font-size: 12pt; + -prince-bookmark-level: 1; + -prince-bookmark-label: content(); +} + +emu-clause emu-clause h1, emu-annex emu-annex h1 { + -prince-bookmark-level: 2; + -prince-bookmark-state: closed; +} + +emu-clause emu-clause h1, emu-annex emu-annex h1, +emu-intro h2, emu-clause h2, emu-annex h2 { + font-size: 11pt; +} + +emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex h1 { + -prince-bookmark-level: 3; +} + +emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex h1 { + -prince-bookmark-level: 4; +} + +emu-clause emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex emu-annex h1 { + -prince-bookmark-level: 5; +} + +emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex h1 { + -prince-bookmark-level: 6; +} + +emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex h1, +emu-clause emu-clause h2, emu-annex emu-annex h2, +emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex h1, +emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex h2, +emu-clause emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex emu-annex h1, +emu-clause emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex emu-annex h2, +emu-clause emu-clause emu-clause emu-clause emu-clause emu-clause h1, emu-annex emu-annex emu-annex emu-annex emu-annex emu-annex h1, +emu-clause emu-clause emu-clause emu-clause emu-clause h2, emu-annex emu-annex emu-annex emu-annex emu-annex h2 { + font-size: 10pt; +} + +emu-clause ol, emu-clause ul, emu-clause dl, emu-annex ol, emu-annex ul, emu-annex dl { + margin-left: 0; + padding-left: 1.75em; +} + +emu-clause ol ol, emu-clause ul ul { + padding-left: 2em; +} + +emu-grammar { + display: block; +} + +emu-grammar:has(emu-production.inline) { + display: inline-block; +} + +h1 + emu-grammar { + margin-top: 1ex; +} + +p + emu-grammar { + break-before: avoid; +} + emu-table td, emu-table th { overflow-wrap: break-word; } -emu-table table { - table-layout: auto; - width: 100%; +caption, table > figcaption { + caption-side: top; + color: #555555; + font-weight: bold; + margin-bottom: 1rem; + text-align: center; +} + +caption { + -prince-caption-page: first; +} + +/* do not break inside of small tables */ +table:not(:has(tr:nth-of-type(5))) { + break-inside: avoid-page; +} + +table > figcaption { + display: table-caption; + -prince-caption-page: following; +} + +table > figcaption::after { + content: ' (continued)'; + font-style: italic; + font-weight: normal; +} + +th[rowspan] { + vertical-align: bottom; +} + +td[rowspan] { + vertical-align: middle; +} + +emu-table thead { + display: table-header-group; +} + +emu-table tfoot { + display: table-footer-group; } emu-figure img { + margin-top: 1ex; max-width: 100%; height: auto; } #spec-container { - max-width: none; + max-width: initial; +} + +#spec-container > emu-annex { + margin-top: 0; +} + +#toc > h2 { + -prince-bookmark-level: 1; + -prince-bookmark-label: content(); +} + +#toc > h2::after { + content: 'page'; + float: right; + font-size: 10pt; + text-align: right; } #toc a, @@ -55,27 +495,31 @@ emu-figure img { color: #000; } -#toc a[href] { - background: #fff; - padding-right: 0.5em; -} #toc a[href]::after { - content: /* leader(dotted) */ target-counter(attr(href), page); - float: right; - padding-left: 0.5em; - background: #fff; + content: leader(dotted) target-counter(attr(href), page); } -/* NOTE: hacks because Paged.js doesn't support leader() in content directives */ -#toc ol { - overflow-x: hidden; + +#toc > ol.toc { + margin-top: 0; } -#toc ol li:before { - float: left; - width: 0; - white-space: nowrap; - content: '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' - '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' - '. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .'; + +ol.toc { + font-weight: bold; + margin-left: 0; + padding-left: 0; +} + +ol.toc ol.toc { + padding-left: 0; +} + +ol.toc li { + text-indent: 35pt hanging; +} + +ol.toc .secnum { + display: inline-block; + min-width: 3.25em; } /* skip the Introduction since it's before the first emu-clause (and therefore doesn't have a proper page number) */ @@ -84,119 +528,123 @@ emu-figure img { } #toc > ol > li { - margin-top: 1ex; + margin-top: 0.75ex; } #toc, #spec-container > emu-intro, #spec-container > emu-annex { break-before: recto; - break-after: always; + break-after: page; } -/* according to Ecma guidelines, we're actually not supposed to break before every clause (only the first), but Paged.js fails if we do that */ -/* so instead, just break before any of the clauses that have sub-clauses */ -#spec-container > emu-clause:has(emu-clause:not([example])) { - break-before: always; +a[data-print-href]::after { + content: ' <' attr(href) '>'; + color: initial; } -#spec-container > emu-clause:first-of-type { - break-before: recto; +.real-table { + max-width: 100%; + width: auto; } -emu-note, -emu-note p, -emu-table tr, -emu-table th, -emu-table td, -emu-alg li, -pre, -h1, -#metadata-block { - break-inside: avoid; +.annex-title { + text-align: center; } -emu-table thead, -h1, -figcaption, -emu-alg > ol > li:first-child { - break-after: avoid; +.copyright-notice { /* ecma mandated */ + font-style: italic; + border: 1px solid black; + padding: 1em; + page: copyright; + page-break-before: always; + page-break-after: always; } -emu-grammar + emu-alg, -figcaption + emu-table, -figcaption + span[id] + emu-table, -emu-alg > ol > li:last-child { - break-before: avoid; +.full-page-svg { + color: rgba(255, 255, 255, 0); } -a[data-print-href]::after { - content: ' <' attr(href) '>'; - color: initial; +.secnum { + font-family: Arial, Helvetica, sans-serif; } -emu-table thead { - display: table-header-group; -} -emu-table tfoot { - display: table-footer-group; +#front-cover { + page: front-cover; + position: relative; + width: 210mm; + height: 297mm; } -@page { - size: A4; +#front-cover h1 { + color: black; + display: block; + font-family: Verdana; + position: absolute; } -@page { - @top-center { - content: url(img/ecma-header.png); - } +h1.shortname { + top: 86mm; + font-weight: 400; + font-size: 21pt; + right: 31mm; + text-align: right; + margin-top: 0; } -@page :first { - @top-center { - content: none; - } + +h1.shortname a:link, h1.shortname a:visited, h1.shortname a:hover, h1.shortname a:active { + color: black; } -:root { - --page-number-style: decimal; +h1.shortname .status { + display: inline-block; + margin-right: 7em; + text-transform: capitalize; } -#toc { - page: toc; +h1.version { + font-size: 9.7pt; + font-weight: normal; + margin-top: 0; + text-align: right; + top: 96mm; + left: 139mm; + string-set: year attr(data-year); } -@page toc { - --page-number-style: lower-roman; + +#front-cover h1.title { + font-weight: bold; + font-size: 20pt; + line-height: 1.2; + top: 109mm; + right: 15mm; + width: 95mm; + text-align: left; } -emu-intro { - page: intro; + +#inside-cover { + page: inside-cover; } -@page intro { - --page-number-style: lower-roman; + +#inside-cover address { + color: black; + font-size: 12pt; + font-style: normal; + position: relative; + left: 72mm; + top: 82mm; } #toc { - counter-reset: page 1; -} -#spec-container > emu-clause:first-of-type { + page: toc; counter-reset: page 1; } -@page :left { - @bottom-left { - content: counter(page, var(--page-number-style)); - } -} -@page :right { - @bottom-right { - content: counter(page, var(--page-number-style)); - } +#toc h2 { + font-size: 12pt; + margin-bottom: 1.5ex; } -@page :first { - @bottom-left { - content: ''; - } - @bottom-right { - content: ''; - } +.annex-kind { + font-weight: normal; } diff --git a/img/ecma-header.svg b/img/ecma-header.svg new file mode 100644 index 00000000..5eaffa9d --- /dev/null +++ b/img/ecma-header.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/print-front-cover.svg b/img/print-front-cover.svg new file mode 100644 index 00000000..aa0ae357 --- /dev/null +++ b/img/print-front-cover.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/img/print-inside-cover.svg b/img/print-inside-cover.svg new file mode 100644 index 00000000..c0f6f02d --- /dev/null +++ b/img/print-inside-cover.svg @@ -0,0 +1,59 @@ + + + + + + + COPYRIGHT PROTECTED DOCUMENT + + + Ecma International + Rue du Rhone 114 CH-1204 Geneva + Tel: +41 22 849 6000 + Fax: +41 22 849 6001 + Web: + + + https://www.ecma-international.org + + + + + © Ecma International + + + + + + + + is the registered trademark of Ecma International + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/print.js b/js/print.js new file mode 100644 index 00000000..237ddbcb --- /dev/null +++ b/js/print.js @@ -0,0 +1,75 @@ +/** + * Some notes: + * - Prince cant grok trailing commas, so prettier is disabled anywhere it tries to enforce wrapping with trailing comma + * - Prince doesn't support template strings yet + * - Set Prince.trackboxes to true for advanced debugging, see https://www.princexml.com/doc/cookbook/#how-and-where-is-my-box + * */ + +/* eslint-disable no-undef */ +'use strict'; + +// Prince.trackBoxes = true; + +const specContainer = document.getElementById('spec-container'); +const shortname = specContainer.querySelector('h1.shortname'); +const version = specContainer.querySelector('h1.version'); + +rearrangeTables(); + +PDF.pageLayout = 'two-column-right'; +PDF.pageMode = 'show-bookmarks'; +PDF.duplex = 'duplex-flip-long-edge'; +PDF.title = document.title; +PDF.author = 'Ecma International'; +PDF.subject = shortname.innerHTML + ', ' + version.innerHTML; + +/** + * Sets up table captions and figcaptions for tables, which provides for + * continuation table captions. + * */ +function rearrangeTables() { + const tables = Array.from(document.getElementsByTagName('emu-table')); + + tables.forEach(emuTable => { + const figcaption = emuTable.getElementsByTagName('figcaption')[0]; + const tableCaptionText = figcaption.innerHTML; + const table = emuTable.getElementsByTagName('table')[0]; + const captionElement = document.createElement('caption'); + + captionElement.innerHTML = tableCaptionText; + + table.insertBefore(captionElement, table.getElementsByTagName('thead')[0]); + table.appendChild(figcaption); + }); +} + +/** + * @typedef {Object} PrinceBox + * @property {string} type + * @property {number} pageNum + * @property {number} x + * @property {number} y + * @property {number} w + * @property {number} h + * @property {number} baseline + * @property {number} marginTop + * @property {number} marginBottom + * @property {number} marginLeft + * @property {number} marginRight + * @property {number} paddingTop + * @property {number} paddingBottom + * @property {number} paddingLeft + * @property {number} paddingRight + * @property {number} borderTop + * @property {number} borderBottom + * @property {number} borderLeft + * @property {number} borderRight + * @property {string} floatPosition "TOP" or "BOTTOM" + * @property {PrinceBox[]} children + * @property {PrinceBox} parent + * @property {Element|null} element + * @property {string|null} pseudo + * @property {string} text + * @property {string} src + * @property {CSSStyleSheet} style + * */ diff --git a/spec/index.html b/spec/index.html index a1b8e995..b140bb32 100644 --- a/spec/index.html +++ b/spec/index.html @@ -75,7 +75,7 @@

Options

`copyright`Emit copyright and software license information. Boolean, default true. `contributors`Contributors to this specification, i.e. those who own the copyright. If your proposal includes text from any Ecma specification, this should include "Ecma International". `--no-toc``toc`Emit table of contents. Boolean, default true. - `--old-toc``oldToc`Emit the table of contents at the top of the document rather than as a side pane. Boolean, default false. + `--printable``printable`Make the output suitable for printing. Boolean, default false. `--load-biblio``extraBiblios`Extra biblio.json file(s) to load. This should contain either an object as exported by `--write-biblio` or an array of such objects. `boilerplate`An object with `address` and/or `copyright` and/or `license` fields containing paths to files containing the corresponding content for populating an element with class "copyright-and-software-license" (or if not found, an appended with that id) when `copyright` is true. Absent fields are assumed to reference files in a "boilerplate" directory sibling of the directory containing the ecmarkup executable. `--mark-effects``markEffects`Propagate and render effects like "user code". @@ -260,6 +260,7 @@

emu-annex

Attributes

normative: If present, annex is normative. Default is non-normative.

+

back-matter: If present, annex is back matter (such as a colophon), which means it will not be assigned an annex letter and will appear without one in the table of contents and document. This attribute should only be used with non-normative annexes, and back matter annexes should not be followed by non-back-matter annexes.

id: Clause id. Must be unique.

aoid: Abstract operation ID. A unique name identifying this clause as an abstract operation. Algorithm steps will auto-link calls to this abstract operation to this clause. If left blank, the aoid will be set to the id of this clause.

diff --git a/src/Clause.ts b/src/Clause.ts index a7794f35..526c3342 100644 --- a/src/Clause.ts +++ b/src/Clause.ts @@ -56,6 +56,9 @@ export default class Clause extends Builder { /** @internal */ signature: Signature | null; /** @internal */ skipGlobalChecks: boolean; /** @internal */ skipReturnChecks: boolean; + isAnnex: boolean; + isBackMatter: boolean; + isNormative: boolean; constructor(spec: Spec, node: HTMLElement, parent: Clause, number: string) { super(spec, node); @@ -69,6 +72,9 @@ export default class Clause extends Builder { this.effects = []; this.skipGlobalChecks = false; this.skipReturnChecks = false; + this.isAnnex = node.nodeName === 'EMU-ANNEX'; + this.isBackMatter = this.isAnnex && node.hasAttribute('back-matter'); + this.isNormative = !this.isAnnex || node.hasAttribute('normative'); // namespace is either the entire spec or the parent clause's namespace. let parentNamespace = spec.namespace; @@ -402,6 +408,20 @@ export default class Clause extends Builder { clauseStack.pop(); } + getSecnumHTML() { + if (!this.number || this.isBackMatter) return ''; + if (this.isAnnex) { + const isInnerAnnex = this.node.parentElement?.nodeName === 'EMU-ANNEX'; + if (isInnerAnnex) { + return `${this.number} `; + } else { + return `Annex ${this.number} (${this.isNormative ? 'normative' : 'informative'}) `; + } + } else { + return `${this.number} `; + } + } + static elements = ['EMU-INTRO', 'EMU-CLAUSE', 'EMU-ANNEX']; static linkElements = Clause.elements; } diff --git a/src/H1.ts b/src/H1.ts index a47459d1..8117bdcb 100644 --- a/src/H1.ts +++ b/src/H1.ts @@ -20,11 +20,19 @@ export default class H1 extends Builder { parent.titleHTML = headerClone.innerHTML; parent.title = headerClone.textContent; if (parent.number) { - const numElem = spec.doc.createElement('span'); - numElem.setAttribute('class', 'secnum'); - numElem.textContent = parent.number; - node.insertBefore(spec.doc.createTextNode(' '), node.firstChild); - node.insertBefore(numElem, node.firstChild); + // we want to prepend some HTML but setting `innerHTML` on the node will blow away the existing children, + // which messes up other stuff which expects those to keep their identity + node.insertAdjacentHTML('afterbegin', parent.getSecnumHTML()); + + // const frag = spec.doc.createElement('div'); + // frag.innerHTML = parent.getSecnumHTML() + node.innerHTML; + // node.prepend(...frag.children); + // node.innerHTML = ; + // const numElem = spec.doc.createElement('span'); + // numElem.setAttribute('class', 'secnum'); + // numElem.textContent = parent.number; + // node.insertBefore(spec.doc.createTextNode(' '), node.firstChild); + // node.insertBefore(numElem, node.firstChild); } } } diff --git a/src/Spec.ts b/src/Spec.ts index 5b1f035a..18c92f6d 100644 --- a/src/Spec.ts +++ b/src/Spec.ts @@ -90,7 +90,7 @@ const FONT_FILES = new Map([ ['IBMPlexMono-BoldItalic', 'IBMPlexMono-BoldItalic-SlashedZero.woff2'], ]); -const IMG_FILES = new Set(['ecma-header.png']); +const IMG_FILES = new Set(['ecma-header.svg', 'print-front-cover.svg', 'print-inside-cover.svg']); interface VisitorMap { [k: string]: BuilderInterface; @@ -390,6 +390,12 @@ export default class Spec { throw new Error('--js-out and --css-out have been removed; use --assets-dir instead'); } + if (this.opts.oldToc) { + throw new Error( + '--old-toc has been removed; specify --printable to get a printable document', + ); + } + if ( this.opts.assets != null && this.opts.assets !== 'external' && @@ -398,6 +404,15 @@ export default class Spec { throw new Error(`--assets=${this.opts.assets} cannot be used --assets-dir"`); } + if (this.opts.printable) { + if (this.opts.title == null) { + throw new Error(`--printable requires a title to be set in the metadata"`); + } + if (this.opts.shortname == null) { + throw new Error(`--printable requires a shortname to be set in the metadata"`); + } + } + if (this.opts.multipage) { this.opts.outfile ??= ''; const type = this.opts.assets ?? 'external'; @@ -533,8 +548,10 @@ export default class Spec { this.log('Propagating effect annotations...'); this.propagateEffects(); - this.log('Annotating external links...'); - this.annotateExternalLinks(); + if (this.opts.printable) { + this.log('Annotating external links...'); + this.annotateExternalLinks(); + } this.log('Linking xrefs...'); this._xrefs.forEach(xref => xref.build()); this.log('Linking non-terminal references...'); @@ -574,20 +591,69 @@ export default class Spec { this.setCharset(); const wrapper = this.buildSpecWrapper(); + if (this.opts.printable) { + this.log('Building covers and applying other print tweaks...'); + const metadataEle = this.doc.querySelector('#metadata-block'); + if (metadataEle) { + this.doc.querySelector('emu-intro')!.appendChild(metadataEle); + } + const scopeEle = document.getElementById('scope') ?? document.getElementById('sec-scope'); + if (!scopeEle) { + throw new Error('--printable requires a scope'); + } + scopeEle.before(this.doc.querySelector('h1.title')!.cloneNode(true)); + + // front cover + const frontCover = document.createElement('div'); + + frontCover.classList.add('full-page-svg'); + frontCover.setAttribute('id', 'front-cover'); + + const shortNameNode = this.doc.querySelector('h1.shortname'); + if (shortNameNode != null) { + frontCover.appendChild(shortNameNode.cloneNode(true)); + } + const versionNode = this.doc.querySelector('h1.version'); + if (versionNode != null) { + frontCover.appendChild(versionNode.cloneNode(true)); + } + // we know title exists because we enforce it in the constructor when using --printable + frontCover.appendChild(this.doc.querySelector('title')!.cloneNode(true)); + wrapper.before(frontCover); + + // inside cover + const insideCover = document.createElement('div'); + + insideCover.classList.add('full-page-svg'); + insideCover.setAttribute('id', 'inside-cover'); + insideCover.innerHTML = '

Ecma International
Rue du Rhone 114 CH-1204 Geneva
Tel: +41 22 849 6000
Fax: +41 22 849 6001
Web: https://www.ecma-international.org
Ecma is the registered trademark of Ecma International.

'; + + frontCover.after(insideCover); + + + } + let commonEles: HTMLElement[] = []; let tocJs = ''; if (this.opts.toc) { this.log('Building table of contents...'); - if (this.opts.oldToc) { + if (this.opts.printable) { new Toc(this).build(2); } else { ({ js: tocJs, eles: commonEles } = makeMenu(this)); } } - this.log('Building shortcuts help dialog...'); - commonEles.push(this.buildShortcutsHelp()); + if (this.opts.printable) { + this.log('Applying tweaks for printable document...'); + // The logo is present in ecma-262. We could consider removing it from the document instead of having this tweak. + document.getElementById('ecma-logo')?.remove(); + } else { + // apparently including this confuses Prince, even when it's `display: none` + this.log('Building shortcuts help dialog...'); + commonEles.push(this.buildShortcutsHelp()); + } for (const ele of commonEles) { this.doc.body.insertBefore(ele, this.doc.body.firstChild); @@ -605,7 +671,7 @@ export default class Spec { const file = this.opts.multipage ? path.join(this.opts.outfile!, 'index.html') - : this.opts.outfile ?? null; + : (this.opts.outfile ?? null); this.generatedFiles.set(file, this.toHTML()); return this; @@ -1078,7 +1144,7 @@ ${await utils.readFile(path.join(__dirname, '../js/multipage.js'))} const urlRef = this.assets.type === 'inline' ? `data:font/${fontType};base64,${FONT_FILE_CONTENTS.get(fontFile)!.toString('base64')}` - : `./${fontFile}`; + : `../${fontFile}`; return `${indent}src: local(${displayName}), local(${postScriptName}), url(${urlRef}) format('${fontType}');`; }, ) @@ -1103,8 +1169,8 @@ ${await utils.readFile(path.join(__dirname, '../js/multipage.js'))} : process.cwd(); const scriptLocationOnDisk = path.join(this.assets.directory, 'ecmarkup.js'); - const styleLocationOnDisk = path.join(this.assets.directory, 'ecmarkup.css'); - const printStyleLocationOnDisk = path.join(this.assets.directory, 'print.css'); + const styleLocationOnDisk = path.join(this.assets.directory, 'css', 'ecmarkup.css'); + const printStyleLocationOnDisk = path.join(this.assets.directory, 'css', 'print.css'); this.generatedFiles.set(scriptLocationOnDisk, jsContents); this.generatedFiles.set(styleLocationOnDisk, cssContents); @@ -1117,7 +1183,7 @@ ${await utils.readFile(path.join(__dirname, '../js/multipage.js'))} } for (const imgFile of IMG_FILES) { this.generatedFiles.set( - path.join(this.assets.directory, imgFile), + path.join(this.assets.directory, 'img', imgFile), IMG_FILE_CONTENTS.get(imgFile)!, ); } @@ -1127,7 +1193,11 @@ ${await utils.readFile(path.join(__dirname, '../js/multipage.js'))} script.setAttribute('defer', ''); this.doc.head.appendChild(script); - this.addStyle(this.doc.head, path.relative(outDir, printStyleLocationOnDisk), 'print'); + this.addStyle( + this.doc.head, + path.relative(outDir, printStyleLocationOnDisk), + this.opts.printable ? void 0 : 'print', + ); this.addStyle(this.doc.head, path.relative(outDir, styleLocationOnDisk)); } else { // i.e. assets.type === 'inline' @@ -1141,41 +1211,13 @@ ${await utils.readFile(path.join(__dirname, '../js/multipage.js'))} style.textContent = cssContents; this.doc.head.appendChild(style); const printStyle = this.doc.createElement('style'); - printStyle.textContent = `@media print {\n${printCssContents}\n}`; - this.doc.head.appendChild(printStyle); - } - const currentYearStyle = this.doc.createElement('style'); - currentYearStyle.textContent = ` - @media print { - @page :left { - @bottom-right { - content: '© Ecma International ${this.opts.date!.getFullYear()}'; - } - } - @page :right { - @bottom-left { - content: '© Ecma International ${this.opts.date!.getFullYear()}'; - } - } - @page :first { - @bottom-left { - content: ''; - } - @bottom-right { - content: ''; - } - } - @page :blank { - @bottom-left { - content: ''; - } - @bottom-right { - content: ''; - } + if (this.opts.printable) { + printStyle.textContent = printCssContents; + } else { + printStyle.textContent = `@media print {\n${printCssContents}\n}`; } + this.doc.head.appendChild(printStyle); } - `; - this.doc.head.appendChild(currentYearStyle); this.addStyle( this.doc.head, `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/${ @@ -1385,7 +1427,29 @@ ${this.opts.multipage ? `
  • Navigate to/from multipagem { + if (last) return false; + if (node.nodeName === 'EMU-CLAUSE' || node.nodeName === 'EMU-ANNEX') { + last = node as HTMLElement; + return false; + } + }); + + if (last && last.parentNode) { + last.parentNode.insertBefore(copyrightClause, last.nextSibling); + } else { + this.doc.body.appendChild(copyrightClause); + } + } } } @@ -1395,7 +1459,7 @@ ${this.opts.multipage ? `
  • Navigate to/from multipagemNavigate to/from multipagemNavigate to/from multipagem${shortname}` : shortname; const shortnameHtml = - status.charAt(0).toUpperCase() + status.slice(1) + ' ' + shortnameLinkHtml; + (this.opts.printable && status === 'standard' + ? '' + : `${status.charAt(0).toUpperCase() + status.slice(1)} `) + + shortnameLinkHtml; if (!this._updateBySelector('h1.shortname', shortnameHtml)) { const h1 = this.doc.createElement('h1'); @@ -1466,7 +1541,9 @@ ${this.opts.multipage ? `
  • Navigate to/from multipagem n.getAttribute('property') === 'og:title')) { - const title = this.opts.title ?? this.doc.querySelector('title')?.textContent; + const title = this.opts.title + ? utils.textContentFromHTML(this.doc, this.opts.title) + : this.doc.querySelector('title')?.textContent; if (title) { const meta = this.doc.createElement('meta'); meta.setAttribute('property', 'og:title'); @@ -1517,24 +1594,10 @@ ${this.opts.multipage ? `
  • Navigate to/from multipagem { - if (last) return false; - if (node.nodeName === 'EMU-CLAUSE' || node.nodeName === 'EMU-ANNEX') { - last = node as HTMLElement; - return false; - } - }); - copyrightClause = this.doc.createElement('emu-annex'); copyrightClause.setAttribute('id', 'sec-copyright-and-software-license'); - - if (last && last.parentNode) { - last.parentNode.insertBefore(copyrightClause, last.nextSibling); - } else { - this.doc.body.appendChild(copyrightClause); - } } + copyrightClause.setAttribute('back-matter', ''); copyrightClause.innerHTML = `

    Copyright & Software License

    @@ -1544,6 +1607,8 @@ ${this.opts.multipage ? `
  • Navigate to/from multipagemSoftware License ${license} `; + + return copyrightClause; } private generateSDOMap() { diff --git a/src/Toc.ts b/src/Toc.ts index 8eaf1a4a..87535cff 100644 --- a/src/Toc.ts +++ b/src/Toc.ts @@ -16,7 +16,7 @@ export default class Toc { const html = Toc.build(this.spec, { maxDepth }); const tocContainer = this.spec.doc.createElement('div'); tocContainer.setAttribute('id', 'toc'); - tocContainer.innerHTML = '

    Table of Contents

    ' + html; + tocContainer.innerHTML = '

    Contents

    ' + html; const intro = this.spec.doc.querySelector('emu-intro, emu-clause, emu-annex'); if (intro && intro.parentNode) { intro.parentNode.insertBefore(tocContainer, intro); @@ -44,11 +44,7 @@ export default class Toc { } } - html += ''; - if (sub.number) { - html += '' + sub.number + ' '; - } - html += shorten(sub.titleHTML) + ''; + html += `${sub.getSecnumHTML()}${shorten(sub.titleHTML)}`; if (sub.subclauses.length > 0) html += Toc.build(sub, { maxDepth: maxDepth - 1, expandy }); html += '
  • '; }); diff --git a/src/args.ts b/src/args.ts index c92fb5ab..f7aa0277 100644 --- a/src/args.ts +++ b/src/args.ts @@ -45,9 +45,9 @@ export const options = [ description: "Don't include the table of contents", }, { - name: 'old-toc', + name: 'printable', type: Boolean, - description: 'Use the old table of contents styling', + description: 'Make the output suitable for printing', }, { name: 'mark-effects', @@ -104,4 +104,8 @@ export const options = [ name: 'js-out', type: String, }, + { + name: 'old-toc', + type: Boolean, + }, ] as const; diff --git a/src/cli.ts b/src/cli.ts index e33f3506..328bc183 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -24,7 +24,7 @@ function usage() { }, { header: 'Options', - hide: ['files', 'js-out', 'css-out'], + hide: ['files', 'js-out', 'css-out', 'old-toc'], optionList: options as unknown as commandLineUsage.OptionDefinition[], }, ]), @@ -64,6 +64,10 @@ if (args.strict && args.watch) { fail('Cannot use --strict with --watch'); } +if (args['old-toc']) { + fail('--old-toc has been removed; specify --printable to get a printable document'); +} + if (args['js-out'] || args['css-out']) { fail('--js-out and --css-out have been removed; specify --assets-dir instead'); } @@ -112,8 +116,8 @@ const build = debounce(async function build() { if (args['no-toc'] != null) { opts.toc = !args['no-toc']; } - if (args['old-toc'] != null) { - opts.oldToc = args['old-toc']; + if (args.printable != null) { + opts.printable = args.printable; } if (args.assets != null) { diff --git a/src/ecmarkup.ts b/src/ecmarkup.ts index 5ee57920..34aae168 100644 --- a/src/ecmarkup.ts +++ b/src/ecmarkup.ts @@ -37,6 +37,7 @@ export interface Options { contributors?: string; toc?: boolean; oldToc?: boolean; + printable?: boolean; markEffects?: boolean; lintSpec?: boolean; cssOut?: never; diff --git a/src/header-parser.ts b/src/header-parser.ts index 66dadbfd..32197708 100644 --- a/src/header-parser.ts +++ b/src/header-parser.ts @@ -688,7 +688,7 @@ export function formatPreamble( ? 'It is defined piecewise over the following productions:' : 'It performs the following steps when called:'; const getRelevantElement = (el: Element): Element => - el.tagName === 'INS' || el.tagName === 'DEL' ? el.firstElementChild ?? el : el; + el.tagName === 'INS' || el.tagName === 'DEL' ? (el.firstElementChild ?? el) : el; let next = dl.nextElementSibling; while (next != null && getRelevantElement(next)?.tagName === 'EMU-NOTE') { next = next.nextElementSibling; diff --git a/src/type-logic.ts b/src/type-logic.ts index cd96d7bc..f86ad3e6 100644 --- a/src/type-logic.ts +++ b/src/type-logic.ts @@ -376,7 +376,9 @@ export function typeFromExpr( // however we mostly use `never` to mean user error, so use unknown instead // this should only ever happen after `Return` { kind: 'unknown' } - : callType.of.find(k => k.kind === 'normal completion')?.of ?? { kind: 'unknown' }; + : (callType.of.find(k => k.kind === 'normal completion')?.of ?? { + kind: 'unknown', + }); return normal; } } diff --git a/src/utils.ts b/src/utils.ts index 72d23330..9902b4d5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -82,6 +82,13 @@ export function htmlToDom(html: string) { return new jsdom.JSDOM(html, { includeNodeLocations: true, virtualConsole }); } +/** @internal */ +export function textContentFromHTML(doc: Document, html: string) { + const ele = doc.createElement('span'); + ele.innerHTML = html; + return ele.textContent!; +} + /** @internal */ export function domWalkBackward(root: Node, cb: (node: Element) => boolean | undefined) { const childNodes = root.childNodes; diff --git a/test/baselines/generated-reference/assets-inline.html b/test/baselines/generated-reference/assets-inline.html index 4f72c8ee..9e8897af 100644 --- a/test/baselines/generated-reference/assets-inline.html +++ b/test/baselines/generated-reference/assets-inline.html @@ -1709,7 +1709,7 @@ font-size: 18px; line-height: 1.5; font-family: - IBM Plex Serif, + 'IBM Plex Serif', serif; font-variant-numeric: slashed-zero; padding: 0; @@ -1728,6 +1728,8 @@ padding-bottom: 1em; } +h1.shortname { display: none; } + body.oldtoc { margin: 0 auto; } @@ -1768,7 +1770,7 @@ vertical-align: middle; text-transform: uppercase; font-family: - IBM Plex Sans, + 'IBM Plex Sans', sans-serif; font-weight: 900; font-size: x-small; @@ -1793,8 +1795,8 @@ code { font-weight: bold; font-family: - Comic Code, - IBM Plex Mono, + 'Comic Code', + 'IBM Plex Mono', monospace; white-space: pre; } @@ -1865,7 +1867,7 @@ emu-const { font-family: - IBM Plex Sans, + 'IBM Plex Sans', sans-serif; font-variant: small-caps; text-transform: uppercase; @@ -2067,7 +2069,7 @@ emu-t { display: inline-block; font-family: - IBM Plex Mono, + 'IBM Plex Mono', monospace; font-weight: bold; white-space: nowrap; @@ -2104,7 +2106,7 @@ emu-opt { margin-right: 1ex; font-family: - IBM Plex Mono, + 'IBM Plex Mono', monospace; } @@ -2120,7 +2122,7 @@ emu-gprose { font-size: 0.9em; font-family: - IBM Plex Sans, + 'IBM Plex Sans', sans-serif; } @@ -2389,6 +2391,10 @@ background: #fff; } +td[colspan]:not([colspan="1"]), th[colspan]:not([colspan="1"]) { + text-align: center; +} + /* Note: the left content edges of table.lightweight-table >tbody >tr >td and div.display line up. */ table.lightweight-table { @@ -2534,7 +2540,7 @@ height: 18px; font-size: 12px; margin: 0 5px 0 10px; - font-family: IBM Plex Sans; + font-family: 'IBM Plex Sans', sans-serif; } #menu-pins .unpin-all:hover { background: #ddd; @@ -2559,7 +2565,7 @@ align-self: center; } -#menu-pins-list > li:before, +#menu-pins-list > li::before, #menu-pins-list > li > .unpin { flex-shrink: 0; flex-grow: 0; @@ -2569,7 +2575,7 @@ background: none; border: none; } -#menu-pins-list > li:before, +#menu-pins-list > li::before, #menu-pins-list > li > .unpin:hover { background: #ccc; } @@ -2583,7 +2589,7 @@ color: #bb1212; } -#menu-pins-list > li:before { +#menu-pins-list > li::before { content: counter(pins-counter); counter-increment: pins-counter; font-size: 16px; @@ -2761,7 +2767,7 @@ overflow-y: auto; } -li.menu-search-result-clause:before { +li.menu-search-result-clause::before { content: 'clause'; width: 40px; display: inline-block; @@ -2770,7 +2776,7 @@ color: #666; font-size: 75%; } -li.menu-search-result-op:before { +li.menu-search-result-op::before { content: 'op'; width: 40px; display: inline-block; @@ -2780,7 +2786,7 @@ font-size: 75%; } -li.menu-search-result-prod:before { +li.menu-search-result-prod::before { content: 'prod'; width: 40px; display: inline-block; @@ -2790,7 +2796,7 @@ font-size: 75%; } -li.menu-search-result-term:before { +li.menu-search-result-term::before { content: 'term'; width: 40px; display: inline-block; @@ -2820,10 +2826,10 @@ white-space: nowrap; } -#menu-trace-list li .secnum:after { +#menu-trace-list li .secnum::after { content: ' '; } -#menu-trace-list li:before { +#menu-trace-list li::before { content: counter(item) ' '; background-color: #222; counter-increment: item; @@ -2910,8 +2916,8 @@ text-decoration: underline; } -.toolbox:after, -.toolbox:before { +.toolbox::after, +.toolbox::before { top: 100%; left: 15px; border: solid transparent; @@ -2922,13 +2928,13 @@ pointer-events: none; } -.toolbox:after { +.toolbox::after { border-color: rgba(0, 0, 0, 0); border-top-color: #ddd; border-width: 10px; margin-left: -10px; } -.toolbox:before { +.toolbox::before { border-color: rgba(204, 204, 204, 0); border-top-color: #aaa; border-width: 12px; @@ -2967,7 +2973,7 @@ display: flex; } -#references-pane-close:after { +#references-pane-close::after { content: '\2716'; float: right; cursor: pointer; @@ -2982,10 +2988,18 @@ padding-right: 5px; } +emu-normative-optional::before { + display: block; + color: #884400; + content: "NORMATIVE OPTIONAL"; +} + +emu-normative-optional, [normative-optional], [deprecated], [legacy] { border-left: 5px solid #ff6600; + display: block; padding: 0.5em; background: #ffeedd; } @@ -3039,16 +3053,236 @@ background-color: #eee; box-shadow: inset 0 -1px 0 #ccc; } + +#metadata-block { + margin: 4em 0; + padding: 10px; + border: 1px solid #ee8421; +} + +#metadata-block h1 { + font-size: 1.5em; + margin-top: 0; +} +#metadata-block > ul { + list-style-type: none; + margin: 0; padding: 0; +} + +#ecma-logo { + width: 500px; +}
    +}
    • Toggle shortcuts help?
    • Toggle "can call user code" annotationsu
    • @@ -3283,13 +3734,13 @@
    +

    Intro

    This is a section.

    - -

    A Copyright & Software License

    + +

    Copyright & Software License

    Copyright Notice

    © 1997 Ecma

    diff --git a/test/baselines/generated-reference/boilerplate-address.html b/test/baselines/generated-reference/boilerplate-address.html index 2c0a250e..30361537 100644 --- a/test/baselines/generated-reference/boilerplate-address.html +++ b/test/baselines/generated-reference/boilerplate-address.html @@ -9,8 +9,8 @@
  • Jump to nth pin1-9
  • test title!

    - -

    A Copyright & Software License

    + +

    Copyright & Software License

    Tet Corporation

    2 Hammarskjöld Plaza

    New York City, New York

    diff --git a/test/baselines/generated-reference/boilerplate-all.html b/test/baselines/generated-reference/boilerplate-all.html index 8addee3e..95e17f5a 100644 --- a/test/baselines/generated-reference/boilerplate-all.html +++ b/test/baselines/generated-reference/boilerplate-all.html @@ -9,8 +9,8 @@
  • Jump to nth pin1-9
  • test title!

    - -

    A Copyright & Software License

    + +

    Copyright & Software License

    Tet Corporation

    2 Hammarskjöld Plaza

    New York City, New York

    diff --git a/test/baselines/generated-reference/boilerplate-copyright.html b/test/baselines/generated-reference/boilerplate-copyright.html index c7fe4767..131ae6ac 100644 --- a/test/baselines/generated-reference/boilerplate-copyright.html +++ b/test/baselines/generated-reference/boilerplate-copyright.html @@ -9,8 +9,8 @@
  • Jump to nth pin1-9
  • test title!

    - -

    A Copyright & Software License

    + +

    Copyright & Software License

    Ecma International

    Rue du Rhone 114

    CH-1204 Geneva

    diff --git a/test/baselines/generated-reference/boilerplate-license.html b/test/baselines/generated-reference/boilerplate-license.html index b47ffb0f..8c09b220 100644 --- a/test/baselines/generated-reference/boilerplate-license.html +++ b/test/baselines/generated-reference/boilerplate-license.html @@ -9,8 +9,8 @@
  • Jump to nth pin1-9
  • test title!

    - -

    A Copyright & Software License

    + +

    Copyright & Software License

    Ecma International

    Rue du Rhone 114

    CH-1204 Geneva

    diff --git a/test/baselines/generated-reference/clauses.html b/test/baselines/generated-reference/clauses.html index 3cf2fa80..15058e57 100644 --- a/test/baselines/generated-reference/clauses.html +++ b/test/baselines/generated-reference/clauses.html @@ -1,14 +1,6 @@ -
    -
      -
    • Toggle shortcuts help?
    • -
    • Toggle "can call user code" annotationsu
    • - -
    • Jump to search box/
    • -
    • Toggle pinning of the current clausep
    • -
    • Jump to nth pin1-9
    • -
    - +Test Doc
    Test Doc

    Ecma International
    Rue du Rhone 114 CH-1204 Geneva
    Tel: +41 22 849 6000
    Fax: +41 22 849 6001
    Web: https://www.ecma-international.org
    Ecma is the registered trademark of Ecma International.

    Test Doc

    +

    Intro

    Sub Intro

    @@ -60,7 +52,7 @@

    7.6.2.1 Increasing multi-step explicit numbers -

    A Annex

    +

    Annex A (informative) Annex

    A.1 Sub-annex

    diff --git a/test/baselines/generated-reference/copyright.html b/test/baselines/generated-reference/copyright.html index 934ecd7e..9db23f97 100644 --- a/test/baselines/generated-reference/copyright.html +++ b/test/baselines/generated-reference/copyright.html @@ -10,8 +10,8 @@

    Draft 1 / September 26, 2014

    test title!

    1 Test Clause

    -
    -

    A Copyright & Software License

    + +

    Copyright & Software License

    Ecma International

    Rue du Rhone 114

    CH-1204 Geneva

    diff --git a/test/baselines/generated-reference/namespaces.html b/test/baselines/generated-reference/namespaces.html index d55e1256..adc8da41 100644 --- a/test/baselines/generated-reference/namespaces.html +++ b/test/baselines/generated-reference/namespaces.html @@ -58,7 +58,7 @@

    1.1.1 SomeAlg

    -

    A Annex

    +

    Annex A (informative) Annex

    Foo :: bar @@ -85,7 +85,7 @@

    A.2 Annex 1.2

    -

    B Annex 2

    +

    Annex B (informative) Annex 2

    B.1 SomeAlg

    diff --git a/test/baselines/generated-reference/proposal-copyright.html b/test/baselines/generated-reference/proposal-copyright.html index 19a5d0c4..94cf8c08 100644 --- a/test/baselines/generated-reference/proposal-copyright.html +++ b/test/baselines/generated-reference/proposal-copyright.html @@ -9,8 +9,8 @@
  • Jump to nth pin1-9
  • Stage 0 Draft / September 26, 2014

    test title!

    - -

    A Copyright & Software License

    + +

    Copyright & Software License

    Copyright Notice

    © 2014 Brian Terlson, Ecma International

    diff --git a/test/baselines/generated-reference/shortname.html b/test/baselines/generated-reference/shortname.html index 7e842d50..f5f177c9 100644 --- a/test/baselines/generated-reference/shortname.html +++ b/test/baselines/generated-reference/shortname.html @@ -7,7 +7,7 @@
  • Jump to search box/
  • Toggle pinning of the current clausep
  • Jump to nth pin1-9
  • -

    Draft ECMA-000

    Draft 1 / September 26, 2015

    test title!

    +

    Draft ECMA-000

    Draft 1 / September 26, 2015

    test title!

    Some body content

    \ No newline at end of file diff --git a/test/baselines/generated-reference/test.html b/test/baselines/generated-reference/test.html index 46a8bd42..6672afb7 100644 --- a/test/baselines/generated-reference/test.html +++ b/test/baselines/generated-reference/test.html @@ -15,7 +15,7 @@

    Draft 1 / September 26, 2015

    Ecmarkup Test Document

    +

    Draft 1 / September 26, 2015

    Ecmarkup Test Document

    Intro

    @@ -161,6 +161,6 @@

    1.3.1 Import 3

    -

    A Annex

    +

    Annex A (informative) Annex

    \ No newline at end of file diff --git a/test/baselines/sources/clauses.html b/test/baselines/sources/clauses.html index 96594410..dba843c7 100644 --- a/test/baselines/sources/clauses.html +++ b/test/baselines/sources/clauses.html @@ -1,5 +1,7 @@