<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2enclosuresfull.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:media="http://search.yahoo.com/mrss/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>John Dyer's Code</title>
	
	<link>http://johndyer.name</link>
	<description>Curly Braces and Angled Brackets</description>
	<lastBuildDate>Sat, 18 May 2013 21:41:20 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/johndyer" /><feedburner:info uri="johndyer" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><media:keywords>c,ASP,NET,Flash,Papervision,JavaScript,HTML,XHTML,3D,NET,AS3,ActionScript</media:keywords><media:category scheme="http://www.itunes.com/dtds/podcast-1.0.dtd">Technology</media:category><itunes:owner><itunes:email>johndyer@gmail.com</itunes:email></itunes:owner><itunes:explicit>no</itunes:explicit><itunes:keywords>c,ASP,NET,Flash,Papervision,JavaScript,HTML,XHTML,3D,NET,AS3,ActionScript</itunes:keywords><itunes:subtitle>Technology and web development in ASP.NET, Flash, and JavaScript</itunes:subtitle><itunes:category text="Technology" /><item>
		<title>Coloring Hebrew Vowels and Accents in HTML/CSS</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/3ATgq14SfyY/</link>
		<comments>http://johndyer.name/coloring-hebrew-vowels-and-accents-in-htmlcss/#comments</comments>
		<pubDate>Sat, 18 May 2013 21:37:47 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[Bible Tools]]></category>
		<category><![CDATA[HTML/CSS]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=340</guid>
		<description><![CDATA[In a recent conversation with a Hebrew professor, we discussed adding color to Hebrew vowels and accents. If you&#8217;re unfamiliar, Hebrew is different from most Western languages in two main ways. First, it reads right to left (or rtl in HTML terms) and second vowels are represented as dots and lines that can be above [...]]]></description>
				<content:encoded><![CDATA[<style><!--
table.hebrew-demo { border-collapse: collapse; } table.hebrew-demo th, table.hebrew-demo td { vertical-align:top; padding: 10px 6px; border: solid 1px #ccc; } .table.hebrew-demo th { background: #ddd; } .hebrew { font-family: auto; font-size: 32px; direction: rtl; color:#111111; } .hebrew-vowel { color: #ff0000; } .hebrew-accent-minor, .hebrew-accent-major { color: #00ff00; } .hebrew-layers { display: inline-block; position: relative; } .hebrew-layers .hebrew-layers-original { color: transparent; } .hebrew-layers .hebrew-layers-vowels { position: absolute; top: 0; right: 0; color: #ff0000; } .hebrew-layers .hebrew-layers-accents { position: absolute; top: 0; right: 0; color: #00ff00; } .hebrew-layers .hebrew-layers-consonants { position: absolute; top: 0; right: 0; color: #111111; } .ezrasil td span { font-family: 'Ezra SIL'; font-size: 24px; } .sblhebrew td span { font-family: 'SBL Hebrew'; } .arial td span { font-family: Arial; } .hebrew-sentence {line-height: 1.4;  }
--></style>
<p>In a recent conversation with a Hebrew professor, we discussed adding color to Hebrew vowels and accents. If you&#8217;re unfamiliar, Hebrew is different from most Western languages in two main ways. First, it reads right to left (or <code>rtl</code> in HTML terms) and second vowels are represented as dots and lines that can be above or below the consonants.</p>
<p>Browsers seem to handle the <code>rtl</code> part pretty well, but they don&#8217;t do an awesome job with the vowel points. And for teaching purposes, it&#8217;s very hard to make the vowels a different color from the consonants. Below are a few attempts to do this using various combinations of HTML and JavaScript.</p>
<h2>Standard Hebrew Text vs. Pointed Text</h2>
<p>In the table below, you can see what Hebrew text looks like with and without vowels. Unlike English, when you remove the vowels the word still takes up the same amount of space.</p>
<table class="hebrew-demo">
<thead>
<tr>
<th></th>
<th>Hebrew</th>
<th>English</th>
</tr>
</thead>
<tbody>
<tr>
<th>Consonants Only</th>
<td><span class="hebrew">אלהים</span></td>
<td>lhm</td>
</tr>
<tr>
<th>With Vowels</th>
<td><span class="hebrew">אֱלֹהִים</span></td>
<td>elohim</td>
</tr>
</tbody>
</table>
<h2>Coloring Individual Vowels</h2>
<p>The first thing I wanted to try was adding <code>&lt;span&gt;</code> tags around vowels and coloring them differently. In the markup below you&#8217;ll see the letters pulled out of order with the English HTML, but it&#8217;ll give you an idea of what we&#8217;re trying to do.</p>
<h3>Markup</h3>
<pre class="brush: html; gutter: true">א&lt;span class=&quot;hebrew-vowel&quot;&gt;ֱ&lt;/span&gt;ל&lt;span class=&quot;hebrew-vowel&quot;&gt;ֹ&lt;/span&gt;ה&lt;span class=&quot;hebrew-vowel&quot;&gt;ִ&lt;/span&gt;&lt;span class=&quot;hebrew-accent-minor&quot;&gt;֤&lt;/span&gt;ים</pre>
<h3>CSS</h3>
<pre class="brush: css; gutter: true">.hebrew-vowel {
	color: #ff0000;
}
.hebrew-accent-minor, .hebrew-accent-major {
	color: #00ff00;
}</pre>
<h3>Demo</h3>
<table class="hebrew-demo">
<thead>
<tr>
<th></th>
<th>Hebrew</th>
<th>English</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<th>Original</th>
<td><span class="hebrew">אֱלֹהִ֤ים</span></td>
<td>elohim</td>
<td>Correct in all browsers</td>
</tr>
<tr>
<th>Span Colors</th>
<td><span class="hebrew">א<span class="hebrew-vowel">ֱ</span>ל<span class="hebrew-vowel">ֹ</span>ה<span class="hebrew-vowel">ִ</span><span class="hebrew-accent-minor">֤</span>ים</span></td>
<td><span class="hebrew-vowel">e</span>l<span class="hebrew-vowel">o</span>h<span class="hebrew-vowel">i</span>m</td>
<td>Chrome/Safari: colored, but misaligned. Firefox/IE: aligned, but not colored</td>
</tr>
</tbody>
</table>
<h3>Results</h3>
<p>In the example above if you&#8217;re using a Webkit based browser on a Mac, you&#8217;ll see nice red vowels and green accents. The only problem is that &#8211; depending on the font &#8211; the vowels are often shifted of place.</p>
<p>However, if you&#8217;re using Firefox or Internet Explorer, the vowels and accents stay in the right place, but apparently Firefox and IE can&#8217;t color them &#8211; they just stay black and ignore the CSS color. Lastly there is the strange case of PC/Chrome which renders the vowels as as standalone entities with an outline for the missing consonants (see screenshots at the end).</p>
<h2>Layering Vowels with Absolute Positioning</h2>
<p>Since Chrome can&#8217;t keep the vowels correctly aligned and Firefox and IE can&#8217;t color the vowels, I decided to try to layer the text using absolute positioning. In the example, I&#8217;ve tried putting the accents and vowels on a single layer and splitting them into two layers with different colors.</p>
<ol>
<li>Transparent: The bottom layer is the original text used to correctly size the outer <code>span</code> tag</li>
<li>Green: The second layer has vowels removed and only leaves consonants and accents</li>
<li>Red: The third layer has consonants removed and only leaves consonants and vowels</li>
<li>Black: The top layer has only consonants layered on top of the colored layers below</li>
</ol>
<h3>Markup</h3>
<pre class="brush: html; gutter: true">&lt;span class=&quot;hebrew-layers&quot;&gt;
	&lt;span class=&quot;hebrew-layers-original&quot;&gt;אֱלֹהִים&lt;/span&gt;
	&lt;span class=&quot;hebrew-layers-accents&quot;&gt;אלה֤ים&lt;/span&gt;	
	&lt;span class=&quot;hebrew-layers-vowels&quot;&gt;אֱלֹהִים&lt;/span&gt;
	&lt;span class=&quot;hebrew-layers-consonants&quot;&gt;אלהים&lt;/span&gt;
&lt;/span&gt;</pre>
<h3>CSS</h3>
<pre class="brush: css; gutter: true">.hebrew-layers {
	display: inline-block;
	position: relative;
}
.hebrew-layers .hebrew-layers-original {
	color: transparent;
}
.hebrew-layers .hebrew-layers-vowels {
	position: absolute;
	top: 0;
	right: 0;
	color: #ff0000;
}
.hebrew-layers .hebrew-layers-accents {
	position: absolute;
	top: 0;
	right: 0;
	color: #00ff00;
}
.hebrew-layers .hebrew-layers-consonants {
	position: absolute;
	top: 0;
	right: 0;
	color: #000000;
}</pre>
<h3>Demo: Layering</h3>
<table class="hebrew-demo">
<thead>
<tr>
<th>Style</th>
<th>Demo</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<th>Original</th>
<td><span class="hebrew">אֱלֹהִ֤ים</span></td>
<td>Correct in all browsers.</td>
</tr>
<tr>
<th>Span Colors</th>
<td><span class="hebrew">א<span class="hebrew-vowel">ֱ</span>ל<span class="hebrew-vowel">ֹ</span>ה<span class="hebrew-vowel">ִ</span><span class="hebrew-accent-minor">֤</span>ים</span></td>
<td>Chrome/Safari: colored, but misaligned. Firefox/IE: aligned, but not colored</td>
</tr>
<tr>
<th>Single Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִ֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִ֤ים</span><span class="hebrew-layers-consonants">אלהים</span></span></td>
<td>Mostly works. Webkit often misaligns.</td>
</tr>
<tr>
<th>Double Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִים</span><span class="hebrew-layers-accents">אלה֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִים</span><span class="hebrew-layers-consonants">אלהים</span><br />
</span></td>
<td>Mostly works, but messes up accent/vowel pairs</td>
</tr>
</tbody>
</table>
<h3>Results</h3>
<p>In this case, all browsers (including Firefox and IE) are now able to render different colors for the consonants and vowels. And, for the most part, they all put them in the right place.</p>
<p>There are, however, two problems. First, is that while IE and Firefox render things perfectly almost all the time, Chrome doesn&#8217;t always keep the consonants in the same position across layers and that creates what looks like a text-shadow effect. I love using and developing in Chrome, and this is one of the few ares I&#8217;ve ever found Chrome/Webkit to be the worst in an area (Chrome 26 vs. IE 10 vs. Firefox 21).</p>
<p>The second problem is a bit more obscure. Even in browsers that render more consistently (IE and Firefox), the layered solution isn&#8217;t perfect if you want a different color for accents and vowels. The reason is that the vowel and accent positions change depending on if a consonant has only a vowel, only an accent, or both an accent and a vowel. You can see this on the middle letter where the red dot [hiriq] and green arrow [yetiv] change positions in the two layers example. Since the accents are typically bigger I put them in a layer underneath the vowel point so the vowel would show up more clearly on top. So this effect is a kind of trade-off that may or may not be worth it depending on your teaching or reading goals.</p>
<h2>Font Demos</h2>
<p>While Chrome is by far the worst at rendering the layers consistently, all browsers have trouble at times with some popular Hebrew fonts. Below I&#8217;m including some popular ones with screenshots of how they render. At the end, there is a demo of an entire verse where problems are even more frequent.</p>
<h3>Browser Font Matrix</h3>
<table class="hebrew-demo">
<thead>
<tr>
<th></th>
<th>Your Browser</th>
<th>Mac/Chrome</th>
<th>PC/Chrome</th>
<th>Mac/Firefox</th>
<th>PC/IE</th>
</tr>
</thead>
<tbody>
<tr>
<th>Default Font</th>
<td>
<table class="hebrew-demo">
<tbody>
<tr>
<th>Original</th>
<td><span class="hebrew">אֱלֹהִ֤ים</span></td>
</tr>
<tr>
<th>Span Colors</th>
<td><span class="hebrew">א<span class="hebrew-vowel">ֱ</span>ל<span class="hebrew-vowel">ֹ</span>ה<span class="hebrew-vowel">ִ</span><span class="hebrew-accent-minor">֤</span>ים</span></td>
</tr>
<tr>
<th>Single Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִ֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִ֤ים</span><span class="hebrew-layers-consonants">אלהים</span></span></td>
</tr>
<tr>
<th>Double Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִים</span><span class="hebrew-layers-accents">אלה֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִים</span><span class="hebrew-layers-consonants">אלהים</span><br />
</span></td>
</tr>
</tbody>
</table>
</td>
<td><img class="alignleft size-full wp-image-374" alt="macchrome-default" src="http://johndyer.name/wp-content/uploads/2013/05/macchrome-default.png" width="111" height="173" /></td>
<td><img class="alignleft size-full wp-image-379" alt="pcchrome-default" src="http://johndyer.name/wp-content/uploads/2013/05/pcchrome-default.png" width="143" height="172" /></a></td>
<td><img class="alignleft size-full wp-image-383" alt="macfirefox-default" src="http://johndyer.name/wp-content/uploads/2013/05/macfirefox-default.png" width="89" height="172" /></a></td>
<td><img class="alignleft size-full wp-image-387" alt="ie-default" src="http://johndyer.name/wp-content/uploads/2013/05/ie-default.png" width="87" height="174" /></a></td>
</tr>
<tr>
<th>Ezra SIL</th>
<td>
<table class="hebrew-demo ezrasil">
<tbody>
<tr>
<th>Original</th>
<td><span class="hebrew">אֱלֹהִ֤ים</span></td>
</tr>
<tr>
<th>Span Colors</th>
<td><span class="hebrew">א<span class="hebrew-vowel">ֱ</span>ל<span class="hebrew-vowel">ֹ</span>ה<span class="hebrew-vowel">ִ</span><span class="hebrew-accent-minor">֤</span>ים</span></td>
</tr>
<tr>
<th>Single Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִ֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִ֤ים</span><span class="hebrew-layers-consonants">אלהים</span></span></td>
</tr>
<tr>
<th>Double Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִים</span><span class="hebrew-layers-accents">אלה֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִים</span><span class="hebrew-layers-consonants">אלהים</span></span></td>
</tr>
</tbody>
</table>
</td>
<td><img class="alignleft size-full wp-image-375" alt="macchrome-ezra" src="http://johndyer.name/wp-content/uploads/2013/05/macchrome-ezra.png" width="90" height="174" /></a></td>
<td><img class="alignleft size-full wp-image-380" alt="pcchrome-ezra" src="http://johndyer.name/wp-content/uploads/2013/05/pcchrome-ezra.png" width="88" height="172" /></a></td>
<td><img class="alignleft size-full wp-image-384" alt="macfirefox-ezra" src="http://johndyer.name/wp-content/uploads/2013/05/macfirefox-ezra.png" width="90" height="174" /></a></td>
<td><img class="alignleft size-full wp-image-388" alt="ie-ezra" src="http://johndyer.name/wp-content/uploads/2013/05/ie-ezra.png" width="89" height="174" /></a></td>
</tr>
<tr>
<th>SBL Hebrew</th>
<td>
<table class="hebrew-demo sblhebrew">
<tbody>
<tr>
<th>Original</th>
<td><span class="hebrew">אֱלֹהִ֤ים</span></td>
</tr>
<tr>
<th>Span Colors</th>
<td><span class="hebrew">א<span class="hebrew-vowel">ֱ</span>ל<span class="hebrew-vowel">ֹ</span>ה<span class="hebrew-vowel">ִ</span><span class="hebrew-accent-minor">֤</span>ים</span></td>
</tr>
<tr>
<th>Single Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִ֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִ֤ים</span><span class="hebrew-layers-consonants">אלהים</span></span></td>
</tr>
<tr>
<th>Double Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִים</span><span class="hebrew-layers-accents">אלה֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִים</span><span class="hebrew-layers-consonants">אלהים</span></span></td>
</tr>
</tbody>
</table>
</td>
<td><img class="alignleft size-full wp-image-376" alt="macchrome-sbl" src="http://johndyer.name/wp-content/uploads/2013/05/macchrome-sbl.png" width="92" height="172" /></a></td>
<td><img class="alignleft size-full wp-image-381" alt="pcchrome-sbl" src="http://johndyer.name/wp-content/uploads/2013/05/pcchrome-sbl.png" width="150" height="172" /></a></td>
<td><img class="alignleft size-full wp-image-385" alt="macfirefox-sbl" src="http://johndyer.name/wp-content/uploads/2013/05/macfirefox-sbl.png" width="92" height="172" /></a></td>
<td><img class="alignleft size-full wp-image-389" alt="ie-sbl" src="http://johndyer.name/wp-content/uploads/2013/05/ie-sbl.png" width="93" height="171" /></a></td>
</tr>
<tr>
<th>Arial</th>
<td>
<table class="hebrew-demo arial">
<tbody>
<tr>
<th>Original</th>
<td><span class="hebrew">אֱלֹהִ֤ים</span></td>
</tr>
<tr>
<th>Span Colors</th>
<td><span class="hebrew">א<span class="hebrew-vowel">ֱ</span>ל<span class="hebrew-vowel">ֹ</span>ה<span class="hebrew-vowel">ִ</span><span class="hebrew-accent-minor">֤</span>ים</span></td>
</tr>
<tr>
<th>Single Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִ֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִ֤ים</span><span class="hebrew-layers-consonants">אלהים</span></span></td>
</tr>
<tr>
<th>Double Layer</th>
<td><span class="hebrew hebrew-layers"><span class="hebrew-layers-original">אֱלֹהִים</span><span class="hebrew-layers-accents">אלה֤ים</span><span class="hebrew-layers-vowels">אֱלֹהִים</span><span class="hebrew-layers-consonants">אלהים</span></span></td>
</tr>
</tbody>
</table>
</td>
<td><img class="alignleft size-full wp-image-377" alt="macchrome-arial" src="http://johndyer.name/wp-content/uploads/2013/05/macchrome-arial.png" width="92" height="170" /></a></td>
<td><img class="alignleft size-full wp-image-382" alt="pcchrome-arial" src="http://johndyer.name/wp-content/uploads/2013/05/pcchrome-arial.png" width="152" height="175" /></a></td>
<td><img class="alignleft size-full wp-image-386" alt="macfirefox-arial" src="http://johndyer.name/wp-content/uploads/2013/05/macfirefox-arial.png" width="96" height="173" /></a></td>
<td><img class="alignleft size-full wp-image-390" alt="ie-arial" src="http://johndyer.name/wp-content/uploads/2013/05/ie-arial.png" width="98" height="174" /></a></td>
</tr>
</tbody>
</table>
<h2>Full Sentence Demo</h2>
<p>I have this running in a Bible application: </p>
<p><a class="post-button" target="_blank" href="http://biblewebapp.com/app/?location1=Gen.1.1&#038;document1=eng_nasb&#038;location2=Gen.1.1&#038;document2=heb_wlc">Bible Web App: Hebrew style</a></p>
<p>And here is entire Hebrew sentence marked up in all the styles:</p>
<table class="hebrew-demo hebrew-sentence">
<tbody>
<tr>
<th>Original</th>
<td dir="rtl"><span class="hebrew">וְהָאָ֗רֶץ הָיְתָ֥ה תֹ֙הוּ֙ וָבֹ֔הוּ וְחֹ֖שֶׁךְ עַל־פְּנֵ֣י תְה֑וֹם וְר֣וּחַ אֱלֹהִ֔ים מְרַחֶ֖פֶת עַל־פְּנֵ֥י הַמָּֽיִם׃</span></td>
</tr>
<tr>
<th>Span Colors</th>
<td dir="rtl">
				<span class="hebrew"><span class="w H776" data-lemma="H776">ו<span class="hebrew-vowel">ְ</span>ה<span class="hebrew-vowel">ָ</span>א<span class="hebrew-vowel">ָ</span><span class="hebrew-accent-major">֗</span>ר<span class="hebrew-vowel">ֶ</span>ץ</span> <span class="w H1961" data-lemma="H1961">ה<span class="hebrew-vowel">ָ</span>י<span class="hebrew-vowel">ְ</span>ת<span class="hebrew-vowel">ָ</span><span class="hebrew-accent-minor">֥</span>ה</span> <span class="w H8414" data-lemma="H8414">ת<span class="hebrew-vowel">ֹ</span><span class="hebrew-accent-major">֙</span>הו<span class="hebrew-vowel">ּ</span><span class="hebrew-accent-major">֙</span></span> <span class="w H922" data-lemma="H922">ו<span class="hebrew-vowel">ָ</span>ב<span class="hebrew-vowel">ֹ</span><span class="hebrew-accent-major">֔</span>הו<span class="hebrew-vowel">ּ</span></span> <span class="w H2822" data-lemma="H2822">ו<span class="hebrew-vowel">ְ</span>ח<span class="hebrew-vowel">ֹ</span><span class="hebrew-accent-major">֖</span>ש<span class="hebrew-vowel">ׁ</span><span class="hebrew-vowel">ֶ</span>ך<span class="hebrew-vowel">ְ</span></span> <span class="w H5921" data-lemma="H5921">ע<span class="hebrew-vowel">ַ</span>ל</span><span class="w "></span>־<span class="w H6440" data-lemma="H6440">פ<span class="hebrew-vowel">ּ</span><span class="hebrew-vowel">ְ</span>נ<span class="hebrew-vowel">ֵ</span><span class="hebrew-accent-minor">֣</span>י</span> <span class="w H8415" data-lemma="H8415">ת<span class="hebrew-vowel">ְ</span>ה<span class="hebrew-accent-major">֑</span>ו<span class="hebrew-vowel">ֹ</span>ם</span> <span class="w H7307" data-lemma="H7307">ו<span class="hebrew-vowel">ְ</span>ר<span class="hebrew-accent-minor">֣</span>ו<span class="hebrew-vowel">ּ</span>ח<span class="hebrew-vowel">ַ</span></span> <span class="w H430" data-lemma="H430">א<span class="hebrew-vowel">ֱ</span>ל<span class="hebrew-vowel">ֹ</span>ה<span class="hebrew-vowel">ִ</span><span class="hebrew-accent-major">֔</span>ים</span> <span class="w H7363" data-lemma="H7363">מ<span class="hebrew-vowel">ְ</span>ר<span class="hebrew-vowel">ַ</span>ח<span class="hebrew-vowel">ֶ</span><span class="hebrew-accent-major">֖</span>פ<span class="hebrew-vowel">ֶ</span>ת</span> <span class="w H5921" data-lemma="H5921">ע<span class="hebrew-vowel">ַ</span>ל</span><span class="w "></span>־<span class="w H6440" data-lemma="H6440">פ<span class="hebrew-vowel">ּ</span><span class="hebrew-vowel">ְ</span>נ<span class="hebrew-vowel">ֵ</span><span class="hebrew-accent-minor">֥</span>י</span> <span class="w H4325" data-lemma="H4325">ה<span class="hebrew-vowel">ַ</span>מ<span class="hebrew-vowel">ּ</span><span class="hebrew-vowel">ָ</span><span class="hebrew-vowel">ֽ</span>י<span class="hebrew-vowel">ִ</span>ם</span><span class="w "></span>׃</span>
			</td>
</tr>
<tr>
<th>Double Layered</th>
<td dir="rtl">
				<span class="hebrew"><span class="w H776 hebrew-layers" data-lemma="H776"><span class="hebrew-layers-original">וְהָאָ֗רֶץ</span><span class="hebrew-layers-vowels">וְהָאָרֶץ</span><span class="hebrew-layers-accents">והא֗רץ</span><span class="hebrew-layers-consonants">והארץ</span></span> <span class="w H1961 hebrew-layers" data-lemma="H1961"><span class="hebrew-layers-original">הָיְתָ֥ה</span><span class="hebrew-layers-vowels">הָיְתָה</span><span class="hebrew-layers-accents">הית֥ה</span><span class="hebrew-layers-consonants">היתה</span></span> <span class="w H8414 hebrew-layers" data-lemma="H8414"><span class="hebrew-layers-original">תֹ֙הוּ֙</span><span class="hebrew-layers-vowels">תֹהוּ</span><span class="hebrew-layers-accents">ת֙הו֙</span><span class="hebrew-layers-consonants">תהו</span></span> <span class="w H922 hebrew-layers" data-lemma="H922"><span class="hebrew-layers-original">וָבֹ֔הוּ</span><span class="hebrew-layers-vowels">וָבֹהוּ</span><span class="hebrew-layers-accents">וב֔הו</span><span class="hebrew-layers-consonants">ובהו</span></span> <span class="w H2822 hebrew-layers" data-lemma="H2822"><span class="hebrew-layers-original">וְחֹ֖שֶׁךְ</span><span class="hebrew-layers-vowels">וְחֹשֶׁךְ</span><span class="hebrew-layers-accents">וח֖שך</span><span class="hebrew-layers-consonants">וחשך</span></span> <span class="w H5921 hebrew-layers" data-lemma="H5921"><span class="hebrew-layers-original">עַל</span><span class="hebrew-layers-vowels">עַל</span><span class="hebrew-layers-accents">על</span><span class="hebrew-layers-consonants">על</span></span><span class="w  hebrew-layers"><span class="hebrew-layers-original"></span><span class="hebrew-layers-vowels"></span><span class="hebrew-layers-accents"></span><span class="hebrew-layers-consonants"></span></span>־<span class="w H6440 hebrew-layers" data-lemma="H6440"><span class="hebrew-layers-original">פְּנֵ֣י</span><span class="hebrew-layers-vowels">פְּנֵי</span><span class="hebrew-layers-accents">פנ֣י</span><span class="hebrew-layers-consonants">פני</span></span> <span class="w H8415 hebrew-layers" data-lemma="H8415"><span class="hebrew-layers-original">תְה֑וֹם</span><span class="hebrew-layers-vowels">תְהוֹם</span><span class="hebrew-layers-accents">תה֑ום</span><span class="hebrew-layers-consonants">תהום</span></span> <span class="w H7307 hebrew-layers" data-lemma="H7307"><span class="hebrew-layers-original">וְר֣וּחַ</span><span class="hebrew-layers-vowels">וְרוּחַ</span><span class="hebrew-layers-accents">ור֣וח</span><span class="hebrew-layers-consonants">ורוח</span></span> <span class="w H430 hebrew-layers" data-lemma="H430"><span class="hebrew-layers-original">אֱלֹהִ֔ים</span><span class="hebrew-layers-vowels">אֱלֹהִים</span><span class="hebrew-layers-accents">אלה֔ים</span><span class="hebrew-layers-consonants">אלהים</span></span> <span class="w H7363 hebrew-layers" data-lemma="H7363"><span class="hebrew-layers-original">מְרַחֶ֖פֶת</span><span class="hebrew-layers-vowels">מְרַחֶפֶת</span><span class="hebrew-layers-accents">מרח֖פת</span><span class="hebrew-layers-consonants">מרחפת</span></span> <span class="w H5921 hebrew-layers" data-lemma="H5921"><span class="hebrew-layers-original">עַל</span><span class="hebrew-layers-vowels">עַל</span><span class="hebrew-layers-accents">על</span><span class="hebrew-layers-consonants">על</span></span><span class="w  hebrew-layers"><span class="hebrew-layers-original"></span><span class="hebrew-layers-vowels"></span><span class="hebrew-layers-accents"></span><span class="hebrew-layers-consonants"></span></span>־<span class="w H6440 hebrew-layers" data-lemma="H6440"><span class="hebrew-layers-original">פְּנֵ֥י</span><span class="hebrew-layers-vowels">פְּנֵי</span><span class="hebrew-layers-accents">פנ֥י</span><span class="hebrew-layers-consonants">פני</span></span> <span class="w H4325 hebrew-layers" data-lemma="H4325"><span class="hebrew-layers-original">הַמָּֽיִם</span><span class="hebrew-layers-vowels">הַמָּֽיִם</span><span class="hebrew-layers-accents">המים</span><span class="hebrew-layers-consonants">המים</span></span> <span class="w  hebrew-layers"><span class="hebrew-layers-original"></span><span class="hebrew-layers-vowels"></span><span class="hebrew-layers-accents"></span><span class="hebrew-layers-consonants"></span></span>׃</span>
			</td>
</tr>
<tbody>
</table>
<p>&nbsp;</p>
<h2>Conclusion</h2>
<p>Right now, it seems there isn&#8217;t a perfect HTML/CSS way of colorizing the Hebrew pointing system, but the layering solution seems best for making it work across browsers as long as you don&#8217;t mind the accents being slightly out of place. </p>
<p>If you have any other ideas, please let me know!</p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/3ATgq14SfyY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/coloring-hebrew-vowels-and-accents-in-htmlcss/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johndyer.name/coloring-hebrew-vowels-and-accents-in-htmlcss/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=coloring-hebrew-vowels-and-accents-in-htmlcss</feedburner:origLink></item>
		<item>
		<title>This Site Best Viewed in Google Glass</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/iwa6HsC_j8Y/</link>
		<comments>http://johndyer.name/this-site-best-viewed-in-google-glass/#comments</comments>
		<pubDate>Wed, 17 Apr 2013 18:31:11 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=326</guid>
		<description><![CDATA[You might have noticed that Google Glass is now shipping to early adopters and people who won the #ifihadglass competition.

http://www.youtube.com/watch?v=v1uyQZNg2vE

Google has also released their <a href="https://developers.google.com/glass/quickstart/">developer documentation</a>, and this means we're about to start hearing about a lot of cool, new apps that people are building for Glass.]]></description>
				<content:encoded><![CDATA[<p><a href="http://johndyer.name/wp-content/uploads/2013/04/Screen-Shot-2013-04-17-at-1.35.20-PM.png"><img class="alignright size-thumbnail wp-image-336" alt="Screen Shot 2013-04-17 at 1.35.20 PM" src="http://johndyer.name/wp-content/uploads/2013/04/Screen-Shot-2013-04-17-at-1.35.20-PM-150x150.png" width="150" height="150" /></a></p>
<h2>Google Glass is Coming!</h2>
<p>You might have noticed that Google Glass is now shipping to early adopters and people who won the #ifihadglass competition.</p>
<p><iframe width="500" height="281" src="http://www.youtube.com/embed/v1uyQZNg2vE?feature=oembed" frameborder="0" allowfullscreen></iframe></p>
<p>Google has also released their <a href="https://developers.google.com/glass/quickstart/">developer documentation</a>, and this means we&#8217;re about to start hearing about a lot of cool, new apps that people are building for Glass.</p>
<p>But sadly most of us won&#8217;t be able to test them out.</p>
<p>To remedy this sad state of affairs, I&#8217;ve come up with a simple tool to simulate using Glass on a website to start giving people ideas of how they might use it. I also really miss the &#8220;Best viewed in Internet Explorer&#8221; days of web development, so I&#8217;m doing what I can to bring back that culture.</p>
<h2>Google Glass jQuery Plugin</h2>
<p>Here&#8217;s the simple code (running on this post) to help people get started:</p>
<pre class="brush: javascript; gutter: true">jQuery(function($) {

	// add Google&#039;s Roboto font
	$(&#039;&lt;link href=&quot;http://fonts.googleapis.com/css?family=Roboto:300&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;&#039;)
		.appendTo( $(&#039;head&#039;) );

	// insert helpful message with base64 image
	$(&#039;&lt;div style=&quot;position: fixed; top: 0; right: 0; background: rgba(0,0,0,0.9); border: solid 1px #eee; color: #fff; font-family: Roboto, Helvetica, sanserif; z-index: 9999;&quot;&gt;&#039; +
			&#039;&lt;div style=&quot;padding: 10%; font-size:3.0em; line-height: 1.5;&quot;&gt;&#039; +				
				&#039;&lt;p&gt;Google Glass not detected.&lt;p&gt;&#039; + 
				&#039;&lt;p&gt;Please upgrade to view the top right corner of this site.&lt;/p&gt;&#039; + 
			&#039;&lt;/div&gt;&#039; +
			&#039;&lt;img alt=&quot;Glass Logo&quot; style=&quot;position: absolute; bottom: 10%; right: 10%;&quot; src=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAA3CAYAAABJnAVSAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpCOUI1NUI1QjA5MjA2ODExODA4M0MyODA3MkUyNTY4MCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0RTIzRjY0NTlGQjAxMUUyQjc2MkYzQTY2MkIzNTg4OSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0RTIzRjY0NDlGQjAxMUUyQjc2MkYzQTY2MkIzNTg4OSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjlCNTVCNUIwOTIwNjgxMTgwODNDMjgwNzJFMjU2ODAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjlCNTVCNUIwOTIwNjgxMTgwODNDMjgwNzJFMjU2ODAiLz4gPC9yZGY6RGVzY3Jp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&quot; /&gt;&#039; + 
		&#039;&lt;/div&gt;&#039;)
		.appendTo( $(&#039;body&#039;) )
		.width( $(window).width() / 2 )
		.height( $(window).height() / 2 );
});</pre>
<h2>How Will You Use It?</h2>
<p>I really hope this gets your ideas flowing.</p>
<p>If you use this incredible powerful new script or come up with any additions, please let me know.</p>
<p><script src="http://johndyer.name/wp-content/uploads/2013/04/jquery.googleglass.js"></script></p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/iwa6HsC_j8Y" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/this-site-best-viewed-in-google-glass/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://johndyer.name/this-site-best-viewed-in-google-glass/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=this-site-best-viewed-in-google-glass</feedburner:origLink></item>
		<item>
		<title>Drawing 3D Objects and Buildings on Google Maps</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/Sqb4Ym-mtn8/</link>
		<comments>http://johndyer.name/drawing-3d-objects-and-building-on-google-maps/#comments</comments>
		<pubDate>Mon, 11 Feb 2013 16:45:44 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[HTML/CSS]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=298</guid>
		<description><![CDATA[As a web developer for a school, I&#8217;ve always enjoyed the challenge of creating a good campus map. The school I work for has a growing online seminary program to train pastors around the world, but helping people get around the main campus in Dallas is a still an integral part of any school. TL;DR: Final result: DTS &#8220;3D&#8221; [...]]]></description>
				<content:encoded><![CDATA[<p>As a web developer for a <a href="http://www.dts.edu/">school</a>, I&#8217;ve always enjoyed the challenge of creating a good campus map. The school I work for has a growing <a href="http://www.dts.edu/onlineeducation/">online seminary</a> program to train pastors around the world, but helping people get around the main campus in Dallas is a still an integral part of any school.</p>
<p>TL;DR: Final result: <a href="http://www.dts.edu/maps/dallascampus/">DTS &#8220;3D&#8221; campus map</a></p>
<p><a href="http://www.dts.edu/maps/dallascampus/"><img src="http://johndyer.name/wp-content/uploads/2013/02/campus-map-sidebyside-768x225.jpg" alt="campus-map-sidebyside" width="768" height="225" class="aligncenter size-large wp-image-320" /></a></p>
<h2>Back Story: The Old Map</h2>
<p>Several years ago, I wanted to make an interactive 3D building map, so I learned <a href="http://code.google.com/p/papervision3d/">Papervision 3D</a> a powerful Flash-based 3D engine (<a href="http://j.hn/lab/campusmap-papervision/">papervision map</a>). I really liked it at the time but now that Flash is largely out of the picture, it was time to replace the map. I&#8217;ve wanted to port the old map to <a href="https://github.com/mrdoob/three.js/">Three.js</a> a JavaScript based 3D engine, but I found that conversion wasn&#8217;t as easy as I thought (<a href="http://j.hn/lab/campusmap-threejs">three.js experiment</a>).</p>
<p><a href="http://johndyer.name/wp-content/uploads/2013/02/Screen-Shot-2013-02-08-at-5.34.43-PM.png"><img alt="Flash based map" src="http://johndyer.name/wp-content/uploads/2013/02/Screen-Shot-2013-02-08-at-5.34.43-PM-768x409.png" width="768" height="409" /></a></p>
<h2>To Google Maps</h2>
<p>Since I also want to give first-class support to mobile devices, I decided to switch gears to Google Maps API since it runs really well on phones and tablets now. The problem was finding a way to display the map in an interesting and clear way.</p>
<h3>Attempt #1: Flat polygons</h3>
<p>My team and I drew out the floor line  of several buildings using Google&#8217;s Polygon tool with editable:true turned on, then we created a little loop to draw them all at once. It looks great, but the problem is that it&#8217;s not really clear that we&#8217;re showing buildings and other than color, it&#8217;s hard to tell what&#8217;s a parking lot.</p>
<pre class="brush: javascript; gutter: true">var 
	mapCenter = [32.794488, -96.780372],
	mapOptions = {
		zoom: dts.maps.current.campus.zoom,
		center: new google.maps.LatLng(mapCenter[0], mapCenter[1]),
		mapTypeId: google.maps.MapTypeId.SATELLITE			

	},
	map = new google.maps.Map(document.getElementById(&quot;map&quot;), mapOptions),
	buildingCoordinates = [
		[32.7948702128665,-96.78071821155265],
		[32.79478789915277,-96.780713852963],
		[32.794745606448785,-96.7807638091059],
		[32.79465594788115,-96.78065450908855],
		[32.794983444026556,-96.78026383449742],
		[32.79507153522898,-96.78037564908573],
		[32.79502490803293,-96.78043113728472],
		[32.79502635107838,-96.78054211368271]	
	],
	polygonCoords = [];

for (var j=0; j&amp;lt;buildingCoordinates.length; j++) {
	polygonCoords.push(new google.maps.LatLng(
		buildingCoordinates[j][0], 
		buildingCoordinates[j][1]
	));	
}       

var polygon = new google.maps.Polygon({
  paths: polygonCoords,
  strokeColor: &#039;#ee1111&#039;,
  strokeOpacity: 0.6,
  strokeWeight: 1,
  fillColor: &#039;#eeeeee&#039;,
  fillOpacity: 0.7
});		

polygon.setMap(map);</pre>
<p><a href="http://johndyer.name/wp-content/uploads/2013/02/campus-map-flat.png"><img class="aligncenter size-large wp-image-305" alt="campus-map-flat" src="http://johndyer.name/wp-content/uploads/2013/02/campus-map-flat-768x480.png" width="768" height="480" /></a></p>
<h3>Attempt #2: Roof</h3>
<p>The next step was trying draw a floating roof simply by copying the the coordinates and adding a little bit to the latitude to make it seem like it was &#8220;up in the air.&#8221; The result doesn&#8217;t really make sense, but it starts to give some building like feeling:</p>
<p><a href="http://johndyer.name/wp-content/uploads/2013/02/campus-map-roofs.png"><img class="aligncenter size-large wp-image-307" alt="campus-map-roofs" src="http://johndyer.name/wp-content/uploads/2013/02/campus-map-roofs-768x480.png" width="768" height="480" /></a></p>
<h3>Attempt #3: Drawing a single &#8220;Wall&#8221;</h3>
<p>My next thought was to remove the floor and then draw to draw a single wall on the South side to make it look like a wrap around wall. To do this, I looked through the coordinates and found the western and eastern edge and then tried to draw along it. It worked in many places, but in complex buildings it looked a little strange since there is only one &#8220;south&#8221; wall.</p>
<p><a href="http://johndyer.name/wp-content/uploads/2013/02/campus-map-single-wall.png"><img class="aligncenter size-large wp-image-308" alt="campus-map-single-wall" src="http://johndyer.name/wp-content/uploads/2013/02/campus-map-single-wall-768x479.png" width="768" height="479" /></a></p>
<h3>Attempt #4: Drawing individual Walls</h3>
<p>To fix the problem of complex buildings, I decided to draw a polygon for each wall. This involves taking each floor coordinate and making a pair with the next one and then stretching it upward. Here&#8217;s what my new function looks like</p>
<pre class="brush: javascript; gutter: true">function drawExcrudedShape(map, coordinates, height, strokeColor, strokeOpacity, strokeWeight, fillColor, fillOpacity) {

	var pairs = [],
		polygons = [];

	// build line pairs for each wall
	for (var i=0; i&lt;coordinates.length; i++) {

		var point = coordinates[i],
			otherIndex = (i == coordinates.length-1) ? 0 : i+1,
			otherPoint = coordinates[otherIndex];

		pairs.push([point, otherPoint]);
	}

	// draw excrusions
	for (var i=0; i&lt;pairs.length; i++) {

		var first = pairs[i][0],
			second = pairs[i][1],
			wallCoordinates =  [
				new google.maps.LatLng(first[0],first[1]),
				new google.maps.LatLng(first[0]+height,first[1]),
				new google.maps.LatLng(second[0]+height,second[1]),
				new google.maps.LatLng(second[0],second[1])									
			],
			polygon = new google.maps.Polygon({
				paths: wallCoordinates,
				strokeColor: strokeColor,
				strokeOpacity: strokeOpacity, 
				strokeWeight: strokeWeight,
				fillColor: fillColor,
				fillOpacity: fillOpacity
				zIndex: zIndexBase+i
			});

		polygon.setMap(map);

		polygons.push(polygon);
	}		

	return polygons;
}</pre>
<p>Here is the result:</p>
<p><a href="http://johndyer.name/wp-content/uploads/2013/02/campus-map-multi-wall.png"><img class="aligncenter size-large wp-image-306" alt="campus-map-multi-wall" src="http://johndyer.name/wp-content/uploads/2013/02/campus-map-multi-wall-768x478.png" width="768" height="478" /></a></p>
<p>This looks much better, but now we have two problems. First, some walls incorrectly overlap since I haven&#8217;t explicitly told Google the correct order to draw them in z-index problem. Second, if you were to rotate the map 180 degrees (see below), the buildings would be upside-down. This is because I&#8217;m not checking which wall is the southern most or the direction of the map.</p>
<p><a href="http://johndyer.name/wp-content/uploads/2013/02/campus-map-upsidedown.png"><img class="aligncenter size-large wp-image-309" alt="campus-map-upsidedown" src="http://johndyer.name/wp-content/uploads/2013/02/campus-map-upsidedown-768x475.png" width="768" height="475" /></a></p>
<h3>Attempt #5: Re-Ordering the Walls</h3>
<p>So in my final attempt, I&#8217;ve taken the pairs above and ordered them based on the Google&#8217;s heading (map.getHeading()). This allows me to figure out which way is &#8220;up&#8221; and correctly layer the walls so that they look like real 3D objects. Here&#8217;s the final function and map result:</p>
<pre class="brush: javascript; gutter: true">
function drawExcrudedShape(map, coordinates, height, zIndexBase, heading, strokeColor, strokeOpacity, strokeWeight, fillColor, fillOpacity) {

	
	var pairs = [],
		polygons = [];
		
	// build line pairs
	for (var i=0; i&lt;coordinates.length; i++) {
	
		var point = coordinates[i],
			otherIndex = (i == coordinates.length-1) ? 0 : i+1,
			otherPoint = coordinates[otherIndex];
	
		pairs.push([point, otherPoint]);
	}
	
	// sort the pairs based on which one has the &quot;lowest&quot; point based on the heading
	pairs.sort(function(a, b) {
		var aLowest = 0,
			bLowest = 0;
			
		switch (heading) {
			case 0:
				aLowest = Math.min(a[0][0], a[1][0]);
				bLowest = Math.min(b[0][0], b[1][0]);	
				
				
				if (aLowest &lt; bLowest) {
					return 1;
				} else if (aLowest &gt; bLowest) {
					return -1;
				} else {
					return 0;
				}						
			case 90:
				aLowest = Math.min(a[0][1], a[1][1]);
				bLowest = Math.min(b[0][1], b[1][1]);	
				
				if (aLowest &lt; bLowest) {
					return 1;
				} else if (aLowest &gt; bLowest) {
					return -1;
				} else {
					return 0;
				}						
	
			case 180:
				aLowest = Math.max(a[0][0], a[1][0]);
				bLowest = Math.max(b[0][0], b[1][0]);	
				
				
				if (aLowest &gt; bLowest) {
					return 1;
				} else if (aLowest &lt; bLowest) {
					return -1;
				} else {
					return 0;
				}	
			
			case 270:
				aLowest = Math.max(a[0][1], a[1][1]);
				bLowest = Math.max(b[0][1], b[1][1]);	
				
				if (aLowest &gt; bLowest) {
					return 1;
				} else if (aLowest &lt; bLowest) {
					return -1;
				} else {
					return 0;
				}	
		
		} 
	});
			
	// draw excrusions
	for (var i=0; i&lt;pairs.length; i++) {
		
		var first = pairs[i][0],
			second = pairs[i][1],
			wallCoordinates = null;
			
		switch (heading) {
			case 0:
				wallCoordinates = [
					new google.maps.LatLng(first[0],first[1]),
					new google.maps.LatLng(first[0]+height,first[1]),
					new google.maps.LatLng(second[0]+height,second[1]),
					new google.maps.LatLng(second[0],second[1])									
				];
				break;
				
			case 90:
				wallCoordinates = [
					new google.maps.LatLng(first[0],first[1]),
					new google.maps.LatLng(first[0],first[1]+height),
					new google.maps.LatLng(second[0],second[1]+height),
					new google.maps.LatLng(second[0],second[1])									
				];
				break;
				
			case 180:
				wallCoordinates = [
					new google.maps.LatLng(first[0],first[1]),
					new google.maps.LatLng(first[0]-height,first[1]),
					new google.maps.LatLng(second[0]-height,second[1]),
					new google.maps.LatLng(second[0],second[1])									
				];
				break;
				
			case 270:
				wallCoordinates = [
					new google.maps.LatLng(first[0],first[1]),
					new google.maps.LatLng(first[0],first[1]-height),
					new google.maps.LatLng(second[0],second[1]-height),
					new google.maps.LatLng(second[0],second[1])									
				];
				break;
		}				
			
		var polygon = new google.maps.Polygon({
			paths: wallCoordinates,
			strokeColor: strokeColor,
			strokeOpacity: strokeOpacity, 
			strokeWeight: strokeWeight,
			fillColor: fillColor,
			fillOpacity: fillOpacity, 
			zIndex: zIndexBase+i
		});
		
		polygon.setMap(map);
		
		polygons.push(polygon);
	}		
	
	return polygons;
}</pre>
<h3>Final Map</h3>
<p>Here is the final result. We&#8217;ve changed the parking lots to just have a colored border to help people know where to park and the full map has some interactivity on the buildings, lots, and departments. Go give it a try!</p>
<p><a href="http://www.dts.edu/maps/dallascampus/"><img class="aligncenter size-full wp-image-304" alt="campus-map-final" src="http://johndyer.name/wp-content/uploads/2013/02/campus-map-final.png" width="746" height="485" /></a></p>
<p>Thanks to <a href="http://googlemapsmania.blogspot.com/">Google Maps Mania</a> for the kind words <a href="http://googlemapsmania.blogspot.com/2013/02/3d-buildings-in-google-maps.html">here</a> and <a href="http://googlemapsmania.blogspot.com/2013/02/creating-3d-buildings-with-google-maps.html">here</a>.</p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/Sqb4Ym-mtn8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/drawing-3d-objects-and-building-on-google-maps/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://johndyer.name/drawing-3d-objects-and-building-on-google-maps/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=drawing-3d-objects-and-building-on-google-maps</feedburner:origLink></item>
		<item>
		<title>How to Create a Responsive, Retina-ready Website</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/X3hyCniHxm8/</link>
		<comments>http://johndyer.name/how-to-create-a-responsive-retina-ready-website/#comments</comments>
		<pubDate>Wed, 12 Sep 2012 18:06:35 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[HTML/CSS]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=279</guid>
		<description><![CDATA[My team and I at Dallas Theological Seminary just finished putting together a new site that has both a responsive layout and Retina graphics, and I thought it would be worth documenting the decisions we made along the way and some of the tools we used. We are a pretty small team (we hired the [...]]]></description>
				<content:encoded><![CDATA[<p>My team and I at <a href="http://www.dts.edu/">Dallas Theological Seminary</a> just finished putting together a new site that has both a responsive layout and Retina graphics, and I thought it would be worth documenting the decisions we made along the way and some of the tools we used.</p>
<p>We are a pretty small team (we hired the awesome <a href="http://pixelightcreative.com/">Chris Merritt</a> to do the design and our 1.5 person team [including the awesome <a href="http://www.michaeljordanmedia.com/">Michael Jordan</a>] at DTS did the coding) and had a very tight window (2.5 months) and fairly large site (700+ pages and 2000+ videos and blog posts), so that led us to make some strategic decisions (i.e. compromises) to get it done.</p>
<p><a href="http://www.dts.edu/"><img class="aligncenter size-full wp-image-280" title="DTS webiset side-by-side" src="http://johndyer.name/wp-content/uploads/2012/09/side-by-side.jpg" alt="" width="750" height="340" /></a></p>
<h2>Responsive Layout Plan</h2>
<p>In the past we had a separate mobile site (mobile.dts.edu) and I wanted to move to a single site with a responsive layout. We surveyed a lot of responsive sites and found quite a few approaches and ways we could go with it.</p>
<h3>Some Great Options</h3>
<p>Some have several very different layouts for each of the following: (1) desktop, (2) portrait tablet, and (3) mobile phones. A good example of this is <a href="http://www.BarackObama.com">www.BarackObama.com</a> which rearranges its content for each size. Obama’s site is an amazing achievement, but from my perspective there are two main issues. First, it requires an incredible amount of work and time which we didn’t. Second, I personally find that some sites are so different in each layout that its hard to remember or guess where anything will be. For example, in Obama&#8217;s case, the big three buttons under the header such as &#8220;Get the Facts&#8221; are missing in the tablet layout and the &#8220;Donate&#8221; button gets moved to a more prominent spot.</p>
<p><a href="http://johndyer.name/wp-content/uploads/2012/09/barackobama.com_.jpg"><img class="aligncenter size-full wp-image-284" title="barackobama.com" src="http://johndyer.name/wp-content/uploads/2012/09/barackobama.com_.jpg" alt="barackobama.com" width="750" height="352" /></a></p>
<p>Other sites use a fluid grid to handle both desktop and tablet and then when the screen is small enough (like a phone), the layout collapses. A good example of this is <a href="http://www.marshill.com">www.marshill.com</a>. I like this simpler and more straightforward approach, but a fluid grid layout wouldn’t really fit with what we wanted to do design wise on many of our subpages.</p>
<p><a href="http://johndyer.name/wp-content/uploads/2012/09/marshill.com_.jpg"><img class="aligncenter size-full wp-image-285" title="marshill.com" src="http://johndyer.name/wp-content/uploads/2012/09/marshill.com_.jpg" alt="marshill.com" width="750" height="352" /></a></p>
<h3>Our Modified Approach</h3>
<p>About 5% of our users are on tablets and 5% on mobile phones, and it seems that tablet users seem pretty adept at pinching and zooming. This lead us to a pretty simple approach: (1) a desktop/tablet site, (2) a mobile phone site where our grid collapses (see above).</p>
<p>While changing responsive layouts for mobile devices gets a lot of attention, we also wanted to take into account different desktop sizes. This lead us to create a kind of “bleed” that allows the site to look good on older 1024&#215;768 monitors, traditional 1440&#215;900 laptops, and much larger screens. For our  90% desktop users, we now have a site that is &#8220;responsive&#8221; to their screen sizes without actually modifying the structural layout and placement of content.</p>
<p><a href="http://www.dts.edu/"><img class="aligncenter size-full wp-image-282" title="Layout Sizes" src="http://johndyer.name/wp-content/uploads/2012/09/layout-sizes.jpg" alt="Layout Sizes" width="750" height="451" /></a></p>
<p><a href="http://www.dts.edu/onlineeducation/"><img class="aligncenter size-full wp-image-286" title="layout-sizes-2" src="http://johndyer.name/wp-content/uploads/2012/09/layout-sizes-2.jpg" alt="Layout sizes Online Ed" width="750" height="451" /></a></p>
<h2>Retina (pixel-density: 2.0) Graphics</h2>
<p>Right now there are only three main devices that have “Retina” graphics (iPhone4+, MacBook Retina, and iPad 3), but I&#8217;m betting that the clones will be arriving very soon and over the life of this site Retina graphics will become an important distinctive, so I wanted to plan ahead to make things look good now and in the future.</p>
<h3>Logos and arrows as SVG</h3>
<p>Anywhere we have a graphic that’s not a picture (logo, arrow, symbol, etc.) I used SVG (with a fallback PNG). If you go to our site, you’ll see SVGs in our logo, the search icon, and even the social media icons in the footer (roll over the Apple logo for a fun surprise). On a normal computer, the SVG and PNG don’t really look much different, but the difference on an iPhone or MacBook Retina is quite noticeable.</p>
<p><a href="http://johndyer.name/wp-content/uploads/2012/09/img-vs-svg.jpg"><img class="aligncenter size-full wp-image-283" title="Images vs SVG on Retina" src="http://johndyer.name/wp-content/uploads/2012/09/img-vs-svg.jpg" alt="Images vs SVG on Retina" width="750" height="400" /></a></p>
<p>To create the SVGs, I&#8217;ve been using Fireworks and Illustrator together. Illustrator can natively work with SVGs, but I couldn’t find a good way to work in pixels to produce the fallback PNGs I wanted for IE8. Fireworks is much better in my opinion for this kind of thing, but it doesn’t have native SVG support (seriously, Adobe?). Here&#8217;s the workflow I came up with:</p>
<ol>
<li>Use Fireworks to create an icon and save as a Fireworks PNG which retains all vector data (myicon.fw.png). <strong>Note</strong>: Sometimes I made simple vectors like arrows myself, but I also used some EPS and AI files or the SVGs on <a href="http://www.thenounproject.com">www.thenounproject.com</a> and elsewhere. The trick is that you have to open many of these in Illustrator, select the vectors you want, then copy them into Fireworks. Yuck, but it works.</li>
<li>Save a flattened PNG for older browsers that don’t support SVG (myicon.png). Make sure you keep this separate from the original Fireworks file (myicon.fw.png) so you can work with the vector data later.</li>
<li>Fireworks can’t natively save in SVG, so I used this <a href="http://fireworks.abeall.com/extensions/commands/Export/">amazing “Export SVG” script</a> which does everything you need to create (myicon.svg). So far, I haven’t run into any problems with my images.</li>
<li>I used the SVG/PNG combo as background images and relied on a class on the &lt;html&gt; tag to tell me which one I needed. I initially was going to use SVG detection in <a href="http://modernizr.com/">Moderizr</a>, but I went ahead with the <a href="http://www.HTML5Boilerplate.com">HTML5 Boilerplate’s</a> approach of using IE’s conditional comments since I didn&#8217;t want to wait until JavaScript fired and possibly download twice. So I ended up with CSS like this:</li>
</ol>
<div>
<pre class="brush: css; gutter: true">/* SVG for modern browsers */
.logo { 
   background-image:url(&#039;myicon.svg&#039;); 
}
/* applied with IE conditional tags */
.lt-ie9 .logo { 
   background-image:url(&#039;myicon.png&#039;); 
}</pre>
</div>
<h3>Pictures with HTML5-ish Markup and JavaScript</h3>
<p>The W3C hasn’t quite come to a consensus on a <a href="http://www.w3.org/community/respimg/wiki/Picture_Element_Proposal">new &lt;picture&gt;</a> element and how the &lt;source&gt; elements should work, so right now you have to roll your own or choose a library. Here was my criteria: (1) Support everyone with a client side approach (no server-side logic), (2) Don’t force Retina users to download images twice (like Apple.com does!), (3) Distinguish between Retina devices with large screens (MacBooks and iPad 3) from those with small screens (iPhones 4).</p>
<p>I decided to go with <a href="https://github.com/scottjehl/picturefill">PictureFill.js by Scott Jehl</a> and use the version that employs &lt;div&gt;s with data-* attributes and a &lt;noscript&gt; tag. On the the backend I created a simple function (in my case it’s in C#) that renders the markup (like &lt;% WriteDoubleImage(&#8220;image.jpg&#8221;,&#8221;image@2x.jpg&#8221;) %&gt;), so that I can switch to something like <a href="https://github.com/Wilto/picturefill-proposal">Wilto’s fork</a> that uses &lt;picture&gt; elements or another tool later on when/if browsers start using it. I&#8217;m currently only using it for in-page images (not backgrounds) on some key areas of the site, but I hope to add more soon.</p>
<pre class="brush: javascript; gutter: true">&lt;!-- Scott Jehl&#039;s markup --&gt;
&lt;div data-picture data-alt=&quot;Alt text&quot;&gt;
    &lt;div data-src=&quot;image.jpg&quot;&gt;&lt;/div&gt;
    &lt;div data-src=&quot;image@2x.jpg&quot; data-media=&quot;(-webkit-min-device-pixel-ratio: 2.0)&quot;&gt;&lt;/div&gt;
    &lt;noscript&gt;&lt;img src=&quot;small.jpg&quot; alt=&quot;Alt text&quot;&gt;&lt;/noscript&gt;
&lt;/div&gt;</pre>
<pre class="brush: javascript; gutter: true">&lt;!-- proposed W3C markup --&gt;
&lt;picture alt=&quot;Alt text&quot;&gt;
    &lt;source src=&quot;image.jpg 1x, image@2x.jpg 2x&quot; /&gt;
    &lt;img src=&quot;image.jpg&quot; alt=&quot;Alt text&quot;&gt;
&lt;/div&gt;</pre>
<p>On our homepage, the video thumbnails have a Retina (double-pixel) image for Retina devices and a normal image for everyone else. But the hero rotator only sends Retina graphics to large displays (iPads 3 and MacBook Retina) and sends normal graphics to non-Retina machines and Retina iPhones. On the desktop the hero graphic is 500px wide (1000px on a Retina desktop), but on mobile it&#8217;s only 120px (240px Retina), so the mobile Retina only needs the original 500px image to look good. Here&#8217;s one of the video thumbnails:</p>
<p><a href="http://johndyer.name/wp-content/uploads/2012/09/Retina-images.jpg"><img class="aligncenter size-full wp-image-293" title="Retina image vs. non-Retina" src="http://johndyer.name/wp-content/uploads/2012/09/Retina-images.jpg" alt="Retina image vs. non-Retina" width="750" height="400" /></a></p>
<p>For the Rotator, we also split the image into a JPG and PNG, so that the PNG is just the little sliver that pops above the rotator into the area above (see the top of <a href="http://culture-making.com">Andy Crouch</a>&#8216;s head in the first image of this post). If I had made it one PNG it might be 300KB, but the JPG is only 40KB plus a 10K PNG, so this split approach vastly reduces the filesize. Finally, we made the move to Amazon S3 to better handle all the large Retina files.</p>
<p>Here&#8217;s the entire site on a Retina screen (click for 4MB file)</p>
<p><a href="http://johndyer.name/wp-content/uploads/2012/09/www.dts_.edu-retina.jpg"><img class="aligncenter size-medium wp-image-294" title="www.dts.edu retina" src="http://johndyer.name/wp-content/uploads/2012/09/www.dts_.edu-retina-157x300.jpg" alt="www.dts.edu retina" width="157" height="300" /></a></p>
<h2>Room for Improvement, Abandoning Media Queries?</h2>
<p>There are still some areas of the site we’d like to go back and add double-pixel images or SVG, and there are a few places where the mobile layout could be tightened up a bit. But the biggest consideration I have going forward is possibly abandoning media queries so that phone users can see the desktop site if they want to.</p>
<p>Here’s how responsive CSS works today (using a desktop first approach):</p>
<pre class="brush: css; gutter: true">#container {
    width: 1024px;
}
@media only screen and (max-width: 480px) {
    #container {
        width: auto;
    }
}</pre>
<p>But if users hit your site with a phone looking for something that they remember seeing when on a desktop, they have no way of making their phone display the desktop version. One way to solve this is to selectively insert different stylesheets like using something like Nathan Smith’s <a href="http://adapt.960.gs/">adapt.js</a>. This way you could turn it off and allow a phone to see a desktop site. Alternatively, you could add a CSS class to root &lt;html&gt; that the user could toggle according to preference. The CSS would look like (SASS/LESS would make this much easier)</p>
<pre class="brush: css; gutter: true">/* default size */
#container {
    width: 1024px;
}
/* class applied to &lt;html&gt; tag */
.lt-480 #container {
    width: auto;
}</pre>
<p>I&#8217;m not sure which approach we&#8217;ll go with or if we really need to make the change, but for now we have a pretty solid basis to work from as new devices come on the market. The final consideration I&#8217;d like to make is using a Facebook/BarackObama.com like side menu for mobile instead of a drop list.</p>
<p>If you have any other tips you&#8217;ve learned while creating Retina or Responsive sites, please share them!</p>
<p>And go check out <a href="http://www.dts.edu/">http://www.dts.edu/</a> !</p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/X3hyCniHxm8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/how-to-create-a-responsive-retina-ready-website/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		<feedburner:origLink>http://johndyer.name/how-to-create-a-responsive-retina-ready-website/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=how-to-create-a-responsive-retina-ready-website</feedburner:origLink></item>
		<item>
		<title>HTML5 Audio Karaoke – a JavaScript audio text aligner</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/vsYkokemgcI/</link>
		<comments>http://johndyer.name/html5-audio-karoke-a-javascript-audio-text-aligner/#comments</comments>
		<pubDate>Fri, 01 Jun 2012 14:55:18 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[Bible Tools]]></category>
		<category><![CDATA[HTML/CSS]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=265</guid>
		<description><![CDATA[What it Does Based on some amazing work by my friend Weston Ruter, I&#8217;ve put together a little library that mashes together some text (usually some HTML) an audio source reading that text (usually an mp3) a timing file (in this case, generated by CMU Sphinx) The result is that when you press &#8220;play&#8221; the [...]]]></description>
				<content:encoded><![CDATA[<h3>What it Does</h3>
<p>Based on <a href="https://github.com/westonruter/esv-text-audio-aligner">some amazing work</a> by my friend <a href="http://weston.ruter.net/">Weston Ruter</a>, I&#8217;ve put together a little library that mashes together</p>
<ol>
<li>some text (usually some HTML)</li>
<li>an audio source reading that text (usually an mp3)</li>
<li>a timing file (in this case, generated by <a href="http://cmusphinx.sourceforge.net/">CMU Sphinx</a>)</li>
</ol>
<p>The result is that when you press &#8220;play&#8221; the words are highlighted as they are read, and you can click on words to navigate through the audio. The magic comes from data produced by the <a href="http://cmusphinx.sourceforge.net/">CMU Sphinx</a> library (based on Weston&#8217;s work) which creates the word timing information.</p>
<p>I put together two demo versions, one of <a href="http://j.hn/lab/html5karaoke/dream.html">Martin Luther King, Jr.&#8217;s <em>I Have A Dream</em></a> speech and another one of the <a href="http://j.hn/lab/html5karaoke/">English Bible</a> using the English Standard Version which has as <a href="http://www.gnpcb.org/esv/share/services/">great API</a>. Unfortunately, the MLK speech didn&#8217;t align very well so the demo isn&#8217;t very good other than as an example of how dependent the process is on a good alignment.</p>
<table width="100%">
<tbody>
<tr>
<td width="50%">
<p style="text-align: center;"><a href="http://j.hn/lab/html5karaoke/dream.html"><img class="aligncenter size-medium wp-image-271" title="I have a dream" src="http://johndyer.name/wp-content/uploads/2012/06/Screen-Shot-2012-06-01-at-9.48.15-AM-300x112.png" alt="" width="300" height="112" /></a></p>
</td>
<td width="50%"><a href="http://j.hn/lab/html5karaoke/"><img class="aligncenter size-medium wp-image-266" title="HTML5 Karoke" src="http://johndyer.name/wp-content/uploads/2012/05/Screen-Shot-2012-05-16-at-9.55.02-AM-300x151.png" alt="" width="300" height="151" /></a></td>
</tr>
</tbody>
</table>
<p>(note: right now it&#8217;s Chrome/Safari/IE9 only since it requires MP3 playback)</p>
<h3>How it Works</h3>
<p>Although I wanted to use a &#8220;standard&#8221; format like <a href="http://dev.w3.org/html5/webvtt/">WebVTT</a>, I also wanted the filesize to be compact since my intended project involved large datasets of 48 hours or more of audio (i.e. the Bible). So here&#8217;s the basic JSON format:</p>
<pre class="brush: javascript; gutter: true; first-line: 1; highlight: []; html-script: false">{&quot;words&quot;:[
 [&quot;in&quot;,0.03,0.18],
 [&quot;the&quot;,0.18,0.28],
 [&quot;beginning&quot;,0.28,0.88],
 [&quot;god&quot;,0.88,1.35],
 [&quot;created&quot;,1.35,1.93]
]}</pre>
<p>Basically, it&#8217;s just an array of words with a start and end time. The array of arrays format is quite a bit smaller than using JSON and doesn&#8217;t require any processing like WebVTT (although that might change later). It would take quite a bit of time to produce something like this by hand, but Weston used the <a href="http://cmusphinx.sourceforge.net/">CMU Sphinx</a> library to generate this data, and it&#8217;s probably been about 90% accurate for the entire ESV Bible.</p>
<p>Once all the data is loaded, the AudioAligner class searches through a DOM node for the words in the array, skipping over classes or tags you define, and then links those words to the audio player.</p>
<h3>Demo</h3>
<p>Again, the demo I put together utilizes <a href="http://www.gnpcb.org/esv/share/services/">the API</a> provided by the creators of the English Standard Version (ESV) of the Bible. The API allows developers to request the text and the MP3 and then this is mashed up with the timing files generated with SMU Sphinx.</p>
<p><a class="post-button demo" href="http://j.hn/lab/html5karaoke/">HTML5 Karoke Demo</a></p>
<p>If anyone&#8217;s interested in the library, please let me know in the comments and I&#8217;ll post it to Github.</p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/vsYkokemgcI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/html5-audio-karoke-a-javascript-audio-text-aligner/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		<feedburner:origLink>http://johndyer.name/html5-audio-karoke-a-javascript-audio-text-aligner/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=html5-audio-karoke-a-javascript-audio-text-aligner</feedburner:origLink></item>
		<item>
		<title>Cross-Browser Native Get/Set Properties in JavaScript</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/dzwYijEoIb8/</link>
		<comments>http://johndyer.name/native-browser-get-set-properties-in-javascript/#comments</comments>
		<pubDate>Wed, 02 May 2012 20:54:50 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Video]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=252</guid>
		<description><![CDATA[JavaScript Doesn&#8217;t Support Properties When I first created MediaElement.js a few years ago, I wanted to make a JavaScript object that fully mimicked the HTML5 Media API, but under the hood it might have a true &#60;video&#62; tag or a Flash object doing the rendering. The problem I quickly found was that the &#60;video&#62; tag has several properties [...]]]></description>
				<content:encoded><![CDATA[<h3>JavaScript Doesn&#8217;t Support Properties</h3>
<p>When I first created <a href="http://mediaelementjs.com/">MediaElement.js</a> a few years ago, I wanted to make a JavaScript object that fully mimicked the HTML5 Media API, but under the hood it might have a true <code>&lt;video&gt;</code> tag or a Flash object doing the rendering.</p>
<p>The problem I quickly found was that the <code>&lt;video&gt;</code> tag has several properties like <code>.src</code> and <code>.volume</code> that can&#8217;t be replicated since JavaScript doesn&#8217;t have true get/set capability that works in all browsers (by &#8220;all browsers&#8221; I mean &#8220;IE6 and up&#8221;). So I had to create methods like <code>.setSrc()</code> and <code>.setVolume()</code> (or use jQuery&#8217;s <code>.volume()</code> syntax) to make it work correctly across browsers, but that resulted in a API that didn&#8217;t match the HTML5 spec.</p>
<h3>Ah, But JavaScript <em>Does</em> Support Properties</h3>
<p>A long time ago, in Firefox&#8217;s distant past there was a proprietary way to create properties, using the <code><a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/DefineGetter">__defineGetter__</a></code> and <code>__defineSetter__</code>methods. Here&#8217;s what it looks like:</p>
<pre class="brush: javascript; gutter: true">var myObject = {};
myObject.seconds = 22;
myObject.__defineGetter__(&#039;milliseconds&#039;, function() { return this.seconds / 1000;} );
myObject.__defineSetter__(&#039;milliseconds&#039;, function(value) { this.seconds = value * 1000;});

myObject.milliseconds = 1750;
console.log(myObject.seconds); // outputs 1.75;</pre>
<p>It&#8217;s great because it allows you to easily define properties that can do additional calculation work (e.g., from milliseconds to seconds), but it doesn&#8217;t work in all other browsers. The method that <em>is</em> more universally available is <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty">Object.defineProperty</a> which offers a slightly cleaner syntax:</p>
<pre class="brush: javascript; gutter: true">var myObject = {};
myObject.seconds = 22;

Object.defineProperty(obj, &#039;milliseconds&#039;, {
     get: function() { return this.seconds / 1000;},
     set: function(value) { this.seconds = value * 1000;}
});

myObject.milliseconds = 1750;
console.log(myObject.seconds); // outputs 1.75;</pre>
<p>Looks super useful right? So why doesn&#8217;t anyone use it?</p>
<p>The problem is that IE6 and IE7 don&#8217;t support it, and while IE8 does support the <code>Object.defineProperty</code> method, it sadly only works on DOM objects that are attached to the tree.</p>
<p>So, if you really, really needed an object that supported properties, you can create a DOM object, attach it to the document, and then use <code>Object.defineProperty</code> to give it some properties. That&#8217;s a lot of trouble just to support IE8, and you still don&#8217;t get IE6 or IE7 support, so I don&#8217;t know of any libraries that currently use the approach.</p>
<h3>Hacking IE6 and IE7</h3>
<p>I recently came across a technique that I haven&#8217;t seen demonstrated before from <a href="http://thewikies.com/">Jonathan Neal</a> using the <a href="http://msdn.microsoft.com/en-us/library/ie/ms536956(v=vs.85).aspx"><code>onpropertychanged</code></a> event. IE will fire this event when any property, native or added by a developer, is changed. However, like IE8 this technique only works on a real DOM object, not a plain old <code>{}</code>.</p>
<p>Based on Jonathan&#8217;s work, here is a function that will add a property and work in IE6 and up.</p>
<pre class="brush: javascript; gutter: true">// Super amazing, cross browser property function, based on http://thewikies.com/
function addProperty(obj, name, onGet, onSet) {

	// wrapper functions
	var
		oldValue = obj[name],
		getFn = function () {
			return onGet.apply(obj, [oldValue]);
		},
		setFn = function (newValue) {
			return oldValue = onSet.apply(obj, [newValue]);
		};

	// Modern browsers, IE9+, and IE8 (must be a DOM object),
	if (Object.defineProperty) {

		Object.defineProperty(obj, name, {
			get: getFn,
			set: setFn
		});

	// Older Mozilla
	} else if (obj.__defineGetter__) {

		obj.__defineGetter__(name, getFn);
		obj.__defineSetter__(name, setFn);

	// IE6-7
	// must be a real DOM object (to have attachEvent) and must be attached to document (for onpropertychange to fire)
	} else {

		var onPropertyChange = function (e) {

			if (event.propertyName == name) {
				// temporarily remove the event so it doesn&#039;t fire again and create a loop
				obj.detachEvent(&quot;onpropertychange&quot;, onPropertyChange);

				// get the changed value, run it through the set function
				var newValue = setFn(obj[name]);

				// restore the get function
				obj[name] = getFn;
				obj[name].toString = getFn;

				// restore the event
				obj.attachEvent(&quot;onpropertychange&quot;, onPropertyChange);
			}
		};	

		obj[name] = getFn;
		obj[name].toString = getFn;

		obj.attachEvent(&quot;onpropertychange&quot;, onPropertyChange);

	}
}

// must be a DOM object (even if it&#039;s not a real tag) attached to document
var myObject = document.createElement(&#039;fake&#039;);
document.body.appendChild(myObject);

// create property
myObject.firstName = &#039;John&#039;;
myObject.lastName = &#039;Dyer&#039;;
addProperty(myObject, &#039;fullname&#039;,
	function() {
		return this.firstName + &#039; &#039; + this.lastName;
	},
	function(value) {
		var parts = value.split(&#039; &#039;);
		this.firstName = parts[0];
		this.lastName = (parts.length &gt; 1) ? parts[1] : &#039;&#039;;
	});

console.log(myObject.fullname); // returns &#039;John Dyer&#039;</pre>
<p>Pretty aweseome.</p>
<h3>Real World Application</h3>
<p>Mozilla&#8217;s April <a href="https://developer.mozilla.org/en-US/demos/devderby">Dev Derby</a> was on using the &lt;audio&gt; tag, so I thought it&#8217;d be a fun chance to try this out and make a native looking HTML5 API that could wrap things like a Flash Ogg player or the amazing <a href="http://jsmad.org/">JsMad</a> library which can play MP3s using pure JavaScript (great for Firefox which can&#8217;t play MP3s natively).</p>
<p>The result is a fun little audio library called <a href="https://developer.mozilla.org/en-US/demos/detail/shimichanga">Shimichanga</a> that has functional properties like <code>.src</code>. What&#8217;s interesting about the library is that when you set the <code>.src</code> property, the set method loads an appropriate renderer (HTML5, Flash, or JavaScript) depending on the browser&#8217;s capability and then sends the value to renderer to use, all seemless to the developer and end-user. <em>Note: Mozilla didn&#8217;t allow the flash shim part, so to see a full working demo go to: <a href="http://shimichanga.com">shimichanga.com</a></em>.</p>
<p>Hopefully, I can back port this into <a href="http://mediaelementjs.com/">MediaElement.js</a> and make the entire library more dynamic and usable. For now, go check out my hideously styled demo on Mozilla&#8217;s site (<a href="https://developer.mozilla.org/en-US/demos/detail/shimichanga">Shimichanga</a>) and give me some likes!</p>
<p><a class="post-button demo" href="https://developer.mozilla.org/en-US/demos/detail/shimichanga">Dev Derby</a> <a class="post-button demo" href="http://shimichanga.com">shimichanga.com</a></p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/dzwYijEoIb8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/native-browser-get-set-properties-in-javascript/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://johndyer.name/native-browser-get-set-properties-in-javascript/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=native-browser-get-set-properties-in-javascript</feedburner:origLink></item>
		<item>
		<title>HTML5 Video Wrapper for YouTube and Vimeo API – MediaElement.js</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/vXn2qP5oi80/</link>
		<comments>http://johndyer.name/html5-video-wrapper-for-youtube-and-vimeo-api-mediaelement-js/#comments</comments>
		<pubDate>Mon, 28 Nov 2011 20:17:40 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Video]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=234</guid>
		<description><![CDATA[YouTube and Vimeo APIs YouTube and Vimeo have nice APIs to allow JavaScript developers to control the playback of embedded content (oh, and YouTube has a new design). They&#8217;ve also updated the APIs to use the newer &#60;iframe&#62; embed style instead requiring &#60;object&#62;&#60;embed&#62; flash tags. But as powerful as those APIs are, they are not [...]]]></description>
				<content:encoded><![CDATA[<h3><a href="http://mediaelementjs.com/examples/?name=youtube"><img class="aligncenter size-full wp-image-238" title="youtube-mediaelementjs" src="http://johndyer.name/wp-content/uploads/2011/11/youtube-mediaelementjs.jpg" alt="" width="750" height="194" /></a></h3>
<h3>YouTube and Vimeo APIs</h3>
<p><a href="http://code.google.com/apis/youtube/iframe_api_reference.html">YouTube</a> and <a href="http://vimeo.com/api/docs/player-js">Vimeo</a> have nice APIs to allow JavaScript developers to control the playback of embedded content (oh, and <a href="http://www.theverge.com/2011/11/21/2577332/how-to-enable-the-new-youtube-design">YouTube has a new design</a>). They&#8217;ve also updated the APIs to use the newer <code>&lt;iframe&gt;</code> embed style instead requiring <code>&lt;object&gt;&lt;embed&gt;</code> flash tags. But as powerful as those APIs are, they are not consistent with each other and neither one conforms to HTML5 <code>&lt;video&gt;</code> <a href="http://www.w3.org/TR/html5/video.html">properties or events</a> so you can&#8217;t leverage your existing skills or code.</p>
<h3>MediaElement.js HTML5 Wrapper</h3>
<p>To make the YouTube and Vimeo APIs easier to use, I wrapped the MediaElement.js shim structure around their proprietary APIs to make them feel like HTML5. So for a YouTube video, you can use this <code>&lt;video&gt; markup</code></p>
<pre class="brush: html; gutter: true">&lt;video id="youtube1" width="640" height="360"&gt;
	&lt;source src="http://www.youtube.com/watch?v=nOEw9iiopwI" type="video/youtube" &gt;
&lt;/video&gt;</pre>
<p>Then to build a full-fledged player that is CSS skinnable on top of YouTube Chromeless , you can simply call the MediaElementPlayer like this:</p>
<pre class="brush: javascript; gutter: true">jQuery(document).ready(function($) {
	$('#youtube1').mediaelementplayer();
});</pre>
<p><a class="post-button demo" href="http://www.mediaelementjs.com/examples/?name=youtube">Demo</a></p>
<p>Or you can skip MediaElementPlayer&#8217;s controls and build your own player using just the MediaElement wrapper which does not require jQuery (MediaElement is the wrapper which shims HTML5 support into old browsers, MediaElement<em>Player</em> is the full-fledged jQuery-based control bar built on top of MediaElement).</p>
<pre class="brush: javascript; gutter: true">new MediaElement('youtube#', {
	success: function(media, domNode) {
		// add HTML5 events to the YouTube API media object
		media.addEventListener('play', function() {
			console.log('yeah, it is playing!');
		}, false);

		media.addEventListener('timeupdate', function() {
// access HTML5-like properties
			console.log('current: ' + media.currentTime);
		}, false);

		// add click events to control player
		myMuteButton.addEventListener('click', function() {
			// HTML5 has a "muted" setter, but we have to use a function here
			media.setMuted(true);
		}, false);
	}
});</pre>
<p>Once the success event fires, the <code>media</code> object is a JavaScript wrapper that mimics the HTML5 Media API, but under the hood is really a wrapper around YouTube&#8217;s API. Nice right?</p>
<h3>Gotchas</h3>
<h3><span class="Apple-style-span" style="font-size: 13px; font-weight: normal;">There are a few things you should look out for if you want to try it:</span></h3>
<ul>
<li>This code is now in the official 2.4.0 release of MediaElement.js, but it should be considered experimental. I&#8217;d like to pull it out of the core and make it a plugin, but this will require some re-architecting.</li>
<li>The Vimeo API wrapper is not finished yet. It will simply display the default Vimeo controls. (Also, unlike YouTube&#8217;s chromeless option, only Vimeo Pro users can totally remove controls)</li>
<li>Some browsers (Chrome, IE) don&#8217;t allow HTML to be placed over an <code>&lt;iframe&gt;</code> with Flash inside which makes MediaElementPlayer&#8217;s controls not work, so I&#8217;m using the pure Flash version of YouTube for desktop browsers</li>
<li>Fullscreen: JavaScript can&#8217;t initiate Flash&#8217;s true fullscreen, and <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=90268">Firefox still has terrible Flash support</a> (if you try to adjust the size of a Flash movie&#8217;s surrounding DOM nodes, Firefox reloads it!)</li>
</ul>
<p><a class="post-button demo" href="http://mediaelementjs.com/examples/?name=youtube">Demo</a> <a class="post-button demo" href="http://mediaelementjs.com/">Download</a></p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/vXn2qP5oi80" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/html5-video-wrapper-for-youtube-and-vimeo-api-mediaelement-js/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		<feedburner:origLink>http://johndyer.name/html5-video-wrapper-for-youtube-and-vimeo-api-mediaelement-js/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=html5-video-wrapper-for-youtube-and-vimeo-api-mediaelement-js</feedburner:origLink></item>
		<item>
		<title>Pinbox.js – Photos Then and Now (jQuery plugin)</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/44U6v7DtZvQ/</link>
		<comments>http://johndyer.name/pinbox-js-photos-then-and-now-jquery-plugin/#comments</comments>
		<pubDate>Wed, 02 Nov 2011 15:02:02 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=215</guid>
		<description><![CDATA[With the help of a colleague, I&#8217;m gathering images of an area of Dallas, TX just east of downtown around the campus of Dallas Theological Seminary where I work. I wanted to make the photos interactive like the animated GIF you see here. Live Demo Download Code Code Explanation To make this work, I start with [...]]]></description>
				<content:encoded><![CDATA[<p>With the help of a colleague, I&#8217;m gathering images of an area of Dallas, TX just east of downtown around the campus of <a href="http://www.dts.edu/">Dallas Theological Seminary</a> where I work. I wanted to make the photos interactive like the animated GIF you see here.</p>
<p><a href="http://johndyer.name/lab/pinbox/"><img class="aligncenter size-full wp-image-224" title="pinboxdemo" src="http://johndyer.name/wp-content/uploads/2011/11/pinboxdemo.gif" alt="" width="680" height="252" /></a></p>
<p><a class="post-button demo" href="http://johndyer.name/lab/pinbox/">Live Demo</a> <a class="post-button demo" href="http://github.com/johndyer/pinbox.js/">Download Code</a></p>
<h2>Code Explanation</h2>
<p><span id="more-215"></span></p>
<p>To make this work, I start with a simple <code>&lt;img /&gt;</code> tag and then add some HTML5 <code>data-*</code> attributes that specify the size and position of the inner image:</p>
<pre class="brush: html; gutter: true">&lt;img src="images/convent-pano.jpg"
	width="940"
	height="365"
	data-small-src="images/convent-pano-inner.jpg"
	data-small-width="507"
	data-small-height="264"
	data-small-left="163"
	data-small-top="11"
	class="pinbox"
	alt="View from Convent Street"
/&gt;</pre>
<p>This ensures the images will still function properly even if the JavaScript doesn&#8217;t fire properly. The next step is to include the Pinbox.js library and then use jQuery to instantiate it when the document has loaded. The script takes care of the rest.</p>
<pre class="brush: html; gutter: true">&lt;script src="src/jquery.js"&gt;&lt;/script&gt;
&lt;script src="src/jquery.pinbox.js"&gt;&lt;/script&gt;
&lt;link rel="stylesheet" href="src/jquery.pinbox.css" /&gt;
&lt;script&gt;
jQuery(document).ready(function($) {
	$('.pinbox').pinbox();
});
&lt;/script&gt;</pre>
<p><a class="post-button demo" href="http://johndyer.name/lab/pinbox/">Live Demo</a> <a class="post-button demo" href="http://github.com/johndyer/pinbox.js/">Download Code</a></p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/44U6v7DtZvQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/pinbox-js-photos-then-and-now-jquery-plugin/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://johndyer.name/pinbox-js-photos-then-and-now-jquery-plugin/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=pinbox-js-photos-then-and-now-jquery-plugin</feedburner:origLink></item>
		<item>
		<title>Native Fullscreen JavaScript API (plus jQuery plugin)</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/IFZ98A-ROoY/</link>
		<comments>http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 13:17:57 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://johndyer.name/?p=181</guid>
		<description><![CDATA[HTML5 &#60;video&#62; is great, but when it was first released, one of the big complaints was that it couldn&#8217;t do true FullScreen like Flash. Thankfully, this is changing and native FullScreen support is coming to most browsers in the next few months (no word from the Internet Explorer team Update on IE below #5)) The API [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://johndyer.name/wp-content/uploads/2011/10/js-fullscren.png"><img class="size-medium wp-image-213 alignright" title="JS Fullscreen" src="http://johndyer.name/wp-content/uploads/2011/10/js-fullscren-300x297.png" alt="" width="300" height="297" /></a></p>
<p>HTML5 <code>&lt;video&gt;</code> is great, but when it was first released, one of the big complaints was that it couldn&#8217;t do true FullScreen like Flash. Thankfully, this is changing and native FullScreen support is coming to most browsers in the next few months (<del>no word from the Internet Explorer team</del> Update on IE below #5))</p>
<p>The API is still heavily in flux especially since the W3C joined in this week. I spent some time working through the differences to implement FullScreen in <a href="http://mediaelementjs.com/">MediaElement.js HTML5 video player</a>, and it&#8217;s working great in Safari 5.1+, <del><a href="http://tools.google.com/dlpage/chromesxs">Chrome Canary</a></del> Chrome 15+, or <a href="http://nightly.mozilla.org/">Firefox Nightly</a> (go to about:config and set full-screen-api.enabled= true) and scheduled for <a href="https://wiki.mozilla.org/Platform/Features/Full_Screen_APIs">Firefox 10</a>. Below I&#8217;m going to try to explain how things evolved, where we are today, and then some code that you can play with.</p>
<p><a class="post-button demo" href="http://johndyer.name/lab/fullscreenapi/">Simple Demo</a> <a class="post-button demo" href="http://mediaelementjs.com/">Video Demo<span id="more-181"></span></a></p>
<h2>A Brief History of the FullScreen API</h2>
<ol>
<li>The first native FullScreen implementation appeared in Safari 5.0 (and iOS) added a a <code>webkit<strong>Enter</strong>FullScreen()</code> function that only worked on &lt;video&gt; tags using Safari&#8217;s video controls (see <a href="http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html">Apple&#8217;s HTML5VideoElement</a>).</li>
<li>For Safari 5.1, Apple changed the API to be more inline with <a href="https://wiki.mozilla.org/Gecko:FullScreenAPI">Mozilla&#8217;s FullScreen API proposal</a> (which actually predates Apple&#8217;s implementation). Now, <code>webkit<strong>Request</strong>FullScreen()</code> can be called on <em>any</em> DOM element which makes that portion of an HTML page go fullscreen.</li>
<li>Firefox and Chrome announced that they will add FullScreen API support, and the feature has already arrived in <del><a href="http://tools.google.com/dlpage/chromesxs">Chrome Canary</a></del> Chrome 15+ and <a href="http://nightly.mozilla.org/">Firefox Nightly</a> (scheduled for <a href="https://wiki.mozilla.org/Platform/Features/Full_Screen_APIs">Firefox 10</a>). The Mozilla team has posted some <a href="http://pearce.org.nz/full-screen/">a demo</a>.</li>
<li>On October 15, 2011, the W3C released a <a href="http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html">Fullscreen API proposal</a> (written by a member of the Opera team) which has two main differences from Mozilla&#8217;s proposal:
<ol>
<li>Mozilla/Webkit uses a Capital &#8216;S&#8217; (FullScreen) while W3C does not (Fullscreen)</li>
<li>Mozilla/Webkit uses <code><strong>cancel</strong>FullScreen</code><br />
while W3C uses <code><strong>exit</strong>Fullscreen</code></li>
</ol>
</li>
<li><strong>Update (11/15/2011)</strong>: Ted Johnson from <a href="blogs.msdn.com/b/ie/">IEBlog</a> says<del> IE10 will <em>not</em> support the FullScreen API</del> (12/05/2011: I misunderstood the first email from Ted) that the IE10 team has not yet decided whether to implement the FullScreen API. He notes however that, &#8220;Windows 8 Metro style Internet Explorer is always full screen &#8230; and as before, F11 enters full screen mode in desktop versions of IE.&#8221;</li>
</ol>
<h2>Understanding the FullScreen API</h2>
<p>Here are the most important parts of the FullScreen API with notes on how things differ among browsers. In general, I&#8217;m using the Mozilla/Webkit spelling in the examples below, but I&#8217;m also noting the W3C differences where needed.</p>
<h3>1. Detecting FullScreen support</h3>
<p>To detect fullscreen support, you&#8217;ll need to use the typeof command to find out if a given browser has support for the FullScreen API methods. There is also boolean property called <code>fullScreenEnabled</code> that tells you if the user has disabled the feature (strangely WebKit does not have the <code>fullScreenEnabled</code> property making it difficult to detect if it&#8217;s turned off).</p>
<pre class="brush: javascript; gutter: true">// Mozilla&#039;s proposed API: in practice, you&#039;ll need vendor prefixes (see examples below)
if (typeof document.cancelFullScreen != &#039;undefined&#039; &amp;&amp; document.fullScreenEnabled === true) {
    /* do fullscreen stuff */
}</pre>
<h3>2. Entering and Exiting FullScreen</h3>
<p>To enter FullScreen mode, you call <code>requestFullScreen</code> (or <code>requestFullscreen</code> for W3C) on the element want to be viewed in FullScreen. To exit you call <code>cancelFullScreen</code> (or <code>exitFullscreen</code> for W3C) on the <code>document</code> object.</p>
<pre class="brush: javascript; gutter: true">// mozilla proposal
element.requestFullScreen();
document.cancelFullScreen(); 

// Webkit (works in Safari and Chrome Canary)
element.webkitRequestFullScreen(); 
document.webkitCancelFullScreen(); 

// Firefox (works in nightly)
element.mozRequestFullScreen();
document.mozCancelFullScreen(); 

// W3C Proposal
element.requestFullscreen();
document.exitFullscreen();</pre>
<p>Mozilla has also proposed an alternate <code>requestFullScreenWithKeys()</code> method which would enable the user to use the keyboard in FullScreen mode. With Flash, Adobe always disabled keyboard support in FullScreen to prevent malicious sites from attempting to steal passwords, but it looks like the browser makers are considering making this an option.</p>
<h3>3. Fullscreen Event and Current Status</h3>
<p>To detect when a FullScreen event happens, there is a <code>fullscreeneventchange</code> that fires on the element going FullScreen and a boolean property (<code>fullScreen</code>) on the document object that reports if it&#8217;s in FullScreen mode or not.</p>
<pre class="brush: javascript; gutter: true">element.addEventListener(&#039;fullscreeneventchange&#039;, function(e) {
    if (document.fullScreen) {
       /* make it look good for fullscreen */
    } else {
       /* return to the normal state in page */
    }
}, true);

// note: unlike Webkit and the W3C proposal, 
// Mozilla fires its mozfullscreenchange event on the *document* object
// instead of the element going fullscreen</pre>
<p>Mozilla also mentions the possibility of adding a <code>fullscreendenied</code> event in the future. You should also know that Webkit added an &#8216;Is&#8217; to their boolean property and that the W3C proposal strangely does not include this property:</p>
<pre class="brush: javascript; gutter: true">// Mozilla proposal
document.fullScreen;
// Firefox (Nightly)
document.mozFullScreen;
// Webkit (Chrome, Safari)
document.webkitIsFullScreen; // note the &#039;Is&#039;
// W3C proposal
// None? Why?</pre>
<h3>4. Styling FullScreen</h3>
<p>Both Mozilla and the W3C have proposed new pseudo CSS classes for styling elements in FullScreen mode.</p>
<pre class="brush: css; gutter: true">/* normal state */
.my-container {
    width: 640px;
    height: 360px;
}

/* Mozilla proposal (dash) */
.my-container:full-screen {
    width:100%;
    height:100%;
}

/* W3C proposal (no dash) */
.my-container:fullscreen {
    width:100%;
    height:100%;
}

/* currently working vendor prefixes */
.my-container:-webkit-full-screen, .my-container:-moz-full-screen {
    width:100%;
    height:100%;
}</pre>
<h3>5. Embedding FullScreen</h3>
<p>When you embed content from another site (like a YouTube video) using Flash&#8217;s <code>&lt;object&gt;&lt;embed&gt;</code> tags, you can specificy whether or not to allow FullScreen to work. This feature has also been added to the <code>&lt;iframe&gt;</code> tag using the allowFullScreen attribute.</p>
<pre class="brush: html; gutter: true">&lt;!-- content from another site that is allowed to use the fullscreen command --&gt;
&lt;iframe src=&quot;http://anothersite.com/video/123&quot; width=&quot;640&quot; height=&quot;360&quot; allowFullScreen&gt;&lt;/iframe&gt;</pre>
<h2>Putting it All Together</h2>
<p>To make this work in its current state, you need a wrapper that can help detect the right features. Here&#8217;s what I&#8217;ve put together to work in Safari 5.1, <del><a href="http://tools.google.com/dlpage/chromesxs">Chrome Canary</a></del> Chrome 15+, and <a href="http://nightly.mozilla.org/">Firefox Nightly</a>. I&#8217;ll update it if the W3C notation goes through:</p>
<pre class="brush: javascript; gutter: true">(function() {
	var
		fullScreenApi = {
			supportsFullScreen: false,
			isFullScreen: function() { return false; },
			requestFullScreen: function() {},
			cancelFullScreen: function() {},
			fullScreenEventName: &#039;&#039;,
			prefix: &#039;&#039;
		},
		browserPrefixes = &#039;webkit moz o ms khtml&#039;.split(&#039; &#039;);

	// check for native support
	if (typeof document.cancelFullScreen != &#039;undefined&#039;) {
		fullScreenApi.supportsFullScreen = true;
	} else {
		// check for fullscreen support by vendor prefix
		for (var i = 0, il = browserPrefixes.length; i &lt; il; i++ ) {
			fullScreenApi.prefix = browserPrefixes[i];

			if (typeof document[fullScreenApi.prefix + &#039;CancelFullScreen&#039; ] != &#039;undefined&#039; ) {
				fullScreenApi.supportsFullScreen = true;

				break;
			}
		}
	}

	// update methods to do something useful
	if (fullScreenApi.supportsFullScreen) {
		fullScreenApi.fullScreenEventName = fullScreenApi.prefix + &#039;fullscreenchange&#039;;

		fullScreenApi.isFullScreen = function() {
			switch (this.prefix) {
				case &#039;&#039;:
					return document.fullScreen;
				case &#039;webkit&#039;:
					return document.webkitIsFullScreen;
				default:
					return document[this.prefix + &#039;FullScreen&#039;];
			}
		}
		fullScreenApi.requestFullScreen = function(el) {
			return (this.prefix === &#039;&#039;) ? el.requestFullScreen() : el[this.prefix + &#039;RequestFullScreen&#039;]();
		}
		fullScreenApi.cancelFullScreen = function(el) {
			return (this.prefix === &#039;&#039;) ? document.cancelFullScreen() : document[this.prefix + &#039;CancelFullScreen&#039;]();
		}
	}

	// jQuery plugin
	if (typeof jQuery != &#039;undefined&#039;) {
		jQuery.fn.requestFullScreen = function() {

			return this.each(function() {
				if (fullScreenApi.supportsFullScreen) {
					fullScreenApi.requestFullScreen(this);
				}
			});
		};
	}

	// export api
	window.fullScreenApi = fullScreenApi;
})();</pre>
<p>This creates an object called <code>fullScreenApi</code> with a boolean property <code>supportsFullScreen</code> and some methods that allow you to do something more universal. Here&#8217;s an example usage:</p>
<pre class="brush: javascript; gutter: true">if (fullScreenApi.supportsFullScreen) {
	myButton.addEventListener(&#039;click&#039;, function() {
		fullScreenApi.requestFullScreen(someElement);
	}, true);
}</pre>
<p>You can see it in action below:</p>
<p><a class="post-button demo" href="http://johndyer.name/lab/fullscreenapi/">Simple Demo</a> <a class="post-button demo" href="http://mediaelementjs.com/">Video Demo</a></p>
<h2>Issues and Updates</h2>
<p>Since this post, there are some additional things worth mentioning</p>
<ul>
<li><strong>Security concerns</strong> &#8211; Browser vendors are well aware of the potential security issues with fullscreen. For example, a malicious site could show a full screen Windows or Mac login window and steal a password. That&#8217;s why they are disabling keyboard support by default and only enabling by explicitly asking.</li>
<li><strong>Internet Explorer support</strong> &#8211; <del>I have an email from a IE team member saying they are discussing it, but have not made any decisions. </del>As of now, <del>IE10 will <em>not</em> implement the FullScreen API</del>, the IE team has not yet decided if they will implement the FullScreen API.</li>
<li><strong>FullscreenEnabled vs. IsFullScreen</strong> &#8211; The W3C includes the very helpful <code>fullscreenEnabled</code> flag to let your code know if you can use the API, but strangely the W3C does not include an <code>isFullscreen</code> flag. WebKit on the other hand has <code>webkitIsFullScreen</code>, but does not have a <code>webkitFullScreenEnabled</code> equivalent property. Mozilla helpfully includes both.</li>
<li><strong>FullScreenChanged event</strong> &#8211; The W3C and Webkit fire the <code>fullscreenchanged</code> event on the element going fullscreen, but Mozilla fires the event on the <code>document</code> object.</li>
</ul>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/IFZ98A-ROoY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/feed/</wfw:commentRss>
		<slash:comments>73</slash:comments>
		<feedburner:origLink>http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=native-fullscreen-javascript-api-plus-jquery-plugin</feedburner:origLink></item>
		<item>
		<title>Getting Counts for Twitter Links, Facebook Likes/Shares, and Google (+1) PlusOnes in C# or PHP</title>
		<link>http://feedproxy.google.com/~r/johndyer/~3/bxgow7oiviQ/</link>
		<comments>http://johndyer.name/getting-counts-for-twitter-links-facebook-likesshares-and-google-1-plusones-in-c-or-php/#comments</comments>
		<pubDate>Tue, 07 Jun 2011 21:58:00 +0000</pubDate>
		<dc:creator>johndyer@gmail.com</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://e9c7c3e6-a1a4-4a1d-990a-c9cfbab89779</guid>
		<description><![CDATA[I am working on a project where I need to know the number of social shares on Facebook, Twitter, and Google +1 (plusone). Facebook and Twitter make this easy with a simple URL that returns clear JSON data, but Google has not offered an official way to do it yet. However, I found someone who [...]]]></description>
				<content:encoded><![CDATA[<p>I am working on a project where I need to know the number of social shares on Facebook, Twitter, and Google +1 (plusone). Facebook and Twitter make this easy with a simple URL that returns clear JSON data, but Google has not offered an official way to do it yet. However, I <a href="http://www.tomanthony.co.uk/blog/google_plus_one_button_seo_count_api/comment-page-1/">found someone</a> who tracked down how to do it using Google&#8217;s JSON-RPC API, and I&#8217;ve repackaged them together in ASP.NET and PHP for anyone who wants to give it a try.<span id="more-9"></span></p>
<h2>Data URLs</h2>
<p>Here&#8217;s where you can find the data</p>
<table>
<tbody>
<tr>
<th>Facebook</th>
<td>http://graph.facebook.com/?ids=YOURURL</td>
</tr>
<tr>
<th>Twitter</th>
<td>http://urls.api.twitter.com/1/urls/count.json?url=YOURURL</td>
</tr>
<tr>
<th>Google</th>
<td>https://clients6.google.com/rpc [see below for JSON-RPC]</td>
</tr>
</tbody>
</table>
<h2>ASP.NET C#</h2>
<p>Note: Since I&#8217;m using &#8220;dynamic,&#8221; this requires .NET 4.0. Also, I&#8217;m using the JavaScriptSerializer class which is officially depreciated, but will probably not actually be removed. You could also easily use Regex to get these simple values.</p>
<pre class="brush: c-sharp;">int GetTweets(string url) {

	string jsonString = new System.Net.WebClient().DownloadString("http://urls.api.twitter.com/1/urls/count.json?url=" + url);

	var json = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(jsonString);
	int count = (int)json["count"];	

	return count;
}

int GetLikes(string url) {

	string jsonString = new System.Net.WebClient().DownloadString("http://graph.facebook.com/?ids=" + url);

	var json = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(jsonString);
	int count = json[url]["shares"];

	return count;
}

int GetPlusOnes(string url) {

	string googleApiUrl = "https://clients6.google.com/rpc"; //?key=AIzaSyCKSbrvQasunBoV16zDH9R33D88CeLr9gQ";

	string postData = @"[{""method"":""pos.plusones.get"",""id"":""p"",""params"":{""nolog"":true,""id"":""" + url + @""",""source"":""widget"",""userId"":""@viewer"",""groupId"":""@self""},""jsonrpc"":""2.0"",""key"":""p"",""apiVersion"":""v1""}]";

	System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(googleApiUrl);
	request.Method = "POST";
	request.ContentType = "application/json-rpc";
	request.ContentLength = postData.Length;

	System.IO.Stream writeStream = request.GetRequestStream();
	UTF8Encoding encoding = new UTF8Encoding();
	byte[] bytes = encoding.GetBytes(postData);
	writeStream.Write(bytes, 0, bytes.Length);
	writeStream.Close();

	System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
	System.IO.Stream responseStream = response.GetResponseStream();
	System.IO.StreamReader readStream = new System.IO.StreamReader(responseStream, Encoding.UTF8);
	string jsonString = readStream.ReadToEnd();

	readStream.Close();
	responseStream.Close();
	response.Close();

	var json = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(jsonString);
	int count = Int32.Parse(json[0]["result"]["metadata"]["globalCounts"]["count"].ToString().Replace(".0", ""));

	return count;
}</pre>
<h2>PHP</h2>
<p>Again, thanks to <a href="http://www.tomanthony.co.uk/blog/google_plus_one_button_seo_count_api/comment-page-1/">Tom Anthony</a> for the Google part</p>
<pre class="brush: php;">function get_tweets($url) {

	$json_string = file_get_contents('http://urls.api.twitter.com/1/urls/count.json?url=' . $url);
	$json = json_decode($json_string, true);

	return intval( $json['count'] );
}

function get_likes($url) {

	$json_string = file_get_contents('http://graph.facebook.com/?ids=' . $url);
	$json = json_decode($json_string, true);

	return intval( $json[$url]['shares'] );
}

function get_plusones($url) {

	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, "https://clients6.google.com/rpc");
	curl_setopt($curl, CURLOPT_POST, 1);
	curl_setopt($curl, CURLOPT_POSTFIELDS, '[{"method":"pos.plusones.get","id":"p","params":{"nolog":true,"id":"' . $url . '","source":"widget","userId":"@viewer","groupId":"@self"},"jsonrpc":"2.0","key":"p","apiVersion":"v1"}]');
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
	$curl_results = curl_exec ($curl);
	curl_close ($curl);

	$json = json_decode($curl_results, true);

	return intval( $json[0]['result']['metadata']['globalCounts']['count'] );
}</pre>
<p>Hope that helps someone out there!</p>
<img src="http://feeds.feedburner.com/~r/johndyer/~4/bxgow7oiviQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://johndyer.name/getting-counts-for-twitter-links-facebook-likesshares-and-google-1-plusones-in-c-or-php/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		<feedburner:origLink>http://johndyer.name/getting-counts-for-twitter-links-facebook-likesshares-and-google-1-plusones-in-c-or-php/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=getting-counts-for-twitter-links-facebook-likesshares-and-google-1-plusones-in-c-or-php</feedburner:origLink></item>
	<media:rating>nonadult</media:rating><media:description type="plain">Technology and web development in ASP.NET, Flash, and JavaScript</media:description></channel>
</rss>
