Created
October 28, 2013 16:51
-
-
Save bolinfest/7200441 to your computer and use it in GitHub Desktop.
This is the script that I used to convert Appendix B of Closure: The Definitive Guide from the DocBook XML from the Manuscript to HTML.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cgi | |
import re | |
import xml.dom.minidom | |
# Omit trailing slash. | |
HOST = 'http://www.bolinfest.com' | |
STYLE = """ | |
body { | |
width: 825px; | |
margin: 5px auto; | |
} | |
body, textarea { | |
font-family: Arial; | |
font-size: 13px; | |
} | |
h1 { | |
font-size: 1.5em; | |
} | |
h2 { | |
font-size: 15px; | |
} | |
code, pre { | |
color: green; | |
} | |
pre.prettyprint { | |
border-style: none; | |
} | |
.cursor, .character-offset { | |
color: red; | |
} | |
.cursor, .cursor1, .cursor2 { | |
font-weight: bold; | |
} | |
.cursor1 { | |
color: red; | |
} | |
.cursor2 { | |
color: blue; | |
} | |
.figure { | |
text-align: center; | |
} | |
.caption { | |
margin-top: 16px; | |
width: 500px; | |
display: inline-block; | |
font-style: italic; | |
} | |
.aside { | |
padding: 8px; | |
background-color: #FFFCCC; | |
border-radius: 4px; | |
} | |
.wrapping-example { | |
height: 36px; | |
width: 190px; | |
} | |
blockquote, .wrapping-example { | |
margin-left: 40px; | |
} | |
.has-byline { | |
margin: 0; | |
} | |
.byline { | |
margin-bottom: 1em; | |
font-style: italic; | |
} | |
.article { | |
width: 600px; | |
} | |
#info-column { | |
float: right; | |
width: 225px; | |
} | |
#abstract, #toc { | |
padding-bottom: 8px; | |
} | |
.cover-container { | |
font-size: 11px; | |
} | |
.cover-container, .sidebar { | |
margin-left: 30px; | |
} | |
.cover, .sidebar { | |
border: 1px solid #ccc; | |
width: 194px; | |
} | |
.sidebar { | |
background-color: #f7f7f7; | |
} | |
.sidebar a { | |
color: #1C51A8; | |
font-size: 11px; | |
text-decoration: none; | |
} | |
.sidebar a:hover { | |
text-decoration: underline; | |
} | |
.sidebar-header { | |
font-weight: bold; | |
border-bottom: 1px solid #ccc; | |
padding-bottom: 2px; | |
} | |
.sidebar-header-inner { | |
padding: 2px 0 2px 4px; | |
} | |
.sidebar-content { | |
padding: 4px; | |
} | |
.sidebar-prose { | |
font-size: 11px; | |
} | |
.sidebar-list { | |
margin: 0; | |
padding-left: 1em; | |
list-style-position: inside; | |
text-indent: -1em; | |
} | |
""" | |
ABSTRACT = """ | |
<div id="abstract"> | |
<div class="sidebar"> | |
<div class="sidebar-header"> | |
<div class="sidebar-header-inner">Abstract</div> | |
</div> | |
<div class="sidebar-content"> | |
<div class="sidebar-prose">%s</div> | |
</div> | |
</div> | |
</div> | |
""" | |
ABSTRACT_TEXT = """ | |
Discusses common misconceptions about JavaScript that trip up developers, | |
both old and new. | |
""" | |
TOC = """ | |
<div id="toc"> | |
<div class="sidebar"> | |
<div class="sidebar-header"> | |
<div class="sidebar-header-inner">Table of contents</div> | |
</div> | |
<div class="sidebar-content"> | |
<div> | |
<ul class="sidebar-list"> | |
%s | |
</ul> | |
</div> | |
</div> | |
</div> | |
</div> | |
""" | |
PROMO = """ | |
<div id="promo"> | |
<div class="cover-container"> | |
This article is an excerpt from | |
<em> | |
<a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&tag=bolinfestcom-20&link_code=as3&camp=211189&creative=373489&creativeASIN=1449381871" target="_blank">Closure: The Definitive Guide</a></em>, which discusses many aspects of JavaScript in the course of detailing how to leverage | |
the Google Closure Tools to build large-scale JavaScript applications. | |
<p> | |
<a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&tag=bolinfestcom-20&link_code=as3&camp=211189&creative=373489&creativeASIN=1449381871" target="_blank"><img class="cover" src="http://bolinfest.com/plovr/cover_big.png"></a> | |
</p> | |
</div> | |
</div> | |
""" | |
INTRO = """ | |
<em>This is a complete "reprint" of Appendix B from my book,</em> | |
</em><a href="http://www.amazon.com/gp/product/1449381871?ie=UTF8&tag=bolinfestcom-20&link_code=as3&camp=211189&creative=373489&creativeASIN=1449381871" target="_blank">Closure: The Definitive Guide</a></em><em>. | |
Even though my book was designed to focus on Closure rather than JavaScript in general, | |
there were a number of pain | |
points in the language that I did not think were covered well in other popular JavaScript books, | |
such as </em><a href="http://www.amazon.com/gp/product/0596517742/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596517742&linkCode=as2&tag=bolinfestcom-20">JavaScript: The Good Parts</a><em> or | |
</em><a href="http://www.amazon.com/gp/product/0596805527/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596805527&linkCode=as2&tag=bolinfestcom-20">JavaScript: The Definitive Guide</a><em>. | |
So that the book did not lose its focus, I relegated this and my essay, | |
<a href="%(host)s/javascript/inheritance.php">"Inheritance Patterns in JavaScript,"</a> | |
to the back of the book among the appendices. | |
However, many people have told me anecdotally that Appendix B | |
was the most valuable part of the book for them, so it seemed like this was worth sharing | |
more broadly in hopes that it helps demystify a language that I have enjoyed so much. | |
</em><hr> | |
""" % {'host': HOST} | |
FOOTER = """ | |
<script type="text/javascript"> | |
var _sf_async_config={uid:22053,domain:"bolinfest.com"}; | |
(function(){ | |
function loadChartbeat() { | |
window._sf_endpt=(new Date()).getTime(); | |
var e = document.createElement('script'); | |
e.setAttribute('language', 'javascript'); | |
e.setAttribute('type', 'text/javascript'); | |
e.setAttribute('src', | |
(("https:" == document.location.protocol) ? "https://a248.e.akamai.net/chartbeat.download.akamai.com/102508/" : "http://static.chartbeat.com/") + | |
"js/chartbeat.js"); | |
document.body.appendChild(e); | |
} | |
var oldonload = window.onload; | |
window.onload = (typeof window.onload != 'function') ? | |
loadChartbeat : function() { oldonload(); loadChartbeat(); }; | |
})(); | |
var _gaq = _gaq || []; | |
_gaq.push(['_setAccount', 'UA-1213368-1']); | |
_gaq.push(['_trackPageview']); | |
(function() { | |
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | |
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | |
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | |
})(); | |
</script> | |
<script src="%(host)s/javascript/prettify/src/prettify.js"></script> | |
<script> | |
prettyPrint(); | |
</script>""" % {'host': HOST} | |
def esc(str): | |
return cgi.escape(str).encode('ascii', 'xmlcharrefreplace') | |
def transform(document, date, abstract): | |
html_chunks = [] | |
sections = [] | |
create_content(document.documentElement, title_count=[0], out=html_chunks, sections=sections) | |
out = '<!doctype html>' | |
out += '<html>' | |
out += '<head>' | |
out += '<script type="text/javascript">var _sf_startpt=(new Date()).getTime()</script>' | |
title = document.getElementsByTagName('title')[0].firstChild.nodeValue | |
out += '<title>%s</title>' % esc(title) | |
out += '<link rel="stylesheet" type="text/css" href="%s/javascript/prettify/src/prettify.css">' % HOST | |
out += '<link rel="SHORTCUT ICON" href="/bolinfest.png">' | |
out += '<style>%s</style>' % STYLE | |
out += '</head>' | |
out += '<body>' | |
out += '<div id="info-column">' | |
out += create_info_column(abstract, sections) | |
out += '</div>' | |
out += '<div id="content">' | |
out += '<div class="article">' | |
out += create_byline(title, date) | |
out += INTRO | |
out += ''.join(html_chunks) | |
out += '</div>' # class="article" | |
out += '</div>' # id="content" | |
out += FOOTER | |
out += '</body>' | |
out += '</html>' | |
return out | |
def create_byline(title, date): | |
return """\ | |
<div> | |
<img src="%(host)s/javascript/Michael_Bolin.png" style="float: right" width="60"> | |
<h1 class="has-byline">%(title)s</h1> | |
<div class="byline">by <a rel="author" href="https://profiles.google.com/u/0/107176920289865686893">Michael Bolin</a>, %(date)s</div> | |
</div> | |
<br>""" % {'host': HOST, 'title': title, 'date': date} | |
def create_info_column(abstract, sections): | |
out = ABSTRACT % abstract | |
items = '' | |
for section in sections: | |
items += '<li><a href="#%s">%s</a></li>' % (section['id'], esc(section['title'])) | |
out += TOC % items | |
out += PROMO | |
return out | |
def create_content(node, title_count, out, sections): | |
tag = None | |
class_name = None | |
id_attr = None | |
node_type = node.nodeType | |
if node_type == xml.dom.minidom.Node.ELEMENT_NODE: | |
node_name = node.nodeName | |
if node_name in ['appendix', 'sect1']: | |
# Ignore these, so do not set `tag`. | |
None | |
elif node_name == 'para': | |
tag = 'p' | |
elif node_name == 'programlisting': | |
tag = 'pre' | |
class_name = 'prettyprint' | |
elif node_name == 'code': | |
tag = 'code' | |
elif node_name == 'emphasis': | |
tag = 'em' | |
elif node_name == 'xref': | |
linkend = node.getAttributeNode('linkend').nodeValue | |
if linkend == 'advanced_compilation': | |
out.append('Chapter 13') | |
elif linkend == 'prototype_chain': | |
out.append('Figure B-1') | |
elif linkend == 'utilities': | |
out.append('Chapter 4') | |
elif linkend == 'goog-global': | |
out.append('"goog.global"') | |
else: | |
raise Exception('Unrecognized linkend: ' + linkend) | |
# <xref> is a leaf node, so just return. | |
return | |
elif node_name == 'title': | |
if title_count[0] >= 2: | |
tag = 'h1' | |
title_text = node.firstChild.nodeValue | |
id_attr = title_text | |
id_attr = re.sub(r'[^A-Za-z]', '-', id_attr) | |
id_attr = re.sub(r'-+', '-', id_attr) | |
id_attr = id_attr.lower() | |
sections.append({"id": id_attr, "title": title_text}) | |
else: | |
# Record that we have a title element. | |
title_count[0] += 1 | |
return | |
elif node_name == 'figure' and node.getAttributeNode('id').nodeValue == 'prototype_chain': | |
out.append('<img src="%(host)s/javascript/appendix_b_box_and_arrow.png" style="width: 600px">' % {'host': HOST}) | |
return | |
else: | |
raise Exception('Unexpected node type: ' + node_name) | |
elif node_type == xml.dom.minidom.Node.TEXT_NODE: | |
out.append(esc(node.nodeValue)) | |
if tag: | |
if class_name: | |
out.append('<%s class="%s">' % (tag, class_name)) | |
elif id_attr: | |
out.append('<%s id="%s">' % (tag, id_attr)) | |
else: | |
out.append('<%s>' % tag) | |
for child in node.childNodes: | |
create_content(child, title_count, out, sections) | |
if tag: | |
out.append('</%s>' % tag) | |
def main(): | |
document = xml.dom.minidom.parse('js_concepts.xml') | |
html = transform(document, date='October 28, 2013', abstract=ABSTRACT_TEXT) | |
print html | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment