Build the single page version of the spec with the new script.
--- a/build.py Tue Aug 21 17:29:06 2012 +1000
+++ b/build.py Fri Sep 14 20:03:34 2012 +1000
@@ -178,11 +178,11 @@
break
if buildSinglePage:
- chaptersNoIndex = " ".join(all[1:])
- run("xsltproc --novalid --stringparam publish '" + publish_dir +
- "' --stringparam chapters '" + chaptersNoIndex + "' " +
- join(tools_dir, "single-page.xsl") + " " +
- join(publish_dir, "index.html") + " > " + single_page)
+ os.chdir(master_dir)
+ run("node \"" +
+ native_path(join(tools_dir, "publish/publish.js")) +
+ "\" --build-single-page")
+ os.chdir(repo_dir) # chdir back
# Copy over anything else that needs to be copied to 'publish':
--- a/publish/publish.js Tue Aug 21 17:29:06 2012 +1000
+++ b/publish/publish.js Fri Sep 14 20:03:34 2012 +1000
@@ -2,7 +2,8 @@
namespaces = require('./namespaces.js'),
utils = require('./utils.js'),
processing = require('./processing.js'),
- fs = require('fs');
+ fs = require('fs'),
+ DOMParser = require('xmldom').DOMParser;
var conf;
@@ -13,6 +14,7 @@
console.error(' --list-pages Print the names of all pages of the specification.');
console.error(' --build [PAGE ...] Builds the specified pages, or all pages if');
console.error(' none specified.');
+ console.error(' --build-single-page Builds the single page version of the specification.');
console.error(' --local-style Link to local W3C style sheets rather than w3.org.');
console.error(' --help Show this message.');
}
@@ -27,6 +29,9 @@
case '--build':
opts.build = true;
break;
+ case '--build-single-page':
+ opts.buildSinglePage = true;
+ break;
case '--local-style':
opts.localStyle = true;
break;
@@ -91,8 +96,102 @@
fs.writeFileSync(outputFilename, doc.toString());
}
+function buildSinglePage() {
+ var outputFilename = conf.outputDirectory + 'single-page.html';
+ var parser = new DOMParser();
+ parser.recordPositions = true;
+
+ // New document.
+ var doc = parser.parseFromString('<html><head></head><body></body></html>');
+ var head = doc.documentElement.firstChild;
+ var body = doc.documentElement.lastChild;
+
+ // Add HTML doctype.
+ doc.insertBefore(doc.implementation.createDocumentType("html", "", ""), doc.firstChild);
+
+ var foundMathjax = false;
+
+ // Add the contents of each published chapter.
+ for (var i = 0; i < conf.pageOrder.length; i++) {
+ var page = conf.pageOrder[i];
+ var pageDocument = utils.parseXHTML(conf.outputDirectory + page + '.html');
+ var pageHead = utils.child(pageDocument.documentElement, 'head');
+ var pageBody = utils.child(pageDocument.documentElement, 'body');
+ if (i == 0) {
+ // The <head> contents of the index are used for the single page document.
+ head.appendChild(utils.cloneChildren(pageHead));
+ } else {
+ // Separate each page.
+ body.appendChild(utils.parse('<hr class="chapter-divider"/>'));
+ }
+
+ utils.forEachNode(pageHead, function(n) {
+ // Copy a <style> in any chapter's <head> into the single page <head>.
+ if (n.nodeName == 'style') {
+ head.appendChild(n.cloneNode(true));
+ }
+ // Note if Mathjax was used anywhere.
+ if (n.nodeName == 'script' &&
+ n.getAttribute('src') == 'style/load-mathjax.js') {
+ foundMathjax = true;
+ }
+ });
+
+ // Clone the body of the chapter.
+ var clonedPageBody = utils.cloneChildren(pageBody);
+
+ // Fix up IDs so that they don't clash between chapters.
+ utils.forEachNode(clonedPageBody, function(n) {
+ if (n.nodeType == n.ELEMENT_NODE) {
+ if (n.hasAttribute('id')) {
+ n.setAttribute('id', page + '-' + n.getAttribute('id'));
+ }
+ if (n.nodeName == 'a') {
+ if (n.hasAttribute('name')) {
+ n.setAttribute('name', page + '-' + n.getAttribute('name'));
+ }
+ if (n.hasAttribute('href')) {
+ var href = n.getAttribute('href');
+ if (href[0] == '#') {
+ n.setAttribute('href', '#' + page + '-' + href.substring(1));
+ } else if (href.match(/^([^\/]+)\.html#(.*)$/)) {
+ n.setAttribute('href', '#' + RegExp.$1 + '-' + RegExp.$2);
+ } else if (href.match(/^([^\/]+)\.html$/)) {
+ n.setAttribute('href', '#chapter-' + RegExp.$1);
+ }
+ }
+ }
+ }
+ });
+
+ // Copy the chapter in.
+ body.appendChild(utils.parse('<div id="chapter-{{page}}" class="{{classes}}">{{pagebody}}</div>',
+ { page: page,
+ classes: pageBody.getAttribute('class'),
+ pagebody: clonedPageBody }));
+ }
+
+ // Remove all references to expanders.js.
+ utils.forEachNode(doc, function(n) {
+ if (n.nodeName == 'script' &&
+ n.getAttribute('src') == 'style/expanders.js') {
+ n.parentNode.removeChild(n);
+ }
+ });
+
+ // Add one reference to expanders.js at the end of the document.
+ body.appendChild(utils.parse('<script src="style/expanders.js"></script>'));
+
+ // Add reference to load-mathjax.js if we found one in any chapter.
+ if (foundMathjax) {
+ head.appendChild(utils.parse('<script src="style/load-mathjax.js"></script>'));
+ }
+
+ fs.writeFileSync(outputFilename, doc.toString());
+}
+
var opts = parseOptions();
-if (opts.help || (!!opts.listPages + !!opts.build) != 1) {
+if (opts.help || (!!opts.listPages + !!opts.build + !!opts.buildSinglePage) != 1) {
syntax();
process.exit(opts.help ? 0 : 1);
} else {
@@ -102,5 +201,7 @@
listPages();
} else if (opts.build) {
buildPages(opts.rest);
+ } else if (opts.buildSinglePage) {
+ buildSinglePage();
}
}
--- a/single-page.xsl Tue Aug 21 17:29:06 2012 +1000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
- xmlns:h='http://www.w3.org/1999/xhtml'
- xmlns='http://www.w3.org/1999/xhtml'
- version='1.0'>
-
- <xsl:output method='xml' encoding='UTF-8'
- doctype-public='-//W3C//DTD XHTML 1.0 Transitional//EN'
- doctype-system='http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'/>
-
- <xsl:param name='chapters'/>
- <xsl:param name='publish'/>
-
- <xsl:template match='/h:html/h:body'>
- <xsl:copy>
- <xsl:apply-templates select="@*"/>
- <xsl:apply-templates/>
- <xsl:call-template name='do-chapters'>
- <xsl:with-param name='cs' select='concat($chapters, " ")'/>
- </xsl:call-template>
- <script src='style/expanders.js'><xsl:text> </xsl:text></script>
- </xsl:copy>
- </xsl:template>
-
- <xsl:template match='h:div[@class="header" or @class="footer" or @class="header top" or @class="header bottom"]'/>
-
- <xsl:template match="h:head">
- <xsl:copy>
- <xsl:apply-templates select="@*"/>
- <xsl:apply-templates select="*[not(self::script)]"/>
- <xsl:call-template name='do-chapters-style'>
- <xsl:with-param name='cs' select='concat($chapters, " ")'/>
- </xsl:call-template>
- <script>
- var n, local = location.protocol == "file:";
- if (local) {
- for (n = document.head.firstChild; n; n = n.nextSibling) {
- if (n.nodeName.toLowerCase() == "link") {
- if (n.getAttribute("href").indexOf("//") == 0) {
- n.href = "https:" + n.getAttribute("href");
- }
- }
- }
- }
- </script>
- <xsl:call-template name='do-chapters-script'>
- <xsl:with-param name='cs' select='concat($chapters, " ")'/>
- </xsl:call-template>
- </xsl:copy>
- </xsl:template>
-
- <xsl:template match='@id'>
- <xsl:param name='c'/>
- <xsl:attribute name='id'>
- <xsl:value-of select='concat($c, .)'/>
- </xsl:attribute>
- </xsl:template>
-
- <xsl:template match='h:a/@name'>
- <xsl:param name='c'/>
- <xsl:attribute name='name'>
- <xsl:value-of select='concat($c, .)'/>
- </xsl:attribute>
- </xsl:template>
-
- <xsl:template match='h:a/@href'>
- <xsl:param name='c'/>
- <xsl:attribute name='href'>
- <xsl:choose>
- <xsl:when test='starts-with(., "#")'>
- <xsl:value-of select='concat("#", $c, substring(., 2))'/>
- </xsl:when>
- <xsl:when test='substring-after(substring-before(., "#"), ".") = "html" and not(contains(., "/"))'>
- <xsl:value-of select='concat("#", substring-before(., ".html"), "-", substring-after(., "#"))'/>
- </xsl:when>
- <xsl:when test='contains(concat(" ", $chapters, " "), concat(" ", substring-before(., ".html"), " "))'>
- <xsl:value-of select='concat("#chapter-", substring-before(., ".html"))'/>
- </xsl:when>
- <xsl:when test='contains(concat(" ", $chapters, " "), concat(" ", ., " "))'>
- <xsl:value-of select='concat("#chapter-", .)'/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:value-of select='.'/>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:attribute>
- </xsl:template>
-
- <xsl:template match='@aria-describedby'>
- <xsl:param name='c'/>
- <xsl:attribute name='aria-describedby'>
- <xsl:value-of select='concat($c, .)'/>
- </xsl:attribute>
- </xsl:template>
-
- <xsl:template match='h:script[@src="style/expanders.js"]'/>
- <xsl:template match='h:script[@data-script-mathjax]'/>
-
- <xsl:template match='node()|@*'>
- <xsl:param name='c'/>
- <xsl:copy>
- <xsl:apply-templates select="@*">
- <xsl:with-param name='c' select='$c'/>
- </xsl:apply-templates>
- <xsl:apply-templates>
- <xsl:with-param name='c' select='$c'/>
- </xsl:apply-templates>
- </xsl:copy>
- </xsl:template>
-
- <xsl:template name='do-chapters'>
- <xsl:param name='cs'/>
- <xsl:variable name='c' select='concat(substring-before($cs, " "), "-")'/>
- <xsl:if test='$c != "" and $c != "-"'>
- <hr class='chapter-divider'/>
- <xsl:text>
</xsl:text>
- <div id='chapter-{substring($c, 1, string-length($c) - 1)}'>
- <xsl:apply-templates select='document(concat($publish, "/", substring($c, 1, string-length($c) - 1), ".html"))/h:html/h:body/node()'>
- <xsl:with-param name='c' select='$c'/>
- </xsl:apply-templates>
- </div>
- <xsl:call-template name='do-chapters'>
- <xsl:with-param name='cs' select='substring-after($cs, " ")'/>
- </xsl:call-template>
- </xsl:if>
- </xsl:template>
-
- <xsl:template name='do-chapters-style'>
- <xsl:param name='cs'/>
- <xsl:variable name='c' select='concat(substring-before($cs, " "), "-")'/>
- <xsl:if test='$c != "" and $c != "-"'>
- <xsl:apply-templates select='document(concat($publish, "/", substring($c, 1, string-length($c) - 1), ".html"))/h:html/h:head/h:style'/>
- <xsl:call-template name='do-chapters-style'>
- <xsl:with-param name='cs' select='substring-after($cs, " ")'/>
- </xsl:call-template>
- </xsl:if>
- </xsl:template>
-
- <xsl:template name='do-chapters-script'>
- <xsl:param name='cs'/>
- <xsl:variable name='c' select='concat(substring-before($cs, " "), "-")'/>
- <xsl:if test='$c != "" and $c != "-"'>
- <xsl:variable name="mathjax" select='document(concat($publish, "/", substring($c, 1, string-length($c) - 1), ".html"))/h:html/h:head/h:script[@data-script-mathjax]'/>
- <xsl:choose>
- <xsl:when test="count($mathjax) != 0">
- <xsl:copy-of select="$mathjax[1]"/>
- </xsl:when>
- <xsl:otherwise>
- <xsl:call-template name='do-chapters-script'>
- <xsl:with-param name='cs' select='substring-after($cs, " ")'/>
- </xsl:call-template>
- </xsl:otherwise>
- </xsl:choose>
- </xsl:if>
- </xsl:template>
-</xsl:stylesheet>