svg2-tools: Build the single page version of the spec with the new sc...

details:   https://svgwg.org/hg/svg2-tools/rev/c7d24fd2e63b
branches:  
changeset: 62:c7d24fd2e63b
user:      Cameron McCormack <cam@mcc.id.au>
date:      Fri Sep 14 20:03:34 2012 +1000
description:
Build the single page version of the spec with the new script.

diffstat:

 build.py           |   10 +-
 publish/publish.js |  105 +++++++++++++++++++++++++++++++++++-
 single-page.xsl    |  155 -----------------------------------------------------
 3 files changed, 108 insertions(+), 162 deletions(-)

diffs (356 lines):

diff --git a/build.py b/build.py
--- a/build.py
+++ b/build.py
@@ -173,21 +173,21 @@ if not isfile(single_page):
 else:
   singlePageTime = getmtime(single_page)
   for name in all:
     if getmtime(join(master_dir, name + ".html")) > singlePageTime:
       buildSinglePage = True;
       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':
 
 tocopypaths = []
 for f in tocopy:
   tocopypath = join(master_dir, f)
   if os.path.exists(tocopypath):
     tocopypaths.append(tocopypath)
diff --git a/publish/publish.js b/publish/publish.js
--- a/publish/publish.js
+++ b/publish/publish.js
@@ -1,37 +1,42 @@
 var config = require('./config.js'),
     namespaces = require('./namespaces.js'),
     utils = require('./utils.js'),
     processing = require('./processing.js'),
-    fs = require('fs');
+    fs = require('fs'),
+    DOMParser = require('xmldom').DOMParser;
 
 var conf;
 
 function syntax() {
   console.error('Usage: node publish.js options');
   console.error('');
   console.error('Options:');
   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.');
 }
 
 function parseOptions() {
   var opts = { rest: [] };
   for (var i = 2; i < process.argv.length; i++) {
     switch (process.argv[i]) {
       case '--list-pages':
         opts.listPages = true;
         break;
       case '--build':
         opts.build = true;
         break;
+      case '--build-single-page':
+        opts.buildSinglePage = true;
+        break;
       case '--local-style':
         opts.localStyle = true;
         break;
       case '--help':
         opts.help = true;
         break;
       default:
         if (process.argv[i][0] == '-') {
@@ -86,21 +91,117 @@ function buildPage(page) {
    processing.addSectionNumbers,
    processing.formatQuotes,
    // processing.formatIDL,
    processing.formatMarkup].forEach(function(fn) { fn(conf, page, doc); });
 
   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 {
   conf = config.load('publish.xml');
   conf.localStyleSheets = opts.localStyle;
   if (opts.listPages) {
     listPages();
   } else if (opts.build) {
     buildPages(opts.rest);
+  } else if (opts.buildSinglePage) {
+    buildSinglePage();
   }
 }
diff --git a/single-page.xsl b/single-page.xsl
deleted file mode 100644
--- a/single-page.xsl
+++ /dev/null
@@ -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>&#xa;</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>

Received on Friday, 14 September 2012 10:03:34 UTC