Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions css/smpte.css
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,16 @@ blockquote {
margin-left: 1rem;
}

.example {
padding-left: 1rem;
}

.example > .heading-label {
display: block;
margin-left: -1rem;
margin-bottom: 0.25em;
}

/* lists */

li {
Expand All @@ -232,19 +242,16 @@ a.heading-link {
}

@media only screen {
h2:hover .heading-link {
visibility: visible;
}
h3:hover .heading-link {
visibility: visible;
}
h4:hover .heading-link {
visibility: visible;
}
h5:hover .heading-link {
visibility: visible;
}
h6:hover .heading-link {
h2:hover .heading-link,
h3:hover .heading-link,
h4:hover .heading-link,
h5:hover .heading-link,
h6:hover .heading-link,
caption:hover .heading-link,
figcaption:hover .heading-link,
div.formula:hover .heading-link,
.note:hover .heading-link,
.example:hover .heading-link {
visibility: visible;
}
}
Expand Down
186 changes: 115 additions & 71 deletions doc/main.html

Large diffs are not rendered by default.

30 changes: 21 additions & 9 deletions js/validate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,15 @@ function validateFootnoteReferences(root, logger) {
}
}

export function smpteValidate(doc, logger) {
export function validateDataIncludes(doc, logger, fileExists = null) {
for (const el of doc.querySelectorAll("pre[data-include]")) {
const src = el.getAttribute("data-include");
if (fileExists === null || !fileExists(src))
logger.error(`data-include file not found: ${src}`, el);
}
}

export function smpteValidate(doc, logger, fileExists = null) {
const docMetadata = smpte.validateHead(doc.head, logger);
validateDisallowedHeadLinks(doc.head, logger);
validateDisallowedStyleAttributes(doc.documentElement, logger);
Expand All @@ -124,6 +132,8 @@ export function smpteValidate(doc, logger) {
validateTfootNoteOrder(doc.documentElement, logger);
validateFootnoteReferences(doc.documentElement, logger);
validateBody(doc.body, logger);
if (fileExists !== null)
validateDataIncludes(doc, logger, fileExists);
return docMetadata;
}

Expand Down Expand Up @@ -736,10 +746,18 @@ class InternalDefinitionsMatcher {

if (count === 0) {
const next = children[0];
logger.error(`Out of order or unrecognized element in definition${next ? `: ${next.localName}${next.className ? `.${next.className}` : ""}` : ""}<br>Required order is: definition, deprecated, example, note, source`, next || element);
logger.error(`Out of order or unrecognized element in definition${next ? `: ${next.localName}${next.className ? `.${next.className}` : ""}` : ""}<br>Required order is: deprecated, definition, example, note, source`, next || element);
break;
}

/* look for deprecated marker (at most one) */

if (children.length > 0 && DefinitionDeprecatedMatcher.match(children[0], logger)) {
children.shift();
if (children.length > 0 && DefinitionDeprecatedMatcher.match(children[0], logger))
logger.error(`Only one dd.deprecated is permitted per term`, children[0]);
}

/* look for definition */

if (children.length > 0 &&
Expand All @@ -748,13 +766,7 @@ class InternalDefinitionsMatcher {
children.shift();
}

/* look for deprecated marker (at most one) */

if (children.length > 0 && DefinitionDeprecatedMatcher.match(children[0], logger)) {
children.shift();
if (children.length > 0 && DefinitionDeprecatedMatcher.match(children[0], logger))
logger.error(`Only one dd.deprecated is permitted per term`, children[0]);
}


/* look for examples to entry */
count = 0;
Expand Down
7 changes: 5 additions & 2 deletions scripts/validate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
import * as jsdom from "jsdom"
import process from "process";
import * as fs from "fs";
import * as path from "path";
import {smpteValidate} from "../js/validate.mjs";

async function main() {
const dom = new jsdom.JSDOM(fs.readFileSync(process.argv[2]));
smpteValidate(dom.window.document, console);
const filePath = path.resolve(process.argv[2]);
const dom = new jsdom.JSDOM(fs.readFileSync(filePath));
const fileExists = (src) => fs.existsSync(path.resolve(path.dirname(filePath), src));
smpteValidate(dom.window.document, console, fileExists);
}

main().catch(e => { console.error(e); process.exitCode = 1; });
60 changes: 52 additions & 8 deletions smpte.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import { smpteValidate } from "./js/validate.mjs";
import { smpteValidate, validateDataIncludes } from "./js/validate.mjs";
import * as smpte from "./js/common.mjs";

class Logger {
Expand Down Expand Up @@ -77,7 +77,7 @@ function resolveScriptRelativePath(path) {
function asyncFetchLocal(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest
xhr.onload = () => resolve(xhr.responseText);
xhr.onload = () => xhr.status === 200 ? resolve(xhr.responseText) : reject(new TypeError(`File not found: ${url}`));
xhr.onerror = () => reject(new TypeError('Local request failed'));
xhr.open('GET', url);
xhr.send(null);
Expand All @@ -91,7 +91,7 @@ async function asyncAddStylesheet(url) {
s.textContent = data;
document.head.appendChild(s);
})
.catch(err => logError("Cannot fetch: " + err));
.catch(err => logger_.error("Cannot fetch: " + err));
}

function fillTemplate(template, data) {
Expand Down Expand Up @@ -593,7 +593,7 @@ function insertElementsAnnex(docMetadata) {
return;
}

sec.classList.add("unnumbered");
sec.classList.add("annex", "informative");

const intro = document.createElement("p");
intro.innerText = "The following are the non-prose elements of this document:"
Expand Down Expand Up @@ -900,6 +900,50 @@ function addHeadingLinks(docMetadata) {

heading.appendChild(headingLink);
}

for (const table of document.querySelectorAll("table[id]")) {
const caption = table.querySelector("caption");
if (!caption) continue;
const link = document.createElement("a");
link.className = "heading-link";
link.href = `#${table.id}`;
link.innerHTML = "🔗";
caption.appendChild(link);
}

for (const figure of document.querySelectorAll("figure[id]")) {
const figcaption = figure.querySelector("figcaption");
if (!figcaption) continue;
const link = document.createElement("a");
link.className = "heading-link";
link.href = `#${figure.id}`;
link.innerHTML = "🔗";
figcaption.appendChild(link);
}

for (const formula of document.querySelectorAll("div.formula[id]")) {
const link = document.createElement("a");
link.className = "heading-link";
link.href = `#${formula.id}`;
link.innerHTML = "🔗";
formula.appendChild(link);
}

for (const note of document.querySelectorAll(".note[id]")) {
const link = document.createElement("a");
link.className = "heading-link";
link.href = `#${note.id}`;
link.innerHTML = "🔗";
note.appendChild(link);
}

for (const example of document.querySelectorAll(".example[id]")) {
const link = document.createElement("a");
link.className = "heading-link";
link.href = `#${example.id}`;
link.innerHTML = "🔗";
example.appendChild(link);
}
}

function numberSections(element, curHeadingNumber) {
Expand Down Expand Up @@ -1187,7 +1231,6 @@ function numberExamples() {

headingLabel.appendChild(document.createTextNode("EXAMPLE "));
headingLabel.appendChild(headingNumberElement);
headingLabel.appendChild(document.createTextNode(" —⁠ "));

example.insertBefore(headingLabel, example.firstChild);
}
Expand Down Expand Up @@ -1475,9 +1518,9 @@ function asyncInsertSnippets() {
return Promise.all(Array.from(
document.querySelectorAll("pre[data-include]"),
(e) => {
asyncFetchLocal(e.getAttribute("data-include"))
.then(data => e.textContent = data)
.catch(err => logError("Cannot fetch: " + err));
return asyncFetchLocal(e.getAttribute("data-include"))
.then(data => { e.textContent = data; e.removeAttribute("data-include"); })
.catch(() => {});
}
));
}
Expand Down Expand Up @@ -1564,6 +1607,7 @@ document.addEventListener('DOMContentLoaded', async () => {
try {
smpteValidate(window.document, logger_);
await render();
validateDataIncludes(window.document, logger_);
window._smpteRenderComplete = true;
} catch (e) {
logger_.error(e);
Expand Down
1 change: 1 addition & 0 deletions test/resources/html/snippets/example.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This an example.
30 changes: 30 additions & 0 deletions test/resources/html/validation/snippet-invalid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!doctype html>
<html>
<head itemscope="itemscope" itemtype="http://smpte.org/standards/documents">
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script type="module" src="../../../../smpte.js"></script>
<meta itemprop="test" content="invalid" />
<meta itemprop="pubType" content="ST" />
<meta itemprop="pubNumber" content="429" />
<meta itemprop="pubPart" content="6" />
<meta itemprop="pubSuiteTitle" content="Suite title" />
<meta itemprop="pubTC" content="27C" />
<meta itemprop="pubStage" content="WD" />
<meta itemprop="pubState" content="draft" />
<title>Title of the document</title>
</head>
<body>
<section id="sec-scope">
<p>This is the scope of the document.</p>
</section>

<section id="sec-snippets-element">
<h2>External code snippets</h2>
<p>External code snippet:</p>
<pre data-include="../snippets/bad-example.txt"></pre>
</section>

</body>
</html>
30 changes: 30 additions & 0 deletions test/resources/html/validation/snippet-valid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!doctype html>
<html>
<head itemscope="itemscope" itemtype="http://smpte.org/standards/documents">
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script type="module" src="../../../../smpte.js"></script>
<meta itemprop="test" content="valid" />
<meta itemprop="pubType" content="ST" />
<meta itemprop="pubNumber" content="429" />
<meta itemprop="pubPart" content="6" />
<meta itemprop="pubSuiteTitle" content="Suite title" />
<meta itemprop="pubTC" content="27C" />
<meta itemprop="pubStage" content="WD" />
<meta itemprop="pubState" content="draft" />
<title>Title of the document</title>
</head>
<body>
<section id="sec-scope">
<p>This is the scope of the document.</p>
</section>

<section id="sec-snippets-element">
<h2>External code snippets</h2>
<p>External code snippet:</p>
<pre data-include="../snippets/example.txt"></pre>
</section>

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<dt><dfn>Clear</dfn></dt>
<dt>CLR</dt>
<dd>something that is clean</dd>
<dd class="deprecated">old</dd>
<dd class="note">The term shall not be used to refer to clarity of mind.</dd>
<dd class="note">The term is not intended to be used ever.</dd>
<dd class="source"><a href="#bib-smpte-standards-operations-manual"></a></dd>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<dl id="terms-int-defs">
<dt><dfn>Clear</dfn></dt>
<dt>CLR</dt>
<dd class="deprecated">sub-black</dd>
<dd>something that is clean</dd>
<dd class="note">The term shall not be used to refer to clarity of mind.</dd>
<dd class="note">The term is not intended to be used ever.</dd>
Expand Down
12 changes: 7 additions & 5 deletions test/src/testValidation.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,19 @@ import {smpteValidate, ErrorLogger} from "../../js/validate.mjs";

const testDirPath = "test/resources/html/validation";

async function _test(path) {
const dom = new jsdom.JSDOM(fs.readFileSync(path));
async function _test(filePath) {
const dom = new jsdom.JSDOM(fs.readFileSync(filePath));

const expectation = dom.window.document.head.querySelector("meta[itemprop='test']").getAttribute("content");

const logger = new ErrorLogger();

let hasThrown = false;

const fileExists = (src) => fs.existsSync(path.resolve(path.dirname(filePath), src));

try {
smpteValidate(dom.window.document, logger);
smpteValidate(dom.window.document, logger, fileExists);
} catch (e) {
logger.error(`Exception: ${e.stack}`);
hasThrown = true;
Expand All @@ -52,11 +54,11 @@ async function _test(path) {
const hasPassed = !logger.hasFailed() && !hasThrown;

if ((expectation === "valid" && hasPassed) || (expectation !== "valid" && !hasPassed)) {
console.log(`${path} passed.`);
console.log(`${filePath} passed.`);
return true;
}

console.log(`**** ${path} failed.`);
console.log(`**** ${filePath} failed.`);
logger.errorList().map(msg => console.log(` ${msg.message}`));

return false;
Expand Down
Loading