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