Skip to content
Draft
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
4 changes: 2 additions & 2 deletions css/smpte.css
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ blockquote {
font-style: italic;
}

/* note and example */
/* note, example, footnote */

.note, .example {
.note, .example, .footnote {
font-size: 0.9rem;
margin-left: 1rem;
}
Expand Down
73 changes: 72 additions & 1 deletion doc/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -1133,10 +1133,78 @@ <h4>Tables</h4>
<code>&lt;a href=&quot;#tab-sample-tabledata&quot;&gt;&lt;/a&gt;</code> is rendered as <a href="#tab-sample-tabledata"></a>.
</div>

<p>
<p>
For more examples of various table layouts and optional cell/column text alignment, see <a href="#sec-table-examples"></a>
</p>

<p>Tables may include footnotes. Footnotes shall appear in a <code>tfoot</code> element at the foot of the table,
each as a <code>p</code> element with <code>class="footnote"</code> and a unique <code>id</code> attribute. Footnotes are automatically assigned superscript lowercase letters (a, b, c, …) in the order they appear in the <code>tfoot</code> element.
References to footnotes within the table body are marked with an <code>a</code> element whose <code>href</code>
attribute references the footnote's <code>id</code>.</p>

<p class="note">Footnotes are only permitted within tables. They shall not be used outside of a <code>table</code> element.</p>

<div class="example">
<pre>
&lt;table id=&quot;tab-sample-footnote&quot;&gt;
&lt;caption&gt;Sample Table with Footnotes&lt;/caption&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Sample Number&lt;/th&gt;
&lt;th&gt;Sample Name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0001&lt;/td&gt;
&lt;td&gt;Name 01 &lt;a href=&quot;#fn-doc-example&quot;&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0002&lt;/td&gt;
&lt;td&gt;Name 02&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tfoot&gt;
&lt;tr&gt;
&lt;td colspan=&quot;2&quot;&gt;
&lt;p class=&quot;footnote&quot; id=&quot;fn-doc-example&quot;&gt;Footnote content goes here.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tfoot&gt;
&lt;/table&gt;
</pre>

<p>is rendered as</p>

<table id="tab-sample-footnote">
<caption>Sample Table with Footnotes</caption>
<thead>
<tr>
<th>Sample Number</th>
<th>Sample Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>0001</td>
<td>Name 01 <a href="#fn-doc-example"></a></td>
</tr>
<tr>
<td>0002</td>
<td>Name 02</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">
<p class="footnote" id="fn-doc-example">Footnote content goes here.</p>
</td>
</tr>
</tfoot>
</table>
</div>


</section>

<section id="sec-snippets-element">
Expand Down Expand Up @@ -1360,6 +1428,9 @@ <h4>Using <code>class</code> attributes</h4>
<dt><code>example</code></dt>
<dd>Applied to <code>p</code> or <code>div</code> elements to create a formatted and automatically numbered example. See <a href="#sec-example-class"></a>.</dd>

<dt><code>footnote</code></dt>
<dd>Applied to a <code>p</code> element inside a <code>tfoot</code> element to create a table footnote. Footnotes are only permitted inside a <code>tfoot</code> element — use outside of tables is not allowed. See <a href="#sec-table-element"></a>.</dd>

<dt><code>center-cell</code>;</dt>
<dt><code>right-cell</code>;</dt>
<dt><code>left-cell</code>;</dt>
Expand Down
8 changes: 8 additions & 0 deletions js/validate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,18 @@ function validateDisallowedStyleAttributes(root, logger) {
}
}

function validateFootnoteLocation(root, logger) {
for (const fn of root.querySelectorAll('.footnote')) {
if (!fn.closest('tfoot'))
logger.error(`Footnotes are only permitted inside a table footer`, fn);
}
}

export function smpteValidate(doc, logger) {
const docMetadata = smpte.validateHead(doc.head, logger);
validateDisallowedHeadLinks(doc.head, logger);
validateDisallowedStyleAttributes(doc.documentElement, logger);
validateFootnoteLocation(doc.documentElement, logger);
validateBody(doc.body, logger);
return docMetadata;
}
Expand Down
41 changes: 40 additions & 1 deletion smpte.js
Original file line number Diff line number Diff line change
Expand Up @@ -1104,14 +1104,15 @@ function numberNotesToEntry(internalTermsSection) {

child.insertBefore(headingLabel, child.firstChild);
}

}

function numberSectionNotes(section) {
let notes = [];

function _findNotes(e) {
for (const child of e.children) {
if (child.localName === "section")
if (child.localName === "section" || child.localName === "table")
numberSectionNotes(child);
else if (child.classList.contains("note"))
notes.push(child);
Expand Down Expand Up @@ -1194,6 +1195,36 @@ function numberExamples() {
}
}

function numberTableFootnotes() {
for (const table of document.querySelectorAll("table")) {
const footnotes = Array.from(table.querySelectorAll("tfoot p.footnote"));
if (footnotes.length === 0) continue;

let charCode = "a".charCodeAt(0);

for (const fn of footnotes) {
if (!fn.id) continue;
const letter = String.fromCharCode(charCode++);

const headingLabel = document.createElement("span");
headingLabel.className = "heading-label";

const sup = document.createElement("sup");
sup.textContent = letter;

headingLabel.appendChild(sup);
headingLabel.appendChild(document.createTextNode("\u00a0"));

fn.insertBefore(headingLabel, fn.firstChild);

/* fill all reference anchors pointing to this footnote */
for (const ref of table.querySelectorAll(`a[href="#${fn.id}"]`)) {
ref.textContent = letter;
}
}
}
}

function numberTerms() {
const termsSection = document.getElementById("sec-terms-and-definitions");
const terms = document.getElementById("terms-int-defs");
Expand Down Expand Up @@ -1382,6 +1413,13 @@ function resolveLinks(docMetadata) {
anchor.innerText = t;
}

} else if (target.classList.contains("footnote")) {

/* footnote ref — letter already filled by numberTableFootnotes; wrap in <sup> */
const sup = document.createElement("sup");
anchor.replaceWith(sup);
sup.appendChild(anchor);

} else if (target.localName === "section") {

anchor.innerText = _getSectionReference(target);
Expand Down Expand Up @@ -1487,6 +1525,7 @@ async function render() {
numberFormulae();
numberNotes();
numberExamples();
numberTableFootnotes();
numberTerms();
resolveLinks(docMetadata);
insertTOC(docMetadata);
Expand Down
51 changes: 51 additions & 0 deletions test/resources/html/validation/footnote-invalid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!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-footnote">
<h2>Footnote</h2>

<table id="tab-sample-tabledata">
<caption>Sample Table</caption>
<thead>
<tr>
<th>Sample Number</th>
<th>Sample Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>0001</td>
<td>Name 01 <a href="#fn-1-example"></a></td>
</tr>
<tr>
<td>0002</td>
<td>Name 02</td>
</tr>
</tbody>
</table>

<p class="footnote" id="fn-1-example">Footnote content goes here.</p>

</section>

</body>
</html>
56 changes: 56 additions & 0 deletions test/resources/html/validation/footnote-valid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!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-footnote">
<h2>Footnote</h2>

<table id="tab-sample-tabledata">
<caption>Sample Table</caption>
<thead>
<tr>
<th>Sample Number</th>
<th>Sample Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>0001</td>
<td>Name 01 <a href="#fn-1-example"></a></td>
</tr>
<tr>
<td>0002</td>
<td>Name 02</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">
<p class="footnote" id="fn-1-example">Footnote content goes here.</p>
</td>
</tr>
</tfoot>
</table>

</section>

</body>
</html>
3 changes: 3 additions & 0 deletions test/resources/html/validation/st-valid.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@
<section id="sec-scope">
<p>This is the scope of the document.</p>
</section>



</body>
</html>
Loading