<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Weston Ruter</title>
	
	<link>http://weston.ruter.net</link>
	<description>Web application developer in Portland, Oregon</description>
	<lastBuildDate>Wed, 02 May 2012 13:10:42 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/westonruter" /><feedburner:info uri="westonruter" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:browserFriendly></feedburner:browserFriendly><item>
		<title>Showing support for JavaScript and the Mozilla Developer Network</title>
		<link>http://weston.ruter.net/2010/10/04/showing-support-for-javascript-and-the-mozilla-developer-network/</link>
		<comments>http://weston.ruter.net/2010/10/04/showing-support-for-javascript-and-the-mozilla-developer-network/#comments</comments>
		<pubDate>Tue, 05 Oct 2010 05:11:58 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?p=344</guid>
		<description><![CDATA[If you want to see access to JavaScript documentation improve on the Web, grab a copy of the code above from PromoteJS to boost the MDN PageRank. Dare I say, we must take the top spots away from W3Schools.]]></description>
			<content:encoded><![CDATA[<p><a title="JavaScript Array .filter" href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array"><img src="http://static.jsconf.us/promotejsh.gif" alt="JavaScript Array .filter" width="180" height="150" /></a></p>
<p>If you want to see access to JavaScript documentation improve on the Web, grab a copy of the code above from <a href="http://promotejs.com/">PromoteJS</a> to boost the <a href="https://developer.mozilla.org/"><abbr title="Mozilla Developer Network">MDN</abbr></a> PageRank. Dare I say, we must take the top spots away from W3Schools.</p>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/2010/10/04/showing-support-for-javascript-and-the-mozilla-developer-network/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>My X-Team Superhero Identity Revealed</title>
		<link>http://weston.ruter.net/2010/09/01/my-x-team-superhero-identity-revealed/</link>
		<comments>http://weston.ruter.net/2010/09/01/my-x-team-superhero-identity-revealed/#comments</comments>
		<pubDate>Wed, 01 Sep 2010 07:57:07 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?p=321</guid>
		<description><![CDATA[Tonight we at X-Team had a very special meeting: the induction ceremony for new superheroes! X-Team employees are given superhero identities, and I was next in line for induction along with Wojtek Zając and Ben King. Our meeting was extra special because five of us have been together in Los Angeles this month working for [...]]]></description>
			<content:encoded><![CDATA[<p>Tonight we at <a href="http://x-team.com/">X-Team</a> had a very special meeting: the induction ceremony for new superheroes! X-Team employees are given superhero identities, and I was next in line for induction along with <a href="http://wojtekzajac.com/">Wojtek Zając</a> and <a href="http://webkingdesign.com/">Ben King</a>. Our meeting was extra special because five of us have been together in Los Angeles this month working for a client on-site, and so we were able to meet in person while we logged in to our chat room to join up with everyone else around the world. So my superhero identity is:</p>
<h3><img class="alignnone size-medium wp-image-322" title="Thunderboy" src="http://weston.ruter.net/wp-content/uploads/2010/08/thunderboy-title-300x54.png" alt="Thunderboy" width="300" height="54" /></h3>
<p><a title="View full size" href="http://www.gravatar.com/avatar/ac01fb365e479280ccc707153bc71e2a?s=600"><img class="alignright" style="margin-left: 10px; margin-bottom: 10px;" src="http://www.gravatar.com/avatar/ac01fb365e479280ccc707153bc71e2a?s=200" alt="Thunderboy" width="200" height="200" /></a><em>(Weston) is a highly intelligent and technologically resourceful elemental of Nordic descent who can manipulate, control weather and transcommunicate (via electronic voice) through his thundercloud static field. With his static field, he also is able to create lightning bolts and move within them, allowing him to travel at impressive speed.</em></p>
<p>The name is extra meaningful as it was the superhero name that I went by as a kid, so things have come full circle! Along with my name and description, I was also given a badge depicting my superhero self (right), and incidentally my mask actually looks quite similar to Condorman&#8217;s, my favorite old superhero movie. Thanks so much to my coworkers at X-Team for the effort that went into this and for making me feel so part of the team. It&#8217;s truly an honor to be working with you, such a talented international team!</p>
<p>Here&#8217;s the slightly-abridged transcript of our chat for the induction with the unveiling of my superhero:</p>
<blockquote><p><cite>tine</cite>: &#8230;and we have one more superhero to induct today.<br />
<cite>tine</cite>: this is someone who was a superhero coming in, but sealed his superhero status within the first week of working with us.<br />
<cite>tine</cite>: someone who&#8217;s taking us all by STORM and made a BIG, BIG impression.<br />
<cite>tine</cite>: for this superhero, we travel back to the U.S, and to the Westcoast..and we welcome&#8230;<br />
<img class="alignnone size-thumbnail wp-image-326" title="snapshot of tine.jpg 30 kB Tue Aug 31 2010 17:31:01 GMT-0700 (PST)" src="http://weston.ruter.net/wp-content/uploads/2010/08/tine_snapshot_1.28330106136029005051e+09-150x150.jpg" alt="" width="150" height="150" /><br />
<cite>weston</cite>: yay!<br />
<em>benking gave weston a high five!</em><br />
<em>david gave weston a high five!</em><br />
<em>tine gave weston a high five!</em><br />
<em>adi gave weston a high five!</em><br />
<em>marconi gave weston a high five!</em><br />
<em>hz gave weston a high five!</em><br />
<em>dargaud gave weston a high five!</em><br />
<em>wojtek gave weston a high five!</em><br />
<em>vio gave weston a high five!</em><br />
<cite>tine</cite>: @weston, you shall now be known as&#8230;<br />
<cite>tine</cite>: DRUMROLL PLEASE<br />
<cite>marconi</cite>: druuuuuuummmmmuuummmmummm<br />
<cite>tine</cite>: daruuuuuumdrummmmmmmdrummmm<br />
<cite>wojtek</cite>: come on Tine we&#8217;re drumrolling here <img src='http://weston.ruter.net/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /><br />
<cite>tine</cite>: druuuuuuuuuuuuuuuumdarrrummm<br />
<cite>tine</cite>: drrrrrrrrrrr<br />
<cite>tine</cite>: uuuuuuuuum<br />
<cite>tine</cite>: ruum<br />
<cite>wojtek</cite>: Tine!!! <img src='http://weston.ruter.net/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /><br />
<em>Benking faints with anxiety</em><br />
<cite>tine</cite>: Thunderboy<br />
<cite>wojtek</cite>: yay <img src='http://weston.ruter.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /><br />
<cite>marconi</cite>: la la la la la la Thunder!<br />
<cite>tine</cite>: Thunderboy is a highly intelligent and technologically resourceful elemental of Nordic decent who can manipulate, control weather and transcommunicate (via electronic voice) through his thundercloud static field. With his static field, he also is able to create lightning bolts and move within them, allowing him to travel at impressive speeds.<br />
<cite>weston</cite>: That&#8217;s the superhero name I had as a kid <img src='http://weston.ruter.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /><br />
<img src="http://www.gravatar.com/avatar/ac01fb365e479280ccc707153bc71e2a?s=48" alt="Thunderboy" /><br />
<em>vio gave weston a high five!</em><br />
<em>marconi gave weston a high five!</em><br />
<cite>benking</cite>: woe nice one weston<br />
<em>josh gave weston a high five!</em><br />
<cite>josh</cite>: nice!!<br />
<em>adi gave weston a high five!</em><br />
<em>hz gave weston a high five!</em><br />
<em>tine gave weston a high five!</em><br />
<cite>adi</cite>: congrats @weston<br />
<cite>tine</cite>: Welcome to the league, @weston!<br />
<cite>weston</cite>: Awesome, I like it!<br />
<cite>weston</cite>: Thanks all!<br />
<em>marconi can&#8217;t wait to see the new superheroes with their new avatars <img src='http://weston.ruter.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </em><br />
<cite>tine</cite>: @wojtek, @benking, @weston &#8211; you are all amazing, and well-deserving of your new superhero statuses. Wield your new powers with care, but put them to good use!<br />
<cite>dave</cite>: Welcome!!! The league has gone to a whole new level with these new heroes!</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/2010/09/01/my-x-team-superhero-identity-revealed/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>ECMAScript Proposal: Named Function Parameters</title>
		<link>http://weston.ruter.net/2010/05/30/ecmascript-proposal-named-parameters/</link>
		<comments>http://weston.ruter.net/2010/05/30/ecmascript-proposal-named-parameters/#comments</comments>
		<pubDate>Mon, 31 May 2010 04:06:18 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?p=310</guid>
		<description><![CDATA[I recently ran across the ES wiki which is documenting proposals and features for new versions of ECMAScript (JavaScript). I was excited to see the spread operator &#8220;...&#8221; which basically brings Perl-style lists to JavaScript. I was also excited to see the spread-related rest parameters which basically implement Python&#8217;s positional parameter glob *args; however, I [...]]]></description>
			<content:encoded><![CDATA[<p>I recently ran across the <a href="http://wiki.ecmascript.org/doku.php"><abbr title="ECMAScript">ES</abbr> wiki</a> which is documenting proposals and features for new versions of ECMAScript (JavaScript). I was excited to see the  <a href="http://wiki.ecmascript.org/doku.php?id=harmony:spread">spread operator</a> &#8220;<code>...</code>&#8221;  which basically brings Perl-style lists to JavaScript. I was also excited to see the spread-related <a href="http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters">rest parameters</a> which  basically implement Python&#8217;s positional parameter glob <code>*args</code>; however, I did not see something equivalent to Python&#8217;s named parameter glob <code>**kwargs</code> (see on <a title="5.3.4. Call | Python Expressions" href="http://docs.python.org/reference/expressions.html#calls">Python Docs</a>).</p>
<p>I&#8217;ve been giving thought to passing  in named arguments to function calls in JavaScript, eliminating the need for the current pattern of wrapping named arguments in an object, like:</p>
<blockquote>
<pre><code>function foo(kwargs){
    if(kwargs.z === undefined)
        kwargs.z = 3; //default value
    return [kwargs.x, kwargs.y, kwargs.z];
}
foo({z:3, y:2, x:1}); //=&gt; [1, 2, 3]
</code></pre>
</blockquote>
<p>Instead of this, I&#8217;d love to be able discretely define what arguments I&#8217;m expecting in a positional list, but then also allow them to be passed in as named arguments:</p>
<blockquote>
<pre><code>function foo(x, y, z){
    return [x, y, z];
}
foo(1, 2, 3) === foo(z:3, x:1, y:2); //=&gt; [1, 2, 3]
</code></pre>
</blockquote>
<p>This could also work with the ES proposal for <a href="http://wiki.ecmascript.org/doku.php?id=harmony:parameter_default_values">parameter default values</a>:</p>
<blockquote>
<pre><code>function foo(x, y, z = 3){
    return [x, y, z];
}
foo(1, 2) === foo(y:2, x:1); //=&gt; [1, 2, 3]
</code></pre>
</blockquote>
<p>Although the current <cite>proposal</cite> has a requirement that <q cite="http://wiki.ecmascript.org/doku.php?id=harmony:parameter_default_values">only trailing formal parameters may have default values specified</q>, this shouldn&#8217;t be necessary when named parameters are used:</p>
<blockquote>
<pre><code>function foo(x = 1, y, z){
    return [x, y, z];
}
foo(z:3, y:2); //=&gt; [1, 2, 3]
</code></pre>
</blockquote>
<p>Furthermore, although the current spread proposal only works with arrays, it could also work with objects for named parameters:</p>
<blockquote>
<pre><code>var kwargs = {y:2, x:1, z:3};
foo(...kwargs); //=&gt; [1,2,3]</code></pre>
</blockquote>
<p>Finally, the rest parameters proposal uses spread to implement the rough equivalent of Python&#8217;s positional parameter glob (<code>*args</code>), but I&#8217;m not sure how it could also be applied to named parameters to support  Python&#8217;s <code>**kwargs</code> at the same time—I&#8217;m not sure how named rest parameters would work. For example in Python:</p>
<blockquote>
<pre><code>def foo(x, *args, **kwargs):
    return [x, args[0], kwargs['z']]
foo(1, 2, z=3); //=&gt; [1, 2, 3]
</code></pre>
</blockquote>
<p>Python has the <code>*</code> to prefix positional parameter globs and <code>**</code> to prefix named parameter globs, but so far ECMAScript only has one prefix the &#8220;<code>...</code>&#8221; spread operator.</p>
<p>These are just some rough ideas about how JavaScript could support named parameters. I&#8217;m sure there things I&#8217;ve missed and implications I haven&#8217;t thought of, but I wanted to get my thoughts out there. What do you think?</p>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/2010/05/30/ecmascript-proposal-named-parameters/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Programming Languages I’ve Learned In Order</title>
		<link>http://weston.ruter.net/2010/04/02/programming-languages-ive-learned-in-order/</link>
		<comments>http://weston.ruter.net/2010/04/02/programming-languages-ive-learned-in-order/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 19:52:43 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?p=297</guid>
		<description><![CDATA[Update: See also list on MY TECHNE. What follows are the programming languages I&#8217;ve learned in the order of learning them; their relative importance is marked up with big, and small indicates I didn&#8217;t fully learn or actually use the language. Perl 5 JavaScript / ECMAScript PHP 4 &#38; 5 SQL Visual Basic 6 Java [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Update:</strong> See also <a title="Programming languages used by Weston Ruter on MY TECHNE" href="http://mytechne.com/user/westonruter/" target="_blank">list</a> on <a href="http://mytechne.com/" target="_blank">MY TECHNE</a>.</p>
<p>What follows are the programming languages I&#8217;ve learned in the order of learning them; their relative importance is marked up with <code>big</code>, and <code>small</code> indicates I didn&#8217;t fully learn or actually use the language.</p>
<ol>
<li><big>Perl 5</big></li>
<li><big><big>JavaScript / ECMAScript</big></big></li>
<li><big><big>PHP 4 &amp; 5</big></big></li>
<li><big>SQL</big></li>
<li>Visual Basic 6</li>
<li><small>Java</small></li>
<li>Classic ASP: VBScript &amp; JScript</li>
<li>(Visual) C/C++</li>
<li>XSLT</li>
<li><small>Ruby</small></li>
<li><big>Python</big> <small>(I expect/hope that this will supplant PHP in the next couple years, getting an extra <code>big</code> or two.)</small></li>
</ol>
<p>While Python is now my server-side language of choice, I would be much happier to use JavaScript end-to-end. Thanks to the <a href="http://wiki.commonjs.org/wiki/CommonJS" target="_blank">CommonJS</a> initiative, this is becoming a reality.</p>
<p>Not included in the list above are markup languages and other related technologies: (X)HTML 4 &amp; 5, XML, CSS, DOM, RSS, Atom, RDF, XML Schema, XPath, JSON, JSON/XML-RPC, SVG, VML, OSIS, iCal, Microformats, MathML, etc.</p>
<p>Idea <a title="James Tauber: Programming Languages I've Learned In  Order" href="http://jtauber.com/blog/2008/09/28/programming_languages_i%27ve_learned_in_order/" target="_blank">via</a> and <a title="jtauber: now might be a good  opportunity to encourage people to do a blog post like this:  http://bit.ly/bqD0LE  and post a link" href="http://twitter.com/jtauber/status/11497201689" target="_blank">prompted</a> by James Tauber, via <a href="http://blog.dougalmatthews.com/2008/09/languages-ive-learned-in-order/" target="_blank">Dougal Matthews</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/2010/04/02/programming-languages-ive-learned-in-order/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Proposal for Customizing Google’s Crawlable Ajax URLs</title>
		<link>http://weston.ruter.net/2010/03/17/proposal-for-customizing-google-crawlable-ajax-urls/</link>
		<comments>http://weston.ruter.net/2010/03/17/proposal-for-customizing-google-crawlable-ajax-urls/#comments</comments>
		<pubDate>Wed, 17 Mar 2010 18:17:54 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?p=283</guid>
		<description><![CDATA[On the Shepherd Interactive site, we have a dynamic navigation menu in Flash. In order to prevent it from having to reload each time a page is changed, I implemented Ajax loading so that the SWF only has to load once. This is similar to what Lala and Facebook do. So if your browser is [...]]]></description>
			<content:encoded><![CDATA[<p>On the <a href="http://shepherdinteractive.com/" target="_blank">Shepherd Interactive</a> site, we have a dynamic navigation menu in Flash. In order to prevent it from having to reload each time a page is changed, I implemented Ajax loading so that the SWF only has to load once. This is similar to what Lala and Facebook do. So if your browser is Ajax-enabled, upon visiting:</p>
<blockquote><p><code>http://shepherdinteractive.com/portfolio/interactive/</code></p></blockquote>
<p>You will get redirected to site root <code>/</code> with the old path supplied as the URL hash fragment which is then loaded in via JavaScript as the page content:</p>
<blockquote><p><code>http://shepherdinteractive.com/<strong style="background: #FFFFCC;">#</strong>portfolio/interactive/</code></p></blockquote>
<p>However, according to Google&#8217;s <a href="http://code.google.com/web/ajaxcrawling/" target="_blank">Making AJAX Applications Crawlable</a> specification, Ajax pretty URLs are any <q cite="http://code.google.com/web/ajaxcrawling/docs/specification.html">containing a hash fragment beginning with <code>!</code></q>, for example:</p>
<blockquote><p><code>http://shepherdinteractive.com/<strong style="background: #FFFFCC;">#!</strong>portfolio/interactive/</code></p></blockquote>
<p>The purpose of the <code>!</code> is merely to inform Googlebot that such a URL is for an Ajax page whose content can be fetched via:</p>
<blockquote><p><code>http://shepherdinteractive.com/<strong style="background: #FFFFCC;">?_escaped_fragment_=</strong>portfolio/interactive/</code></p></blockquote>
<p>The problem I have with Google&#8217;s specification is that the pretty URL Ajax fragment prefix (<code>!</code>) is mandated; it is not customizable. I should be able to tell Googlebot which fragment identifiers are for Ajax content and which are not. Therefore, instead of requiring authors to to conform to Google&#8217;s Ajax specification, I propose that Google adopt an extension to robots.txt which allows site owners to let Googlebot know what to look for. The current specification&#8217;s mandate for <code>!</code> could be indicated via:</p>
<blockquote><p><code>Ajax-Fragment-Prefix: !</code></p></blockquote>
<p>Or it could be changed to anything else, such as &#8220;<code>ajax:</code>&#8220;. If the Ajax fragment doesn&#8217;t have a prefix at all (as in the case of Shepherd Interactive&#8217;s website above), a regular expression pattern match could be specified in robots.txt, for example:</p>
<blockquote><p><code>Ajax-Fragment-Pattern: .+/</code></p></blockquote>
<p>This would tell Googlebot that a URL with a fragment containing a slash should be fetched via the <code>_escaped_fragment_</code> query parameter, and that the Ajax URL itself (including the fragment identifier) should be indexed and returned verbatim in the search results.</p>
<p>It&#8217;s true that the Shepherd Interactive site implements Hijax (progressive enhancement with Ajax) techniques, so every Ajax URL has a corresponding non-Ajax URL; so in this sense, Google can still access all of the content. The problem, however, is with links to Ajax URLs from around the Web. I assume for Googlebot, every link to an Ajax URL without the obligatory prefix <code>!</code> is interpreted as referring to the home page (site root <code>/</code>):</p>
<ul>
<li><code>http://shepherdinteractive.com/#portfolio/interactive/harmer-steel/</code></li>
<li><code>http://shepherdinteractive.com/#about-us/our-company/</code></li>
<li><code>http://shepherdinteractive.com/#services/web-development/</code></li>
</ul>
<p>So Google then assigns no additional PageRank to our Ajax URLs since it gets assigned to the home page. If, however, Googlebot could be told that those are actually Ajax URLs, then the PageRank could be properly assigned. (My assumptions here could be incorrect.)</p>
<p>Thoughts?</p>
<p><strong id="update-2010-04-22">Update 2010-04-22:</strong> “Cowboy” Ben Alman brought up the <a title="Comment by “Cowboy” Ben Alman" href="http://weston.ruter.net/2010/03/17/proposal-for-customizing-google-crawlable-ajax-urls/#comment-16406">excellent point</a> that customizable Ajax-crawlable URLs need to <q cite="http://weston.ruter.net/2010/03/17/proposal-for-customizing-google-crawlable-ajax-urls/#comment-16406">work on a per-path basis, as different single page web apps on the same server (or even in the same folder) might have different URI requirements.</q> I wonder if in addition to (or instead of) using robots.txt to tell Googlebot the format of the Ajax-crawlable URLs, why not allow that information to be placed in <code>meta</code> tags? Their specification already includes:</p>
<blockquote><p><code>&lt;meta name="fragment" content="!"&gt;</code></p></blockquote>
<p>Why not make Googlebot support additional <code>meta</code> tags for recognizing the prefix (if any) or pattern that characterizes an Ajax-crawlable URL on a given page? For example:</p>
<dl>
<dt>Current default behavior:</dt>
<dd><code>&lt;meta name="crawlable-fragment-prefix" content="!"&gt;</code></dd>
<dt>Starting with “/” like <code>…about/#/us/</code>:</dt>
<dd><code>&lt;meta name="crawlable-fragment-prefix" content="/"&gt;</code></dd>
<dt>All fragments are crawlable:</dt>
<dd><code>&lt;meta name="crawlable-fragment-prefix" content=""&gt;</code></dd>
<dt>Any fragment ending with a slash:</dt>
<dd><code>&lt;meta name="crawlable-fragment-pattern" content=".+/"&gt;</code></dd>
</dl>
<p>With these <code>meta</code> tags, authors would be able to have complete page-level control over the structure of Ajax-crawlable URLs (hash fragments).</p>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/2010/03/17/proposal-for-customizing-google-crawlable-ajax-urls/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Photo of “Audible Ajax”</title>
		<link>http://weston.ruter.net/2010/02/04/audible-ajax-photo/</link>
		<comments>http://weston.ruter.net/2010/02/04/audible-ajax-photo/#comments</comments>
		<pubDate>Thu, 04 Feb 2010 19:18:15 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Photo]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?p=277</guid>
		<description><![CDATA[I miss the Audible Ajax podcast on Ajaxian. Photo by my friend Nathan Watkins; taken 2006-07-17 in Midelt, Morocco where I was soaking in the podcast.]]></description>
			<content:encoded><![CDATA[<p><a href="http://weston.ruter.net/wp-content/uploads/2010/02/audible-ajax.jpg"><img class="alignnone size-large wp-image-278" title="Weston Ruter and Audible Ajax" src="http://weston.ruter.net/wp-content/uploads/2010/02/audible-ajax-700x466.jpg" alt="Weston Ruter and Audible Ajax" width="700" height="466" /></a></p>
<p>I miss the <a href="http://ajaxian.com/by/category/podcasts">Audible Ajax</a> podcast on <a href="http://ajaxian.com/">Ajaxian</a>. Photo by my friend <a href="http://nathanwatkinsphotography.com/">Nathan Watkins</a>; taken 2006-07-17 in <a href="http://en.wikipedia.org/wiki/Midelt">Midelt, Morocco</a> where I was soaking in the podcast.</p>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/2010/02/04/audible-ajax-photo/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>We’re Having a Baby Boy!</title>
		<link>http://weston.ruter.net/2010/01/24/were-having-a-baby-boy/</link>
		<comments>http://weston.ruter.net/2010/01/24/were-having-a-baby-boy/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 22:02:02 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[News]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?p=263</guid>
		<description><![CDATA[Five months in, I&#8217;m pleased to publicly announce that my wife and I are expecting a baby boy at the end of May! His name is “Asecret”]]></description>
			<content:encoded><![CDATA[<p><abbr title="23 weeks">Five months</abbr> in, I&#8217;m pleased to publicly announce that my wife and I are expecting a baby boy at the end of May! His name is “Asecret” <img src='http://weston.ruter.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p><img class="alignnone size-full wp-image-272" title="Baby Boy Ruter profile pic taken 2009-12-18 08:39:43" src="http://weston.ruter.net/wp-content/uploads/2010/01/20091218083943-baby-boy-ruter-profile.jpg" alt="Baby Boy Ruter profile pic taken 2009-12-18 08:39:43" width="700" height="566" /></p>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/2010/01/24/were-having-a-baby-boy/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Portland, Oregon Snow Driving</title>
		<link>http://weston.ruter.net/2010/01/24/portland-oregon-snow-driving/</link>
		<comments>http://weston.ruter.net/2010/01/24/portland-oregon-snow-driving/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 21:44:16 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?p=247</guid>
		<description><![CDATA[I took this video outside of Shepherd Interactive, my work. View in HD to see the sparks fly See also the footage of my commute home which ended up being a journey half on foot from my work to downtown: part 1 and part 2. What follows are screengrabs from Google Maps of the traffic [...]]]></description>
			<content:encoded><![CDATA[<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="700" height="425" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="src" value="http://www.youtube.com/v/6Y77fU22FbQ&amp;hl=en_US&amp;fs=1" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="700" height="425" src="http://www.youtube.com/v/6Y77fU22FbQ&amp;hl=en_US&amp;fs=1" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p>I took this video outside of <a href="http://shepherdinteractive.com/">Shepherd Interactive</a>, my work. View in HD to see the sparks fly <img src='http://weston.ruter.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  See also the footage of my commute home which ended up being a journey half on foot from my work to downtown: <a title="Commuting Home in Snow, part 1" href="http://www.youtube.com/watch?v=by3p_twwvM8">part 1</a> and <a title="Commuting Home in Snow, part 2" href="http://www.youtube.com/watch?v=fSStQxWzexI">part 2</a>.</p>
<p>What follows are screengrabs from Google Maps of the traffic around 5pm. One word: gridlock.</p>
<p><img class="alignnone size-large wp-image-249" title="Portland metro rush-hour traffic during snow" src="http://weston.ruter.net/wp-content/uploads/2009/12/portland-metro-traffic-during-snow-8bit-700x534.png" alt="" width="700" height="534" /></p>
<p><img class="alignnone size-large wp-image-248" title="Inner Portland rush-hour traffic during snow" src="http://weston.ruter.net/wp-content/uploads/2009/12/inner-portland-traffic-during-snow-8bit-700x419.png" alt="" width="700" height="419" /></p>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/2010/01/24/portland-oregon-snow-driving/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML5 Audio Read-Along</title>
		<link>http://weston.ruter.net/projects/html5-audio-read-along/</link>
		<comments>http://weston.ruter.net/projects/html5-audio-read-along/#comments</comments>
		<pubDate>Wed, 23 Dec 2009 04:30:09 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?page_id=236</guid>
		<description><![CDATA[Update: I&#8217;ve created a new ESV Text/Audio Aligner project on GitHub which queries the ESV text and audio data from their API and aligns them using the newly-improved CMU Sphinx codebase, which now works better with long audio. It&#8217;s still a work in progress. Jump to demo below to listen while reading along to the [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Update:</strong> I&#8217;ve created a new <a href="https://github.com/westonruter/esv-text-audio-aligner">ESV Text/Audio Aligner</a> project on GitHub which queries the ESV text and audio data from their API and aligns them using the newly-improved CMU Sphinx codebase, which now works better with long audio. It&#8217;s still a work in progress.</p>
<p><em><a href="/projects/html5-audio-read-along/#nativity-story-from-luke">Jump to demo below</a> to listen while reading along to the nativity story from the Gospel of Luke.</em></p>
<p>I guess I&#8217;ve been on an <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#audio" target="_blank">HTML5 Audio</a> kick lately, with this and my <a title="Google Text-To-Speech (TTS)" href="http://weston.ruter.net/projects/google-tts/">previous post</a> on using it to play Google&#8217;s Text-To-Speech (<abbr title="text-to-speech">TTS</abbr>) service on Google Translate. I wrote that post while attempting to build a text-to-speech interface which audibly reads the text on the page while highlighting the currently-spoken word (like my old favorite <a href="http://www.readplease.com/" target="_blank">ReadPlease</a>), but a <a title="Chromium Issue 30242" href="http://code.google.com/p/chromium/issues/detail?id=30242" target="_blank">bug</a> in Google Chrome for Mac discouraged me from finishing it. However, when I saw the inestimable <a href="http://paulirish.com/" target="_blank">Paul Irish</a>&#8216;s <a title="Updates from all around - Dec 2009 « Paul Irish" href="http://paulirish.com/2009/updates-from-all-around-dec-2009/" target="_blank">post</a> about the <a href="http://yayquery.github.com/jquery-singalong/" target="_blank">jquery-singalong</a> plugin which times HTML5 Audio with text and a bouncing ball cue, I was inspired to do the same but for a (non-<abbr title="text-to-speech">TTS</abbr>) <em>read</em>-along.</p>
<p><a href="http://www.esv.org/" target="_blank"><img style="float: right; margin-left: 0.5em; margin-bottom: 1em;" title="Visit www.esv.org to learn about the ESV Bible" src="http://www.esv.org/assets/buttons/esv.seal.png" alt="ESV" width="120" height="35" /></a>The text I chose was the nativity story from the Gospel of Luke in the <a href="http://www.esv.org/" target="_blank">English Standard Version</a> (<abbr title="English Standard Version">ESV</abbr>), not only in keeping with the (true) Christmas spirit but also because the <abbr title="English Standard Version">ESV</abbr> Online has an excellent <a href="http://www.esvapi.org/" target="_blank">API</a> which allows both a passage&#8217;s text and its audio to be queried. With the text and audio in hand, each of the words in the text needs to be time-indexed for its begin time and duration in the corresponding audio. In the past, audio Bibles were divided into chapter segments only and that was as granular as you could go; the ESV team did the <a title="The Development of Verse-Level Audio at the ESV Online Edition" href="http://www.gnpcb.org/esv/share/about/audio/" target="_blank">innovation</a> of taking this granularity down to the verse-level. Unfortunately, however, the granularity is not available at the word-level. Therefore, in order to make this exciting (IMHO) read-along demo work, I manually traversed the audio to find each word&#8217;s begin time and duration (spending <abbr title="four hours!">more time</abbr> than I care to say), and I added these time indicies to the word markup as <code>data-begin</code> and <code>data-<abbr title="duration">dur</abbr></code> attributes, akin to SMIL&#8217;s <a title="SMIL 3.0 smilText: The begin Attribute" href="http://www.w3.org/TR/SMIL3/smil-text.html#adef-textBegin" target="_blank"><code>begin</code></a> and <a title="SMIL 3.0 smilText: dur" href="http://www.w3.org/TR/SMIL3/smil-text.html#q75" target="_blank"><abbr title="duration"><code>dur</code></abbr></a> attributes (which I suppose I could use). I also spruced up the markup for the passage by incorporating some HTML5 constructs as well as adding <a href="http://bibletechnologies.net/" target="_blank">OSIS</a> <code>verse</code> elements as I&#8217;ve been <a title="Standardizing on a Web Infrastructure and Web Service API for Scripture @ Open Scriptures Google Group" href="http://groups.google.com/group/open-scriptures/browse_thread/thread/d7de2e8a9a8fec1b#8ff306598fd5e1a2" target="_blank">talking about</a> over at <a href="http://openscriptures.org/" target="_blank">Open Scriptures</a>.</p>
<p>The read-along demo has been tested in <strong>Firefox 3.5</strong>, <strong>Chrome 4</strong>, and <strong>Safari 4</strong> (as expected, it will not work in Internet Explorer). Chrome plays the MP3 as served from the ESV API. Firefox doesn&#8217;t support MP3 so I include an OGG Vorbis <code>source</code> as well. Safari seems to have trouble playing the MP3 and can&#8217;t play OGG, so I also include an 8kHz WAV <code>source</code> as well.</p>
<p><strong>What it does:</strong> Upon playing the audio, the word corresponding to the one currently being spoken is highlighted <del datetime="2011-06-17">(via DOM <a title="Document Object Model Range" href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html" target="_blank">Range</a>/<a title="Selection - MDC" href="https://developer.mozilla.org/en/DOM/selection" target="_blank">Selection</a>)</del>;<em> this eliminates the need to re-find your place if you momentarily look away.</em> Likewise, when manually adjusting the seek position, the words which correspond to each position sought will be highlighted; and conversely, clicking a word causes the audio to seek to its corresponding position (and double-clicking will then cause it to start playing). <em>Thus the text itself serves as an interface for navigating the audio.</em> See the inline JavaScript source code for all the magic.</p>
<p>Without further adieu, please enjoy listening while reading along to the Christmas story!</p>
<hr />
<article id="nativity-story-from-luke" 
		 xmlns:o="http://www.bibletechnologies.net/2003/OSIS/namespace">
	<style scoped="">
	/*@namespace o "http://www.bibletechnologies.net/2003/OSIS/namespace";
	o|verse[osisID]:before,
	verse[osisID]:before {
		content: " " attr(osisID);
		font-size:smaller;
		vertical-align:super;
	}*/
	#nativity-story-from-luke {
		overflow:hidden;
	}
	#nativity-story-from-luke audio {
		display:block;
		margin-top:1em;
		margin-bottom:1em;
		width:100%;
		font-size:10px;
		background:black;
		color:white;
	}
	#nativity-story-from-luke span[data-begin].speaking {
		background-color:yellow;
	}
	#nativity-story-from-luke span[data-begin] {
		cursor:pointer;
	}
	#nativity-story-from-luke span[data-begin]:hover {
		background-color:#FFFFCA;
	}
	#nativity-story-from-luke .error {
		color:red;
	}
	#nativity-story-from-luke footer {
		line-height:1.1em;
		margin-top:1em;
	}
	html #nativity-story-from-luke h2 { /* for appearance on my site */
		font-size:1.3em;
		margin-top:0;
		padding-top:0.5em;
		margin-bottom:1em;
	}
	</style>
	<!--
	<o:work osisWork="ESV"> 
		<o:title>English Standard Version</o:title> 
		<o:type type="OSIS">Bible</o:type> 
		<o:identifier type="OSIS">Bible.en.Crossway.ESV.2001</o:identifier> 
		<o:scope>Luke.2.1-Luke.2.20</o:scope> 
		<o:refSystem>Bible</o:refSystem> 
	</o:work> 
	<o:work osisWork="bible"> 
		<o:type type="OSIS">Bible</o:type> 
		<o:refSystem>Bible</o:refSystem>
	</o:work>
	-->
	
	<h2><a href="http://www.gnpcb.org/esv/search/?q=Luke+2%3A1-20" title="View Luke 2:1-20 at the ESV Online" target="_blank">Luke 2:1-20</a> (<a href="http://www.esv.org" class="copyright"><abbr title="English Standard Version">ESV</abbr></a>)</h2>
	
	<audio controls="">
		<source src="/projects/html5-audio-read-along/Luke.2.1-Luke.2.20.mp3" type="audio/mp3" />
		<source src="/projects/html5-audio-read-along/Luke.2.1-Luke.2.20.ogg" type="audio/ogg" />
		<source src="/projects/html5-audio-read-along/Luke.2.1-Luke.2.20.wav" type="audio/wav" />
		<em class='error'><strong>Notice:</strong> Your browser doesn't appear to support HTML5 Audio. Please use try Firefox 3.5, Chrome, or Safari.</em>
	</audio>
	<noscript>
		<p class='error'><em><strong>Notice:</strong> You must have JavaScript enabled/available to try this HTML5 Audio read along. If you're
		viewing this in a feed reader, try <a href="http://weston.ruter.net/projects/html5-audio-read-along/#nativity-story-from-luke">viewing outside</a>.</em></p>
	</noscript>
	
	<!--<h3>The Birth of Jesus Christ</h3>-->
	<p><o:verse sID="Luke.2.1" osisID="Luke.2.1"/><span data-dur="0.154" data-begin="0.775">In</span> <span data-dur="0.28" data-begin="0.929">those</span> <span data-dur="0.29" data-begin="1.218">days</span> <span data-dur="0.131" data-begin="1.508">a</span> <span data-dur="0.525" data-begin="1.639">decree</span> <span data-dur="0.191" data-begin="2.165">went</span> <span data-dur="0.225" data-begin="2.355">out</span> <span data-dur="0.245" data-begin="2.583">from</span> <span data-dur="0.438" data-begin="2.828">Caesar</span> <span data-dur="0.637" data-begin="3.267">Augustus</span> <span data-dur="0.166" data-begin="4.03">that</span> <span data-dur="0.268" data-begin="4.216">all</span> <span data-dur="0.111" data-begin="4.486">the</span> <span data-dur="0.411" data-begin="4.594">world</span> <span data-dur="0.205" data-begin="5.006">should</span> <span data-dur="0.134" data-begin="5.211">be</span> <span data-dur="0.529" data-begin="5.344">registered</span>. <o:verse eID="Luke.2.1"/><o:verse sID="Luke.2.2" osisID="Luke.2.2"/><span data-dur="0.201" data-begin="6.675">This</span> <span data-dur="0.124" data-begin="6.876">was</span> <span data-dur="0.11" data-begin="7">the</span> <span data-dur="0.321" data-begin="7.11">first</span> <span data-dur="0.762" data-begin="7.431">registration</span> <span data-dur="0.164" data-begin="8.193">when</span> <span data-dur="0.474" data-begin="8.357">Quirinius</span> <span data-dur="0.206" data-begin="8.834">was</span> <span data-dur="0.338" data-begin="9.041">governor</span> <span data-dur="0.082" data-begin="9.379">of</span> <span data-dur="0.477" data-begin="9.46">Syria</span>. <o:verse eID="Luke.2.2"/><o:verse sID="Luke.2.3" osisID="Luke.2.3"/><span data-dur="0.119" data-begin="10.676">And</span> <span data-dur="0.24" data-begin="10.794">all</span> <span data-dur="0.186" data-begin="11.034">went</span> <span data-dur="0.087" data-begin="11.22">to</span> <span data-dur="0.139" data-begin="11.307">be</span> <span data-dur="0.592" data-begin="11.446">registered</span>, <span data-dur="0.251" data-begin="12.284">each</span> <span data-dur="0.093" data-begin="12.572">to</span> <span data-dur="0.134" data-begin="12.665">his</span> <span data-dur="0.275" data-begin="12.799">own</span> <span data-dur="0.467" data-begin="13.074">town</span>. <o:verse eID="Luke.2.3"/><o:verse sID="Luke.2.4" osisID="Luke.2.4"/><span data-dur="0.184" data-begin="14.369">And</span> <span data-dur="0.358" data-begin="14.553">Joseph</span> <span data-dur="0.351" data-begin="14.911">also</span> <span data-dur="0.128" data-begin="15.262">went</span> <span data-dur="0.152" data-begin="15.39">up</span> <span data-dur="0.215" data-begin="15.595">from</span> <span data-dur="0.541" data-begin="15.811">Galilee</span>, <span data-dur="0.074" data-begin="16.557">from</span> <span data-dur="0.121" data-begin="16.632">the</span> <span data-dur="0.236" data-begin="16.752">town</span> <span data-dur="0.097" data-begin="16.988">of</span> <span data-dur="0.559" data-begin="17.085">Nazareth</span>, <span data-dur="0.154" data-begin="17.966">to</span> <span data-dur="0.575" data-begin="18.12">Judea</span>, <span data-dur="0.129" data-begin="18.823">to</span> <span data-dur="0.059" data-begin="18.952">the</span> <span data-dur="0.31" data-begin="19.011">city</span> <span data-dur="0.166" data-begin="19.321">of</span> <span data-dur="0.393" data-begin="19.487">David</span>, <span data-dur="0.161" data-begin="20.029">which</span> <span data-dur="0.109" data-begin="20.19">is</span> <span data-dur="0.307" data-begin="20.321">called</span> <span data-dur="0.642" data-begin="20.628">Bethlehem</span>, <span data-dur="0.317" data-begin="21.76">because</span> <span data-dur="0.116" data-begin="22.077">he</span> <span data-dur="0.104" data-begin="22.193">was</span> <span data-dur="0.166" data-begin="22.297">of</span> <span data-dur="0.059" data-begin="22.463">the</span> <span data-dur="0.412" data-begin="22.522">house</span> <span data-dur="0.155" data-begin="22.935">and</span> <span data-dur="0.384" data-begin="23.09">lineage</span> <span data-dur="0.175" data-begin="23.474">of</span> <span data-dur="0.421" data-begin="23.648">David</span>, <o:verse eID="Luke.2.4"/><o:verse sID="Luke.2.5" osisID="Luke.2.5"/><span data-dur="0.127" data-begin="24.714">to</span> <span data-dur="0.172" data-begin="24.84">be</span> <span data-dur="0.53" data-begin="25.013">registered</span> <span data-dur="0.125" data-begin="25.543">with</span> <span data-dur="0.515" data-begin="25.668">Mary</span>, <span data-dur="0.172" data-begin="26.183">his</span> <span data-dur="0.607" data-begin="26.355">betrothed</span>, <span data-dur="0.123" data-begin="27.134">who</span> <span data-dur="0.166" data-begin="27.257">was</span> <span data-dur="0.167" data-begin="27.423">with</span> <span data-dur="0.513" data-begin="27.59">child</span>. <o:verse eID="Luke.2.5"/><o:verse sID="Luke.2.6" osisID="Luke.2.6"/><span data-dur="0.161" data-begin="29.448">And</span> <span data-dur="0.362" data-begin="29.609">while</span> <span data-dur="0.159" data-begin="29.97">they</span> <span data-dur="0.166" data-begin="30.129">were</span> <span data-dur="0.436" data-begin="30.295">there</span>, <span data-dur="0.159" data-begin="31.072">the</span> <span data-dur="0.431" data-begin="31.231">time</span> <span data-dur="0.277" data-begin="31.662">came</span> <span data-dur="0.161" data-begin="31.939">for</span> <span data-dur="0.093" data-begin="32.1">her</span> <span data-dur="0.107" data-begin="32.193">to</span> <span data-dur="0.233" data-begin="32.299">give</span> <span data-dur="0.352" data-begin="32.522">birth</span>. <o:verse eID="Luke.2.6"/><o:verse sID="Luke.2.7" osisID="Luke.2.7"/><span data-dur="0.133" data-begin="33.972">And</span> <span data-dur="0.213" data-begin="34.105">she</span> <span data-dur="0.277" data-begin="34.318">gave</span> <span data-dur="0.253" data-begin="34.596">birth</span> <span data-dur="0.069" data-begin="34.888">to</span> <span data-dur="0.171" data-begin="34.957">her</span> <span data-dur="0.602" data-begin="35.128">firstborn</span> <span data-dur="0.56" data-begin="35.73">son</span> <span data-dur="0.166" data-begin="36.491">and</span> <span data-dur="0.342" data-begin="36.657">wrapped</span> <span data-dur="0.153" data-begin="36.998">him</span> <span data-dur="0.119" data-begin="37.152">in</span> <span data-dur="0.55" data-begin="37.271">swaddling</span> <span data-dur="0.542" data-begin="37.82">cloths</span> <span data-dur="0.154" data-begin="38.644">and</span> <span data-dur="0.287" data-begin="38.798">laid</span> <span data-dur="0.176" data-begin="39.085">him</span> <span data-dur="0.087" data-begin="39.261">in</span> <span data-dur="0.092" data-begin="39.348">a</span> <span data-dur="0.604" data-begin="39.44">manger</span>, <span data-dur="0.277" data-begin="40.182">because</span> <span data-dur="0.131" data-begin="40.46">there</span> <span data-dur="0.151" data-begin="40.591">was</span> <span data-dur="0.213" data-begin="40.742">no</span> <span data-dur="0.312" data-begin="40.975">place</span> <span data-dur="0.121" data-begin="41.287">for</span> <span data-dur="0.158" data-begin="41.408">them</span> <span data-dur="0.116" data-begin="41.566">in</span> <span data-dur="0.111" data-begin="41.683">the</span> <span data-dur="0.406" data-begin="41.794">inn</span>.<o:verse eID="Luke.2.7"/></p>
	<!--<h3>The Shepherds and the Angels</h3>-->
	<p><o:verse sID="Luke.2.8" osisID="Luke.2.8"/><span data-dur="0.144" data-begin="43.166">And</span> <span data-dur="0.089" data-begin="43.309">in</span> <span data-dur="0.102" data-begin="43.398">the</span> <span data-dur="0.329" data-begin="43.5">same</span> <span data-dur="0.406" data-begin="43.829">region</span> <span data-dur="0.146" data-begin="44.236">there</span> <span data-dur="0.082" data-begin="44.382">were</span> <span data-dur="0.495" data-begin="44.463">shepherds</span> <span data-dur="0.154" data-begin="44.959">out</span> <span data-dur="0.104" data-begin="45.112">in</span> <span data-dur="0.094" data-begin="45.216">the</span> <span data-dur="0.561" data-begin="45.311">field</span>, <span data-dur="0.295" data-begin="45.954">keeping</span> <span data-dur="0.334" data-begin="46.249">watch</span> <span data-dur="0.173" data-begin="46.583">over</span> <span data-dur="0.107" data-begin="46.756">their</span> <span data-dur="0.401" data-begin="46.863">flock</span> <span data-dur="0.124" data-begin="47.264">by</span> <span data-dur="0.374" data-begin="47.388">night</span>. <o:verse eID="Luke.2.8"/><o:verse sID="Luke.2.9" osisID="Luke.2.9"/><span data-dur="0.144" data-begin="48.751">And</span> <span data-dur="0.176" data-begin="48.895">an</span> <span data-dur="0.409" data-begin="49.071">angel</span> <span data-dur="0.109" data-begin="49.479">of</span> <span data-dur="0.134" data-begin="49.588">the</span> <span data-dur="0.233" data-begin="49.722">Lord</span> <span data-dur="0.491" data-begin="49.955">appeared</span> <span data-dur="0.107" data-begin="50.446">to</span> <span data-dur="0.31" data-begin="50.553">them</span>, <span data-dur="0.126" data-begin="51.164">and</span> <span data-dur="0.186" data-begin="51.29">the</span> <span data-dur="0.49" data-begin="51.476">glory</span> <span data-dur="0.129" data-begin="51.967">of</span> <span data-dur="0.092" data-begin="52.095">the</span> <span data-dur="0.361" data-begin="52.187">Lord</span> <span data-dur="0.409" data-begin="52.548">shone</span> <span data-dur="0.436" data-begin="52.957">around</span> <span data-dur="0.23" data-begin="53.393">them</span>, <span data-dur="0.102" data-begin="53.899">and</span> <span data-dur="0.084" data-begin="54.001">they</span> <span data-dur="0.092" data-begin="54.085">were</span> <span data-dur="0.401" data-begin="54.177">filled</span> <span data-dur="0.3" data-begin="54.578">with</span> <span data-dur="0.481" data-begin="54.878">fear</span>. <o:verse eID="Luke.2.9"/><o:verse sID="Luke.2.10" osisID="Luke.2.10"/><span data-dur="0.107" data-begin="55.998">And</span> <span data-dur="0.109" data-begin="56.104">the</span> <span data-dur="0.352" data-begin="56.213">angel</span> <span data-dur="0.23" data-begin="56.565">said</span> <span data-dur="0.102" data-begin="56.795">to</span> <span data-dur="0.248" data-begin="56.897">them</span>, <q><span data-dur="0.327" data-begin="57.858">Fear</span> <span data-dur="0.381" data-begin="58.185">not</span>, <span data-dur="0.116" data-begin="58.767">for</span> <span data-dur="0.703" data-begin="58.883">behold</span>, <span data-dur="0.272" data-begin="60.051">I</span> <span data-dur="0.188" data-begin="60.323">bring</span> <span data-dur="0.238" data-begin="60.512">you</span> <span data-dur="0.248" data-begin="60.75">good</span> <span data-dur="0.593" data-begin="60.998">news</span> <span data-dur="0.178" data-begin="61.749">of</span> <span data-dur="0.372" data-begin="61.927">great</span> <span data-dur="0.622" data-begin="62.299">joy</span> <span data-dur="0.114" data-begin="63.247">that</span> <span data-dur="0.171" data-begin="63.361">will</span> <span data-dur="0.169" data-begin="63.532">be</span> <span data-dur="0.151" data-begin="63.701">for</span> <span data-dur="0.315" data-begin="63.852">all</span> <span data-dur="0.149" data-begin="64.167">the</span> <span data-dur="0.481" data-begin="64.316">people</span>. <o:verse eID="Luke.2.10"/><o:verse sID="Luke.2.11" osisID="Luke.2.11"/><span data-dur="0.166" data-begin="65.384">For</span> <span data-dur="0.317" data-begin="65.55">unto</span> <span data-dur="0.121" data-begin="65.867">you</span> <span data-dur="0.173" data-begin="65.988">is</span> <span data-dur="0.287" data-begin="66.162">born</span> <span data-dur="0.302" data-begin="66.449">this</span> <span data-dur="0.316" data-begin="66.751">day</span> <span data-dur="0.074" data-begin="67.067">in</span> <span data-dur="0.069" data-begin="67.142">the</span> <span data-dur="0.28" data-begin="67.211">city</span> <span data-dur="0.173" data-begin="67.491">of</span> <span data-dur="0.461" data-begin="67.664">David</span> <span data-dur="0.087" data-begin="68.34">a</span> <span data-dur="0.689" data-begin="68.426">Savior</span>, <span data-dur="0.126" data-begin="69.353">who</span> <span data-dur="0.184" data-begin="69.479">is</span> <span data-dur="0.411" data-begin="69.663">Christ</span> <span data-dur="0.146" data-begin="70.075">the</span> <span data-dur="0.528" data-begin="70.22">Lord</span>. <o:verse eID="Luke.2.11"/><o:verse sID="Luke.2.12" osisID="Luke.2.12"/><span data-dur="0.173" data-begin="71.32">And</span> <span data-dur="0.191" data-begin="71.493">this</span> <span data-dur="0.131" data-begin="71.684">will</span> <span data-dur="0.104" data-begin="71.815">be</span> <span data-dur="0.035" data-begin="71.919">a</span> <span data-dur="0.461" data-begin="71.954">sign</span> <span data-dur="0.162" data-begin="72.415">for</span> <span data-dur="0.436" data-begin="72.577">you</span>: <span data-dur="0.119" data-begin="73.352">you</span> <span data-dur="0.094" data-begin="73.471">will</span> <span data-dur="0.399" data-begin="73.565">find</span> <span data-dur="0.141" data-begin="73.963">a</span> <span data-dur="0.463" data-begin="74.105">baby</span> <span data-dur="0.416" data-begin="74.818">wrapped</span> <span data-dur="0.146" data-begin="75.234">in</span> <span data-dur="0.602" data-begin="75.381">swaddling</span> <span data-dur="0.498" data-begin="75.982">cloths</span> <span data-dur="0.185" data-begin="76.48">and</span> <span data-dur="0.359" data-begin="76.665">lying</span> <span data-dur="0.092" data-begin="77.024">in</span> <span data-dur="0.074" data-begin="77.116">a</span> <span data-dur="0.617" data-begin="77.19">manger</span>.</q> <o:verse eID="Luke.2.12"/><o:verse sID="Luke.2.13" osisID="Luke.2.13"/><span data-dur="0.161" data-begin="78.542">And</span> <span data-dur="0.624" data-begin="78.703">suddenly</span> <span data-dur="0.173" data-begin="79.327">there</span> <span data-dur="0.206" data-begin="79.501">was</span> <span data-dur="0.196" data-begin="79.706">with</span> <span data-dur="0.136" data-begin="79.902">the</span> <span data-dur="0.478" data-begin="80.038">angel</span> <span data-dur="0.159" data-begin="80.516">a</span> <span data-dur="0.632" data-begin="80.675">multitude</span> <span data-dur="0.102" data-begin="81.307">of</span> <span data-dur="0.072" data-begin="81.408">the</span> <span data-dur="0.387" data-begin="81.48">heavenly</span> <span data-dur="0.389" data-begin="81.867">host</span> <span data-dur="0.47" data-begin="82.657">praising</span> <span data-dur="0.349" data-begin="83.126">God</span> <span data-dur="0.091" data-begin="83.475">and</span> <span data-dur="0.508" data-begin="83.566">saying</span>,<o:verse eID="Luke.2.13"/></p>
	<blockquote>
	<p><o:verse sID="Luke.2.14" osisID="Luke.2.14"/><span data-dur="0.508" data-begin="84.905">Glory</span> <span data-dur="0.178" data-begin="85.413">to</span> <span data-dur="0.352" data-begin="85.591">God</span> <span data-dur="0.079" data-begin="85.943">in</span> <span data-dur="0.057" data-begin="86.022">the</span> <span data-dur="0.58" data-begin="86.079">highest</span>,<br />
	<span data-dur="0.166" data-begin="87.058">and</span> <span data-dur="0.18" data-begin="87.224">on</span> <span data-dur="0.385" data-begin="87.404">earth</span> <span data-dur="0.338" data-begin="88.025">peace</span> <span data-dur="0.271" data-begin="88.363">among</span> <span data-dur="0.314" data-begin="88.634">those</span> <span data-dur="0.165" data-begin="88.948">with</span> <span data-dur="0.197" data-begin="89.113">whom</span> <span data-dur="0.156" data-begin="89.31">he</span> <span data-dur="0.16" data-begin="89.466">is</span> <span data-dur="0.715" data-begin="89.626">pleased</span>!<o:verse eID="Luke.2.14"/></p>
	</blockquote>
	<p><o:verse sID="Luke.2.15" osisID="Luke.2.15"/><span data-dur="0.145" data-begin="91.824">When</span> <span data-dur="0.117" data-begin="91.969">the</span> <span data-dur="0.396" data-begin="92.086">angels</span> <span data-dur="0.158" data-begin="92.482">went</span> <span data-dur="0.256" data-begin="92.64">away</span> <span data-dur="0.215" data-begin="92.896">from</span> <span data-dur="0.188" data-begin="93.112">them</span> <span data-dur="0.269" data-begin="93.3">into</span> <span data-dur="0.459" data-begin="93.569">heaven</span>, <span data-dur="0.065" data-begin="94.204">the</span> <span data-dur="0.489" data-begin="94.269">shepherds</span> <span data-dur="0.236" data-begin="94.758">said</span> <span data-dur="0.104" data-begin="94.994">to</span> <span data-dur="0.128" data-begin="95.098">one</span> <span data-dur="0.483" data-begin="95.226">another</span>, <q><span data-dur="0.136" data-begin="96.508">Let</span> <span data-dur="0.176" data-begin="96.644">us</span> <span data-dur="0.139" data-begin="96.82">go</span> <span data-dur="0.165" data-begin="96.959">over</span> <span data-dur="0.156" data-begin="97.124">to</span> <span data-dur="0.479" data-begin="97.281">Bethlehem</span> <span data-dur="0.08" data-begin="97.76">and</span> <span data-dur="0.312" data-begin="97.84">see</span> <span data-dur="0.31" data-begin="98.152">this</span> <span data-dur="0.253" data-begin="98.462">thing</span> <span data-dur="0.093" data-begin="98.715">that</span> <span data-dur="0.134" data-begin="98.808">has</span> <span data-dur="0.516" data-begin="98.942">happened</span>, <span data-dur="0.208" data-begin="99.642">which</span> <span data-dur="0.113" data-begin="99.85">the</span> <span data-dur="0.24" data-begin="99.964">Lord</span> <span data-dur="0.13" data-begin="100.203">has</span> <span data-dur="0.225" data-begin="100.333">made</span> <span data-dur="0.281" data-begin="100.558">known</span> <span data-dur="0.085" data-begin="100.839">to</span> <span data-dur="0.353" data-begin="100.924">us</span>.</q> <o:verse eID="Luke.2.15"/><o:verse sID="Luke.2.16" osisID="Luke.2.16"/><span data-dur="0.128" data-begin="101.996">And</span> <span data-dur="0.169" data-begin="102.124">they</span> <span data-dur="0.279" data-begin="102.293">went</span> <span data-dur="0.13" data-begin="102.572">with</span> <span data-dur="0.466" data-begin="102.702">haste</span> <span data-dur="0.117" data-begin="103.168">and</span> <span data-dur="0.359" data-begin="103.285">found</span> <span data-dur="0.355" data-begin="103.644">Mary</span> <span data-dur="0.169" data-begin="103.999">and</span> <span data-dur="0.416" data-begin="104.168">Joseph</span>, <span data-dur="0.132" data-begin="104.584">and</span> <span data-dur="0.139" data-begin="104.716">the</span> <span data-dur="0.338" data-begin="104.855">baby</span> <span data-dur="0.357" data-begin="105.193">lying</span> <span data-dur="0.056" data-begin="105.55">in</span> <span data-dur="0.058" data-begin="105.606">a</span> <span data-dur="0.567" data-begin="105.664">manger</span>. <o:verse eID="Luke.2.16"/><o:verse sID="Luke.2.17" osisID="Luke.2.17"/><span data-dur="0.173" data-begin="107.011">And</span> <span data-dur="0.199" data-begin="107.184">when</span> <span data-dur="0.121" data-begin="107.382">they</span> <span data-dur="0.346" data-begin="107.503">saw</span> <span data-dur="0.236" data-begin="107.849">it</span>, <span data-dur="0.152" data-begin="108.382">they</span> <span data-dur="0.21" data-begin="108.534">made</span> <span data-dur="0.373" data-begin="108.744">known</span> <span data-dur="0.071" data-begin="109.117">the</span> <span data-dur="0.424" data-begin="109.188">saying</span> <span data-dur="0.102" data-begin="109.611">that</span> <span data-dur="0.134" data-begin="109.714">had</span> <span data-dur="0.189" data-begin="109.848">been</span> <span data-dur="0.373" data-begin="110.037">told</span> <span data-dur="0.18" data-begin="110.411">them</span> <span data-dur="0.502" data-begin="110.591">concerning</span> <span data-dur="0.136" data-begin="111.092">this</span> <span data-dur="0.684" data-begin="111.228">child</span>. <o:verse eID="Luke.2.17"/><o:verse sID="Luke.2.18" osisID="Luke.2.18"/><span data-dur="0.152" data-begin="112.668">And</span> <span data-dur="0.336" data-begin="112.82">all</span> <span data-dur="0.137" data-begin="113.157">who</span> <span data-dur="0.271" data-begin="113.294">heard</span> <span data-dur="0.262" data-begin="113.565">it</span> <span data-dur="0.446" data-begin="113.827">wondered</span> <span data-dur="0.113" data-begin="114.273">at</span> <span data-dur="0.176" data-begin="114.387">what</span> <span data-dur="0.078" data-begin="114.563">the</span> <span data-dur="0.505" data-begin="114.641">shepherds</span> <span data-dur="0.345" data-begin="115.147">told</span> <span data-dur="0.318" data-begin="115.492">them</span>. <o:verse eID="Luke.2.18"/><o:verse sID="Luke.2.19" osisID="Luke.2.19"/><span data-dur="0.188" data-begin="116.538">But</span> <span data-dur="0.476" data-begin="116.726">Mary</span> <span data-dur="0.371" data-begin="117.201">treasured</span> <span data-dur="0.16" data-begin="117.572">up</span> <span data-dur="0.241" data-begin="117.731">all</span> <span data-dur="0.282" data-begin="117.973">these</span> <span data-dur="0.533" data-begin="118.254">things</span>, <span data-dur="0.537" data-begin="119.178">pondering</span> <span data-dur="0.139" data-begin="119.715">them</span> <span data-dur="0.098" data-begin="119.854">in</span> <span data-dur="0.18" data-begin="119.952">her</span> <span data-dur="0.528" data-begin="120.133">heart</span>. <o:verse eID="Luke.2.19"/><o:verse sID="Luke.2.20" osisID="Luke.2.20"/><span data-dur="0.085" data-begin="121.551">And</span> <span data-dur="0.071" data-begin="121.636">the</span> <span data-dur="0.429" data-begin="121.707">shepherds</span> <span data-dur="0.659" data-begin="122.136">returned</span>, <span data-dur="0.749" data-begin="123.144">glorifying</span> <span data-dur="0.258" data-begin="123.893">and</span> <span data-dur="0.563" data-begin="124.151">praising</span> <span data-dur="0.377" data-begin="124.715">God</span> <span data-dur="0.247" data-begin="125.092">for</span> <span data-dur="0.217" data-begin="125.339">all</span> <span data-dur="0.167" data-begin="125.556">they</span> <span data-dur="0.197" data-begin="125.723">had</span> <span data-dur="0.329" data-begin="125.92">heard</span> <span data-dur="0.119" data-begin="126.249">and</span> <span data-dur="0.591" data-begin="126.368">seen</span>, <span data-dur="0.154" data-begin="127.239">as</span> <span data-dur="0.073" data-begin="127.394">it</span> <span data-dur="0.173" data-begin="127.467">had</span> <span data-dur="0.174" data-begin="127.641">been</span> <span data-dur="0.31" data-begin="127.815">told</span> <span data-dur="0.249" data-begin="128.125">them</span>.<o:verse eID="Luke.2.20"/></p>
	
	<script>
	/* CDATA section stripped due to WordPress bug: https://core.trac.wordpress.org/ticket/3670 */
	(function(){
	var root = document.getElementById('nativity-story-from-luke');
	var audio = root.getElementsByTagName('audio')[0];
	
	/**
	 * Build an index of all of the words that can be read along with their begin,
	 * and end times, and the DOM element representing the word.
	 */
	var wordTimes = [];
	Array.prototype.forEach.call(root.querySelectorAll("[data-begin]"), function(word){
		var wordTime = {
			begin : parseFloat(word.getAttribute("data-begin")),
			dur   : parseFloat(word.getAttribute("data-dur")),
			word  : word
		};
		wordTime.index = wordTimes.length;
		wordTime.end = wordTime.begin + wordTime.dur;
		word.setAttribute('data-timeindex', wordTime.index);
		wordTimes.push(wordTime);
	});
	
	
	/**
	 * Find the next word that should be played (or that is currently being played)
	 * @todo Note: this would better be implemented as a binary search
	 */ 
	function getNextWordTime(){
		var wordTime = null,
			currentTime = audio.currentTime;
		for(var i = 0, len = wordTimes.length; i &lt; len; i++){
			var thisWordTime = wordTimes[i];
			if((currentTime &gt;= thisWordTime.begin &amp;&amp; currentTime &lt; thisWordTime.end) || currentTime &lt; wordTimes[i].begin)
			{
				return thisWordTime;
			}
		}
		return null;
	}
	
	
	var timeoutSelectNext;
	
	function removeSelection(){
		Array.prototype.forEach.call(root.querySelectorAll('span[data-begin].speaking'), function(el){
			el.className = el.className.replace(/\s*speaking\b/, ''); // el.classList.remove('speaking')
		});
	}
	
	/**
	 * Select the next word that is going to be read or the word that is being
	 * read right now
	 * @param Boolean hold  Whether or not the subsequent word should automatically be selected
	 */
	function selectNextWord(hold){
		clearTimeout(timeoutSelectNext);
		var next = getNextWordTime();
		if(next){
			function select(hold){
				removeSelection();
				
				next.word.className += ' speaking'; // next.word.classList.add('speaking')
				
				if(!hold){
					timeoutSelectNext = setTimeout(function(){
						removeSelection();
						if(!audio.paused)
							selectNextWord();
					}, Math.round((next.end - audio.currentTime)*1000));
				}
			}
		
			//Select now
			if(hold || audio.currentTime &gt;= next.begin){
				select(hold);
			}
			//Select later
			else {
				timeoutSelectNext = setTimeout(function(){
					select();
				}, Math.round((next.begin - audio.currentTime)*1000));
			}
		}
	}
	
	
	/**
	 * Select next word (at audio.currentTime) when playing starts
	 */
	audio.addEventListener('play', function(e){
		selectNextWord();
	}, false);
	
	
	/**
	 * Abandon seeking the next word because we're paused
	 */
	audio.addEventListener('pause', function(e){
		clearTimeout(timeoutSelectNext);
	}, false);
	
	
	/**
	 * Seek by clicking (event delegation)
	 */
	root.addEventListener('click', function(e){
		if(e.target.hasAttribute('data-begin')){
			var i = e.target.getAttribute('data-timeindex');
			//audio.currentTime = wordTimes[i].begin + wordTimes[i].dur/2; //@TODO
			audio.currentTime = wordTimes[i].begin + 0.001; //Note: times apparently cannot be exactly set and sometimes select too early
			selectNextWord();
		}
	}, false);
	
	
	/**
	 * Play a word when double-clicking (event delegation)
	 * Only plays the single word.
	 */
	root.addEventListener('dblclick', function(e){
		audio.play();
	}, false);
	
	
	/**
	 * Select a word when seeking
	 */
	audio.addEventListener('seeked', function(e){
		selectNextWord(e.target.paused /* for hold, probably always true */);
	}, false);
	
	})();
	/* CDATA section stripped due to WordPress bug: https://core.trac.wordpress.org/ticket/3670 */
	</script>
	
	<hr />
	<footer>
		<small>Scripture taken from The Holy Bible, English Standard Version. Copyright
		©2001 by <a href="http://www.crosswaybibles.org" target="_blank">Crossway Bibles</a>, a
		publishing ministry of Good News Publishers. Used by permission. All rights
		reserved. Text provided by the <a href="http://www.gnpcb.org/esv/share/services/" target="_blank">Crossway Bibles Web
		Service</a>.</small>
	</footer>
</article>
]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/projects/html5-audio-read-along/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Google Text-To-Speech (TTS)</title>
		<link>http://weston.ruter.net/projects/google-tts/</link>
		<comments>http://weston.ruter.net/projects/google-tts/#comments</comments>
		<pubDate>Sat, 12 Dec 2009 22:49:59 +0000</pubDate>
		<dc:creator>Weston Ruter</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://weston.ruter.net/?page_id=226</guid>
		<description><![CDATA[<p><ins datetime="2010-03-16T11:30"><em><strong>Update <time datetime="2010-03-16T11:30">2010-03-16</time></strong>: Andufo <a href="http://weston.ruter.net/projects/google-tts/#comment-15412">shared</a> the happy news
that more languages are now available in the Google TTS service! I have added a new language selection drop-down for
English, Spanish, French, German, Italian, and Hatian Creole.</em></ins></p>

<p><time datetime="2009-11-16T15:06:00">Recently</time> <a href="http://translate.google.com/" target="_blank">Google Translate</a> 
<a title="“A new look for Google Translate” on the Official Google Blog" target="_blank" href="http://googleblog.blogspot.com/2009/11/new-look-for-google-translate.html">announced</a>
the ability to hear translations into English spoken via <a href="http://en.wikipedia.org/wiki/Text-to-speech" title="Speech synthesis @ Wikipedia" target="_blank">text-to-speech</a> (<abbr title="text-to-speech">TTS</abbr>).
Looking at the Firebug Net panel for where this TTS data was coming from,
I saw that the speech audio is in MP3 format and is queried via a
simple HTTP GET (<abbr title="Representational State Transfer">REST</abbr>) request:
<code class="url">http://translate.google.com/translate_tts?tl=en&amp;q=<mark style="background:#FFFFAA;">text</mark></code>.
<del datetime="2010-03-16T11:30">Google Translate <a title="Google Translate Help" target="_blank" href="http://translate.google.com/support/">notes</a>
that the speech is <q cite="http://translate.google.com/support/">only available for short translations to English</q></del>
<ins datetime="2010-03-16T11:30">Now multiple languages are supported</ins>, and it turns out
that the TTS web service is restricting the <code>text</code> to 100 characters.
Another restriction is that the service returns <code>404 Not Found</code> if the request
includes a <code>Referer</code> header (presumably one that is not for <code>translate.google.com</code>).</p>

<p>In spite of the limitations of the web service which certainly reflect
the intention that the web service is only to be used by Google Translate,
thanks to the new HTML5's <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#audio" target="_blank">Audio</a> element and
<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#link-type-noreferrer" target="_blank"><code>rel="noreferrer"</code></a>,
the service may be utilized by client-side web applications like following (<strong><a href="http://www.google.com/chrome" target="_blank">Google Chrome</a> 4 recommended</strong>):
</p>

<!--
Mashup of Google Text-To-Speech (TTS) and HTML5 Audio <http://weston.ruter.net/projects/google-tts/>
Copyright 2009, by Weston Ruter <http://weston.ruter.net/>
License: GPL 3.0 <http://www.gnu.org/licenses/gpl.html>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<form xmlns="http://www.w3.org/1999/xhtml" id="google-tts-form" action="http://translate.google.com/translate_tts" method="get" target="_blank">
	<style scoped="">
	#google-tts-form fieldset {
		border:solid 1px black;
		-moz-border-radius:1ex;
		-webkit-border-radius:1ex;
		border-radius:1ex;
		padding:1ex;
		padding-top:2ex;
	}
	#google-tts-form table {
		width:100%;
	}
	#google-tts-form p {
		line-height:1.1em;
	}
	#google-tts-form legend {
		margin-left:1ex;
		font-weight:bold;
		padding-left:0.5ex;
		padding-right:0.5ex;
	}
	#google-tts-form [hidden] {
		display:none;
	}
	#google-tts-form small {
		font-size:smaller;
	}
	#google-tts-form button {
		/*margin-top:1ex;*/
	}
	#speechOutput td,
	#speechOutput th {
		padding-top:2ex;
	}
	#google-tts-form th {
		width:9ex;
		text-align:right;
		padding-right:1.5ex;
	}
	#textInputText {
		width:99%;
	}
	#speechOutputAudio {
		width:100%;
	}
	#speechOutputAudio {
		vertical-align:middle;
	}
	#speechOutputDownload {
		margin-top:0.5em !important;
		margin-bottom:0 !important;
		/*display:block;*/
	}
	#google-tts-form .error {
		color:red;
	}
	</style>
	<fieldset>
		<legend>Google Text-To-Speech (<abbr title="Text-To-Speech">TTS</abbr>)</legend>
		
		<!--[if IE]>
		<p class='error'>This does not work in Internet Explorer, which is the browser you are using.
		Please install <a href="http://www.google.com/chrome" target="_blank">Google Chrome</a> or
		<a href="http://code.google.com/chrome/chromeframe/" target="_blank">Google Chrome Frame</a>.</p>
		<![endif]-->
		
		<noscript>
			<p class='error'><em>Due to the <code>Referer</code> header restrictions imposed by Google's <abbr title="Text-To-Speech">TTS</abbr> service,
			you must have JavaScript enabled/available to use this form.</em></p>
		</noscript>
	
		<table>
			<tr>
				<th>
					<label for="textInputLang">Language: </label>
				</th>
				<td>
					<select name="tl" id="textInputLang">
						<option value="en">English (en)</option>
						<option value="es">Spanish (es)</option>
						<option value="fr">French (fr)</option>
						<option value="de">German (de)</option>
						<option value="it">Italian (it)</option>
						<option value="ht">Hatian Creole (ht)</option>
					</select>
				</td>
			</tr>
			<tr id="textInput">
				<th>
					<label for="textInputText">Text: </label>
				</th>
				<td><input type="text" maxlength="100" name="q" id="textInputText" value="Hello, World!" />
				</td>
			</tr>
			<tr>
				<th></th>
				<td><button type="submit" id="ttsBtn">Text-To-Speech</button></td>
			</tr>
			<tr id="speechOutput" hidden="">
				<th>
					<label>Speech:</label>
				</th>
				<td>
					<audio id="speechOutputAudio" src="" controls=""></audio>
					<p id="speechOutputDownload">
						<a id="speechOutputLink" href="" rel="noreferrer" target="_blank">Download audio</a>
						<small>(copy URL if browser doesn't support <code>rel="noreferrer"</code>; otherwise <code>404 Not Found</code>)</small>
					</p>
				</td>
			</tr>
		</table>
		<script>
		(function(){
			var btn = document.getElementById('ttsBtn');
			btn.disabled = true;
			document.addEventListener("DOMContentLoaded", function(e){
				btn.disabled = false;
				/**
				 * Detect if the browser can play MP3 audio using native HTML5 Audio.
				 * Invokes the callack function with first parameter is the boolean success
				 * value; if that value is false, a second error parameter is passed. This error
				 * is either HTMLMediaError or some other DOMException or Error object.
				 * Note the callback is likely to be invoked asynchronously!
				 * @param {function(boolean, Object|undefined)} callback
				 */
				function canPlayAudioMP3(callback){
					try {
						var audio = new Audio();
						//Shortcut which doesn't work in Chrome (always returns ""); pass through
						// if "maybe" to do asynchronous check by loading MP3 data: URI
						if(audio.canPlayType('audio/mpeg') == "probably")
							callback(true);
						
						//If this event fires, then MP3s can be played
						audio.addEventListener('canplaythrough', function(e){
							callback(true);
						}, false);
						
						//If this is fired, then client can't play MP3s
						audio.addEventListener('error', function(e){
							callback(false, this.error)
						}, false);
						
						//Smallest base64-encoded MP3 I could come up with (less than 0.000001 seconds long)
						audio.src = "data:audio/mpeg;base64,/+MYxAAAAANIAAAAAExBTUUzLjk4LjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
						audio.load();
					}
					catch(e){
						callback(false, e);
					}
				}
				
				canPlayAudioMP3(function(success){
					document.getElementById('google-tts-form').addEventListener("submit", function(e){
						//Stop if they didn't provide anything
						var textInputText = document.getElementById('textInputText');
						if(!textInputText.value.replace(/\s+/)){
							textInputText.focus();
							e.preventDefault();
							return false;
						}
						var textInputLang = document.getElementById('textInputLang');
						
						//When the form is submitted, generate the URL for Google TTS
						var params = [];
						for(var i = 0, len = this.elements.length; i &lt; len; i++){
							if(this.elements[i].name)
								params.push(this.elements[i].name + "=" + encodeURIComponent(this.elements[i].value));
						}
						var url = this.action + "?" + params.join("&amp;");
						
						//Update the hash with the new query so it can be bookmarked
						location.hash = "#hl=" + encodeURIComponent(textInputLang.value).replace(/%20/g, '+')
									  + "&amp;text=" + encodeURIComponent(textInputText.value).replace(/%20/g, '+');
						
						var link = document.getElementById('speechOutputLink');
						link.href = url;
						var speechOutput = document.getElementById('speechOutput');
						var audio = document.getElementById('speechOutputAudio');
						
						if(success){
							btn.disabled = false;
							audio.src = url;
							audio.load(); //this is necessary because can't set audio.currentTime in Chrome (below)
							//audio.addEventListener("ended", function(e){
							//	this.currentTime = 0;
							//}, false);
							audio.play();
						}
						else if(audio){
							var warning = document.createElement('em');
							warning.className = "error";
							warning.innerHTML = "Either your browser does not support HTML5 Audio or your browser doesn't support playing MP3s, so you must use the download link:";
							audio.parentNode.replaceChild(warning, audio);
						}
						speechOutput.removeAttribute('hidden');
						
						if(!success)
							link.focus();
						e.preventDefault();
					}, false);
					
					//Focus on the input box
					var textInputText = document.getElementById('textInputText');
					if(location.hash.substr(1) == 'google-tts-form')
						textInputText.focus();
					var textInputLang = document.getElementById('textInputLang');
					
					//Allow TTS results to be browsed in history
					var previousQuery = {};
					var onhashchange = function(){
						var query = {};
						var queryParts = (window.location.search.substr(1) + '&amp;' + window.location.hash.substr(1)).toString().split(/&amp;/);
						for(var i = 0; i &lt; queryParts.length; i++){
							var keyVal = queryParts[i].split(/=/);
							if(keyVal.length == 2)
								query[keyVal[0]] = decodeURIComponent(keyVal[1].replace(/\+/g, '%20'));
						}
						
						if(previousQuery.hl != query.hl || previousQuery.text != query.text){
							previousQuery = query;
							if(query.hl){
								for(var i = 0; i &lt; textInputLang.options.length; i++){
									if(textInputLang.options[i].value == query.hl){
										textInputLang.options[i].selected = true;
										break;
									}
								}
							}
							textInputText.value = query.text;
							textInputText.focus();
							btn.disabled = false;
							btn.click();
						}
					}
					onhashchange();
					window.addEventListener('hashchange', onhashchange, false);
				});
			}, false);
		})();
		</script>
	</fieldset>
</form>
<p>I am really excited at the prospect of text-to-speech being made available on the Web!
It's just too bad that fetching MP3s on an remote web service is the only
standard way of doing so currently; modern operating systems all have <abbr title="Text-To-Speech">TTS</abbr> capabilities, so it's a
shame that web apps and can't utilize them via client-side scripting. I posted to the WHATWG mailing list about such a
<a href="http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-December/024453.html" target="_blank">Text-To-Speech (<abbr title="Text-To-Speech">TTS</abbr>) Web API for JavaScript</a>,
and I was directed to a recent thread about a <a href="http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-December/thread.html#24281" target="_blank">Web API for speech recognition and synthesis</a>.</p>

<p>Perhaps there is some momentum building here? Having <abbr title="Text-To-Speech">TTS</abbr> available in the browser would
boost accessibility for the seeing-impaired and improve usability for people on-the-go.
<abbr title="Text-To-Speech">TTS</abbr> is just another technology
that has traditionally been relegated to desktop applications, but as the Open Web
advances as the preferred platform for application development, it is an essential
service to make available (as with <a href="http://www.w3.org/TR/geolocation-API/" target="_blank">Geolocation API</a>,
<a href="http://www.w3.org/TR/dap-api-reqs/" target="_blank">Device API</a>,
<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/" target="_blank" title="WHATWG HTML (Including HTML5) aka Web Applications 1.0">etc.</a>).
And besides, I want to build <abbr title="Text-To-Speech">TTS</abbr>
applications and my motto is: <em><q cite="http://twitter.com/westonruter/status/5868384829">If it can't be done on the Open Web, it's not worth doing at all</q>!</em>
</p>]]></description>
			<content:encoded><![CDATA[<p><ins datetime="2010-03-16T11:30"><em><strong>Update <time datetime="2010-03-16T11:30">2010-03-16</time></strong>: Andufo <a href="http://weston.ruter.net/projects/google-tts/#comment-15412">shared</a> the happy news
that more languages are now available in the Google TTS service! I have added a new language selection drop-down for
English, Spanish, French, German, Italian, and Hatian Creole.</em></ins></p>

<p><time datetime="2009-11-16T15:06:00">Recently</time> <a href="http://translate.google.com/" target="_blank">Google Translate</a> 
<a title="“A new look for Google Translate” on the Official Google Blog" target="_blank" href="http://googleblog.blogspot.com/2009/11/new-look-for-google-translate.html">announced</a>
the ability to hear translations into English spoken via <a href="http://en.wikipedia.org/wiki/Text-to-speech" title="Speech synthesis @ Wikipedia" target="_blank">text-to-speech</a> (<abbr title="text-to-speech">TTS</abbr>).
Looking at the Firebug Net panel for where this TTS data was coming from,
I saw that the speech audio is in MP3 format and is queried via a
simple HTTP GET (<abbr title="Representational State Transfer">REST</abbr>) request:
<code class="url">http://translate.google.com/translate_tts?tl=en&amp;q=<mark style="background:#FFFFAA;">text</mark></code>.
<del datetime="2010-03-16T11:30">Google Translate <a title="Google Translate Help" target="_blank" href="http://translate.google.com/support/">notes</a>
that the speech is <q cite="http://translate.google.com/support/">only available for short translations to English</q></del>
<ins datetime="2010-03-16T11:30">Now multiple languages are supported</ins>, and it turns out
that the TTS web service is restricting the <code>text</code> to 100 characters.
Another restriction is that the service returns <code>404 Not Found</code> if the request
includes a <code>Referer</code> header (presumably one that is not for <code>translate.google.com</code>).</p>

<p>In spite of the limitations of the web service which certainly reflect
the intention that the web service is only to be used by Google Translate,
thanks to the new HTML5's <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#audio" target="_blank">Audio</a> element and
<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#link-type-noreferrer" target="_blank"><code>rel="noreferrer"</code></a>,
the service may be utilized by client-side web applications like following (<strong><a href="http://www.google.com/chrome" target="_blank">Google Chrome</a> 4 recommended</strong>):
</p>

<!--
Mashup of Google Text-To-Speech (TTS) and HTML5 Audio <http://weston.ruter.net/projects/google-tts/>
Copyright 2009, by Weston Ruter <http://weston.ruter.net/>
License: GPL 3.0 <http://www.gnu.org/licenses/gpl.html>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<form xmlns="http://www.w3.org/1999/xhtml" id="google-tts-form" action="http://translate.google.com/translate_tts" method="get" target="_blank">
	<style scoped="">
	#google-tts-form fieldset {
		border:solid 1px black;
		-moz-border-radius:1ex;
		-webkit-border-radius:1ex;
		border-radius:1ex;
		padding:1ex;
		padding-top:2ex;
	}
	#google-tts-form table {
		width:100%;
	}
	#google-tts-form p {
		line-height:1.1em;
	}
	#google-tts-form legend {
		margin-left:1ex;
		font-weight:bold;
		padding-left:0.5ex;
		padding-right:0.5ex;
	}
	#google-tts-form [hidden] {
		display:none;
	}
	#google-tts-form small {
		font-size:smaller;
	}
	#google-tts-form button {
		/*margin-top:1ex;*/
	}
	#speechOutput td,
	#speechOutput th {
		padding-top:2ex;
	}
	#google-tts-form th {
		width:9ex;
		text-align:right;
		padding-right:1.5ex;
	}
	#textInputText {
		width:99%;
	}
	#speechOutputAudio {
		width:100%;
	}
	#speechOutputAudio {
		vertical-align:middle;
	}
	#speechOutputDownload {
		margin-top:0.5em !important;
		margin-bottom:0 !important;
		/*display:block;*/
	}
	#google-tts-form .error {
		color:red;
	}
	</style>
	<fieldset>
		<legend>Google Text-To-Speech (<abbr title="Text-To-Speech">TTS</abbr>)</legend>
		
		<!--[if IE]>
		<p class='error'>This does not work in Internet Explorer, which is the browser you are using.
		Please install <a href="http://www.google.com/chrome" target="_blank">Google Chrome</a> or
		<a href="http://code.google.com/chrome/chromeframe/" target="_blank">Google Chrome Frame</a>.</p>
		<![endif]-->
		
		<noscript>
			<p class='error'><em>Due to the <code>Referer</code> header restrictions imposed by Google's <abbr title="Text-To-Speech">TTS</abbr> service,
			you must have JavaScript enabled/available to use this form.</em></p>
		</noscript>
	
		<table>
			<tr>
				<th>
					<label for="textInputLang">Language: </label>
				</th>
				<td>
					<select name="tl" id="textInputLang">
						<option value="en">English (en)</option>
						<option value="es">Spanish (es)</option>
						<option value="fr">French (fr)</option>
						<option value="de">German (de)</option>
						<option value="it">Italian (it)</option>
						<option value="ht">Hatian Creole (ht)</option>
					</select>
				</td>
			</tr>
			<tr id="textInput">
				<th>
					<label for="textInputText">Text: </label>
				</th>
				<td><input type="text" maxlength="100" name="q" id="textInputText" value="Hello, World!" />
				</td>
			</tr>
			<tr>
				<th></th>
				<td><button type="submit" id="ttsBtn">Text-To-Speech</button></td>
			</tr>
			<tr id="speechOutput" hidden="">
				<th>
					<label>Speech:</label>
				</th>
				<td>
					<audio id="speechOutputAudio" src="" controls=""></audio>
					<p id="speechOutputDownload">
						<a id="speechOutputLink" href="" rel="noreferrer" target="_blank">Download audio</a>
						<small>(copy URL if browser doesn't support <code>rel="noreferrer"</code>; otherwise <code>404 Not Found</code>)</small>
					</p>
				</td>
			</tr>
		</table>
		<script>
		(function(){
			var btn = document.getElementById('ttsBtn');
			btn.disabled = true;
			document.addEventListener("DOMContentLoaded", function(e){
				btn.disabled = false;
				/**
				 * Detect if the browser can play MP3 audio using native HTML5 Audio.
				 * Invokes the callack function with first parameter is the boolean success
				 * value; if that value is false, a second error parameter is passed. This error
				 * is either HTMLMediaError or some other DOMException or Error object.
				 * Note the callback is likely to be invoked asynchronously!
				 * @param {function(boolean, Object|undefined)} callback
				 */
				function canPlayAudioMP3(callback){
					try {
						var audio = new Audio();
						//Shortcut which doesn't work in Chrome (always returns ""); pass through
						// if "maybe" to do asynchronous check by loading MP3 data: URI
						if(audio.canPlayType('audio/mpeg') == "probably")
							callback(true);
						
						//If this event fires, then MP3s can be played
						audio.addEventListener('canplaythrough', function(e){
							callback(true);
						}, false);
						
						//If this is fired, then client can't play MP3s
						audio.addEventListener('error', function(e){
							callback(false, this.error)
						}, false);
						
						//Smallest base64-encoded MP3 I could come up with (less than 0.000001 seconds long)
						audio.src = "data:audio/mpeg;base64,/+MYxAAAAANIAAAAAExBTUUzLjk4LjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
						audio.load();
					}
					catch(e){
						callback(false, e);
					}
				}
				
				canPlayAudioMP3(function(success){
					document.getElementById('google-tts-form').addEventListener("submit", function(e){
						//Stop if they didn't provide anything
						var textInputText = document.getElementById('textInputText');
						if(!textInputText.value.replace(/\s+/)){
							textInputText.focus();
							e.preventDefault();
							return false;
						}
						var textInputLang = document.getElementById('textInputLang');
						
						//When the form is submitted, generate the URL for Google TTS
						var params = [];
						for(var i = 0, len = this.elements.length; i &lt; len; i++){
							if(this.elements[i].name)
								params.push(this.elements[i].name + "=" + encodeURIComponent(this.elements[i].value));
						}
						var url = this.action + "?" + params.join("&amp;");
						
						//Update the hash with the new query so it can be bookmarked
						location.hash = "#hl=" + encodeURIComponent(textInputLang.value).replace(/%20/g, '+')
									  + "&amp;text=" + encodeURIComponent(textInputText.value).replace(/%20/g, '+');
						
						var link = document.getElementById('speechOutputLink');
						link.href = url;
						var speechOutput = document.getElementById('speechOutput');
						var audio = document.getElementById('speechOutputAudio');
						
						if(success){
							btn.disabled = false;
							audio.src = url;
							audio.load(); //this is necessary because can't set audio.currentTime in Chrome (below)
							//audio.addEventListener("ended", function(e){
							//	this.currentTime = 0;
							//}, false);
							audio.play();
						}
						else if(audio){
							var warning = document.createElement('em');
							warning.className = "error";
							warning.innerHTML = "Either your browser does not support HTML5 Audio or your browser doesn't support playing MP3s, so you must use the download link:";
							audio.parentNode.replaceChild(warning, audio);
						}
						speechOutput.removeAttribute('hidden');
						
						if(!success)
							link.focus();
						e.preventDefault();
					}, false);
					
					//Focus on the input box
					var textInputText = document.getElementById('textInputText');
					if(location.hash.substr(1) == 'google-tts-form')
						textInputText.focus();
					var textInputLang = document.getElementById('textInputLang');
					
					//Allow TTS results to be browsed in history
					var previousQuery = {};
					var onhashchange = function(){
						var query = {};
						var queryParts = (window.location.search.substr(1) + '&amp;' + window.location.hash.substr(1)).toString().split(/&amp;/);
						for(var i = 0; i &lt; queryParts.length; i++){
							var keyVal = queryParts[i].split(/=/);
							if(keyVal.length == 2)
								query[keyVal[0]] = decodeURIComponent(keyVal[1].replace(/\+/g, '%20'));
						}
						
						if(previousQuery.hl != query.hl || previousQuery.text != query.text){
							previousQuery = query;
							if(query.hl){
								for(var i = 0; i &lt; textInputLang.options.length; i++){
									if(textInputLang.options[i].value == query.hl){
										textInputLang.options[i].selected = true;
										break;
									}
								}
							}
							textInputText.value = query.text;
							textInputText.focus();
							btn.disabled = false;
							btn.click();
						}
					}
					onhashchange();
					window.addEventListener('hashchange', onhashchange, false);
				});
			}, false);
		})();
		</script>
	</fieldset>
</form>
<p>I am really excited at the prospect of text-to-speech being made available on the Web!
It's just too bad that fetching MP3s on an remote web service is the only
standard way of doing so currently; modern operating systems all have <abbr title="Text-To-Speech">TTS</abbr> capabilities, so it's a
shame that web apps and can't utilize them via client-side scripting. I posted to the WHATWG mailing list about such a
<a href="http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-December/024453.html" target="_blank">Text-To-Speech (<abbr title="Text-To-Speech">TTS</abbr>) Web API for JavaScript</a>,
and I was directed to a recent thread about a <a href="http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-December/thread.html#24281" target="_blank">Web API for speech recognition and synthesis</a>.</p>

<p>Perhaps there is some momentum building here? Having <abbr title="Text-To-Speech">TTS</abbr> available in the browser would
boost accessibility for the seeing-impaired and improve usability for people on-the-go.
<abbr title="Text-To-Speech">TTS</abbr> is just another technology
that has traditionally been relegated to desktop applications, but as the Open Web
advances as the preferred platform for application development, it is an essential
service to make available (as with <a href="http://www.w3.org/TR/geolocation-API/" target="_blank">Geolocation API</a>,
<a href="http://www.w3.org/TR/dap-api-reqs/" target="_blank">Device API</a>,
<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/" target="_blank" title="WHATWG HTML (Including HTML5) aka Web Applications 1.0">etc.</a>).
And besides, I want to build <abbr title="Text-To-Speech">TTS</abbr>
applications and my motto is: <em><q cite="http://twitter.com/westonruter/status/5868384829">If it can't be done on the Open Web, it's not worth doing at all</q>!</em>
</p>]]></content:encoded>
			<wfw:commentRss>http://weston.ruter.net/projects/google-tts/feed/</wfw:commentRss>
		<slash:comments>35</slash:comments>
		</item>
	</channel>
</rss>

