<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><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:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>remy sharp's b:log</title>
	
	<link>http://remysharp.com</link>
	<description>About [code] and all that jazz</description>
	<pubDate>Wed, 03 Sep 2008 09:33:01 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<geo:lat>50.844973</geo:lat><geo:long>-0.143970</geo:long><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/remysharp" type="application/rss+xml" /><item>
		<title>Safari show bug</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/382174543/</link>
		<comments>http://remysharp.com/2008/09/03/safari-show-bug/#comments</comments>
		<pubDate>Wed, 03 Sep 2008 09:33:01 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[bug]]></category>

		<category><![CDATA[safari]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/09/03/safari-show-bug/</guid>
		<description><![CDATA[This is probably the first time I've come across a bug in Safari that's comparable in bizarreness to IE bugs.

The symptoms of this bug are that when you set the CSS display property to block from none the element doesn't appear.  In fact, it has a height of zero.



The bug only happens in a [...]]]></description>
			<content:encoded><![CDATA[<p>This is probably the first time I've come across a bug in Safari that's comparable in bizarreness to IE bugs.</p>

<p>The symptoms of this bug are that when you set the CSS <code>display</code> property to <code>block</code> from <code>none</code> the element <em>doesn't</em> appear.  In fact, it has a height of zero.</p>

<p><span id="more-240"></span></p>

<p>The bug only happens in a specific markup situation, as such I've never seen it before now - but it's worth being aware of since debugging the issue would normally start in the JavaScript, when in fact it's Webkit that's the problem.</p>

<p>The bug occurs when your hidden content is an inline element, containing <code>&lt;br /&gt;</code> tags and proceeded by another inline element.  It's tricky to explain, but can be seen in the source code of the example below:</p>

<p><a href="http://remysharp.com/demo/safari-hide-bug.html">View the demonstration of the bug</a></p>

<p>The fix, after a lot of debugging, turned out to be simply to follow the inline element with a block element.  Somehow the height in the first example is being set to zero allowing the inline <code>span</code> to sit above the element.  In the second example, the block element clears the newly displayed <code>span</code> and it appears correctly.</p>

<p>It's pretty convoluted, which is why it reminds me a of an IE bug!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=fAGykl"><img src="http://feeds.feedburner.com/~f/remysharp?i=fAGykl" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/382174543" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/09/03/safari-show-bug/feed/</wfw:commentRss>
		<feedburner:origLink>http://remysharp.com/2008/09/03/safari-show-bug/</feedburner:origLink></item>
		<item>
		<title>How to default a variable in XSLT</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/365974036/</link>
		<comments>http://remysharp.com/2008/08/15/how-to-default-a-variable-in-xslt/#comments</comments>
		<pubDate>Fri, 15 Aug 2008 20:58:18 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[Dump]]></category>

		<category><![CDATA[xml]]></category>

		<category><![CDATA[xslt]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/08/15/how-to-default-a-variable-in-xslt/</guid>
		<description><![CDATA[Since I couldn't find this anywhere on the web, and I'm working on a project that has had me very quickly learn XSLT, here's how to default a value in XSLT - useful if you're looking to grab a variable via the query string, and it may not be there in the first place.



&#60;xsl:variable name=&#34;show_comments&#34;&#62;
 [...]]]></description>
			<content:encoded><![CDATA[<p>Since I couldn't find this anywhere on the web, and I'm working on a project that has had me <em>very</em> quickly learn XSLT, here's how to default a value in XSLT - useful if you're looking to grab a variable via the query string, and it may not be there in the first place.</p>

<p><span id="more-239"></span></p>

<pre><code>&lt;xsl:variable name=&quot;show_comments&quot;&gt;
  &lt;xsl:choose&gt;
    &lt;xsl:when test=&quot;//QUERY_STRING/show_comments&quot;&gt;&lt;xsl:value-of select=&quot;//QUERY_STRING/show_comments&quot;/&gt;&lt;/xsl:when&gt;
    &lt;xsl:otherwise&gt;0&lt;/xsl:otherwise&gt; &lt;!-- default value --&gt;
  &lt;/xsl:choose&gt;
&lt;/xsl:variable&gt;</code></pre>

<p><small>Note: <code>//QUERY_STRING</code> is a made up variable</small></p>

<p>I know XSLT might be a bit random for me, but client wants: client gets <img src='http://remysharp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=oAVSpk"><img src="http://feeds.feedburner.com/~f/remysharp?i=oAVSpk" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/365974036" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/08/15/how-to-default-a-variable-in-xslt/feed/</wfw:commentRss>
		<feedburner:origLink>http://remysharp.com/2008/08/15/how-to-default-a-variable-in-xslt/</feedburner:origLink></item>
		<item>
		<title>Grid Tools</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/345765708/</link>
		<comments>http://remysharp.com/2008/07/25/grid-tools/#comments</comments>
		<pubDate>Fri, 25 Jul 2008 15:40:10 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[bookmarklet]]></category>

		<category><![CDATA[css]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[grid]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/07/25/grid-tools/</guid>
		<description><![CDATA[I've recently been doing more and more cutting from mocks to HTML &#38; CSS and one particular job required me to work to a specific grid layout.

In working to these grid layouts, I've created a couple of tools to make generating and working with the grids a little easier.



Generating the Grid

There's a few tools for [...]]]></description>
			<content:encoded><![CDATA[<p>I've recently been doing more and more cutting from mocks to HTML &amp; CSS and one particular job required me to work to a specific grid layout.</p>

<p>In working to these grid layouts, I've created a couple of tools to make generating and working with the grids a little easier.</p>

<p><span id="more-238"></span></p>

<h2>Generating the Grid</h2>

<p>There's a few tools for generating PSDs and calculations for grid, but since I'm a real lazy guy - I actually want something to generate the CSS for me.</p>

<p>I use <a href="http://www.29digital.net/grid/" title="Grid Calculator">29digital's grid calculator</a> to work out the details of the grid.  Then once the right grid is configured, I'll use the following bookmarklet to generate the CSS required:</p>

<p><a style="font-size: 150%;" href="javascript:(function(){function%20g(f){return%20parseInt(document.getElementById(f).value)}var%20a=g('columnsvalue'),b=g('columnwidthvalue'),c=g('gutterwidthvalue'),d='',i;d=%22.column%20{%20margin:%200%20%22+c+%22px%20%22+c+%22px%200;%20float:%20left;%20}\n.last%20{%20margin:%200%200%20%22+c+%22px%200;%20}\n%22;for(i=0;i<a;i++){d+=%22.span%22+(i+1)+%22%20{%20width:%20%22+((b*(i+1))+(c*i))+%22px;%20}\n%22}alert(d)})();">Grid CSS</a></p>

<p>This gives me a CSS grid layout such as the following for a 6 column 66px wide grid layout with 14px gutter:</p>

<pre><code>.column { margin: 0 14px 14px 0; float: left; }
.last { margin: 0 0 14px 0; }
.span1 { width: 66px; }
.span2 { width: 146px; }
.span3 { width: 226px; }
.span4 { width: 306px; }
.span5 { width: 386px; }
.span6 { width: 466px; }</code></pre>

<h2>Working with the Grid</h2>

<p>The next step I'll take when working up the markup, is to include the code from <a href="http://gridlayouts.com/" title="Grid Layout">ctrl+shift+g</a>.</p>

<p>The default way of interacting with this code is to use ctrl+shift+g - but this triggers the find function both in Safari and Firefox 3 - and in Firefox it triggers the grid and find at once, then you're stuck in the find box not being able turn the grid back off easily.</p>

<p>So, another little bookmarklet to toggle the grid:</p>

<p><a style="font-size: 150%;" href="javascript:(function(){$('#GridLayout').toggle();})();">Toggle grid</a></p>

<p>All simple stuff, but makes getting a simple grid layout working quicker and easier for the lazy developer <img src='http://remysharp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=KXtoXj"><img src="http://feeds.feedburner.com/~f/remysharp?i=KXtoXj" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/345765708" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/07/25/grid-tools/feed/</wfw:commentRss>
		<feedburner:origLink>http://remysharp.com/2008/07/25/grid-tools/</feedburner:origLink></item>
		<item>
		<title>Download stats for S3</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/337117113/</link>
		<comments>http://remysharp.com/2008/07/16/download-stats-for-s3/#comments</comments>
		<pubDate>Wed, 16 Jul 2008 14:22:31 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Web]]></category>

		<category><![CDATA[google]]></category>

		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/07/16/download-stats-for-s3/</guid>
		<description><![CDATA[As jQuery for Designers continued to grow in interest, so did the video downloads.

The main problem my server faced was that when a large influx of traffic came in all at once, say if Smashing Magazine publishes a post covering a couple of tutorials, all the apache processes would be busy serving up 30Mb video [...]]]></description>
			<content:encoded><![CDATA[<p>As <a href="http://jqueryfordesigners.com/" title="jQuery for Designers - Tutorials and screencasts">jQuery for Designers</a> continued to grow in interest, so did the video downloads.</p>

<p>The main problem my server faced was that when a large influx of traffic came in all at once, say if <a href="http://www.smashingmagazine.com/" title="Smashing Magazine">Smashing Magazine</a> <a href="http://www.smashingmagazine.com/2008/04/15/60-more-ajax-and-javascript-solutions-for-professional-coding/">publishes a post</a> covering a couple of tutorials, all the apache processes would be busy serving up 30Mb video files - traffic starts to build, the server starts to choke and it all goes downhill.</p>

<p><a href="http://aws.amazon.com/s3" title="Amazon Web Services @ Amazon.com">S3</a> to the rescue, but I also wanted to make sure I could keep track of the download stats.</p>

<p><span id="more-236"></span></p>

<h2>Step 1 - Upload to S3</h2>

<p>For the Mac, I've traditionally used <a href="http://cyberduck.ch/" title="Cyberduck | FTP, SFTP, WebDAV &amp; Amazon S3 Browser for Mac OS X.">Cyberduck</a> for transferring files.  In the last 6 months or so, they added support to upload directly to S3 'buckets'.  I've tried the Firefox plugin (it was a bit clunky) and before finding Cyberduck support S3, I had used JungleDisk.</p>

<p>The advantage of Cyberduck is that a) it's free, and b) it's super easy to use - it treats it just like a traditional connection.</p>

<p>Once the files are uploaded, you need to make sure the files have read permission set on everything you want your users to see.</p>

<h2>Step 2 - Tidy URLs</h2>

<p>So first off - this URL sucks:</p>

<p>http://8b8a7422192806b94844336167669d2-default.s3.amazonaws.com/coda-slider.mov</p>

<p>It's too long, and ideally it matches the domain your site is running from.</p>

<p>Digital Magazine have a superb article on <a href="http://www.digital-web.com/articles/scalable_hosting_s3/">how to configure S3</a> to give you really clean URLs.</p>

<p>One tip I can offer when you're setting this up, you need to ensure your 'bucket' name matches the domain you redirect to.</p>

<p>In my case, it was going to be media.jqueryfordesigners.com, so the bucket name (actually just a new directory if you're using Cyberduck) had to match exactly.</p>

<p>I could now access all the files I had uploaded to the bucket, and marked as read through this clean URL:</p>

<p>http://media.jqueryfordesigners.com/coda-slider.mov</p>

<p>However, there's just one more simple step to keeping track of all the downloads.</p>

<h2>Step 3 - Track</h2>

<p>To completely track the downloads, and make use of nice clean URLs, we need two things:</p>

<ol>
<li>.htaccess</li>
<li>A logging script (taken almost entirely from <a href="http://www.vdgraaf.info/google-analytics-without-javascript.html">Linklove's analytics without JavaScript</a>)</li>
</ol>

<p>As mentioned, in my case the target URL is: </p>

<p>http://media.jqueryfordesigners.com/video.fmt</p>

<p>This is achieved by creating a logging script called <code>media.php</code> which requires the query string <code>url=video.fmt</code>.  The actual URL the user will see is:</p>

<p>http://jqueryfordesigners.com/media/video.fmt</p>

<p>This way I can process the call via the <code>media.php</code> script, then redirect the user off the real location.</p>

<h3>.htaccess</h3>

<p>mod_rewrite is used to keep the URL clean and redirect the request to the right script:</p>

<pre><code>&lt;IfModule mod_rewrite.c&gt;
Options +FollowSymLinks +ExecCGI
RewriteEngine On
RewriteBase /

RewriteRule ^media/(.*)$ /media.php?url=$1 [L]

# other rules here...
&lt;/IfModule&gt;</code></pre>

<h3>Logging Script</h3>

<pre><code>&lt;?php
$var_utmac='UA-1234567-8'; // your urchin code
$var_utmhn='jqueryfordesigners.com'; // your domain
$var_utmp='media/'.$_GET['url']; // this is the file name that will be logged in Google Analytics

// Shouldn't need to change these...
$var_utmn=rand(1000000000,9999999999); //random request number
$var_cookie=rand(10000000,99999999); //random cookie number
$var_random=rand(1000000000,2147483647); //number under 2147483647
$var_today=time(); //today
$var_referer=@$_SERVER['HTTP_REFERER']; //referer url
$var_uservar='-'; //enter your own user defined variable

// the Analytics URL - make sure this is still one line
$urchinUrl='http://www.google-analytics.com/__utm.gif?utmwv=1&#038;utmn='
  .$var_utmn.'&#038;utmsr=-&#038;utmsc=-&#038;utmul=-&#038;utmje=0&#038;utmfl=-&#038;utmdt=-&#038;utmhn='
  .$var_utmhn.'&#038;utmr='.$var_referer.'&#038;utmp='
  .$var_utmp.'&#038;utmac='
  .$var_utmac.'&#038;utmcc=__utma%3D'
  .$var_cookie.'.'.$var_random.'.'.$var_today.'.'.$var_today.'.'
  .$var_today.'.2%3B%2B__utmb%3D'
  .$var_cookie.'%3B%2B__utmc%3D'
  .$var_cookie.'%3B%2B__utmz%3D'
  .$var_cookie.'.'.$var_today
  .'.2.2.utmccn%3D(direct)%7Cutmcsr%3D(direct)%7Cutmcmd%3D(none)%3B%2B__utmv%3D'
  .$var_cookie.'.'.$var_uservar.'%3B';

// the request to Google is handled sent
$handle = fopen ($urchinUrl, "r");
$test = fgets($handle);
fclose($handle);

// finally we redirect the user to the real location of the file
header('Location: http://media.jqueryfordesigners.com/' . $_GET['url']);
?&gt;</code></pre>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=s4RBfj"><img src="http://feeds.feedburner.com/~f/remysharp?i=s4RBfj" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/337117113" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/07/16/download-stats-for-s3/feed/</wfw:commentRss>
<enclosure url="http://media.jqueryfordesigners.com/coda-slider.mov" length="95025859" type="video/quicktime" />
		<feedburner:origLink>http://remysharp.com/2008/07/16/download-stats-for-s3/</feedburner:origLink></item>
		<item>
		<title>Updated jQuery Talk</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/330652381/</link>
		<comments>http://remysharp.com/2008/07/09/updated-jquery-talk/#comments</comments>
		<pubDate>Wed, 09 Jul 2008 10:13:40 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[jQuery]]></category>

		<category><![CDATA[presentation]]></category>

		<category><![CDATA[skillswap]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/07/09/updated-jquery-talk/</guid>
		<description><![CDATA[I was asked to share my knowledge at SkillSwap which was great evening (it's certainly encouraged me out of my little box and head to future events) (thanks also to  John).

The slides were updated for SkillSwap, and then also for the ThoughtWorks talk I gave, and in addition, I've recorded screencasts for each of [...]]]></description>
			<content:encoded><![CDATA[<p>I was asked to share my knowledge at <a href="http://skillswap-brighton.org/" title="SkillSwap Brighton">SkillSwap</a> which was great evening (it's certainly encouraged me out of <a href="http://www.flickr.com/photos/remysharp/2324926736/in/set-72157600666145464/">my little box</a> and head to <a href="http://upcoming.yahoo.com/event/535879/">future events</a>) (<a href="http://remysharp.com/2008/03/13/presenting-jquery-at-qcon/#comment-54923">thanks also to  John</a>).</p>

<p>The slides were updated for SkillSwap, and then also for the ThoughtWorks talk I gave, and in addition, I've recorded screencasts for each of the demos.</p>

<p><span id="more-235"></span></p>

<h2>Slides</h2>

<p><a href="http://remysharp.com/downloads/dom-toolkit-jquery.pdf">PDF download</a></p>

<div style="width:425px;text-align:left" id="__ss_310357">
  <object style="margin:0px" width="425" height="355">
    <param name="movie" value="http://static.slideshare.net/swf/ssplayer2.swf?doc=dom-scripting-toolkit-jquery-1205765318674445-2"/>
    <param name="allowFullScreen" value="true" />
    <param name="allowScriptAccess" value="always" />
    <embed src="http://static.slideshare.net/swf/ssplayer2.swf?doc=dom-scripting-toolkit-jquery-1205765318674445-2" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed>
  </object>
</div>

<h2>Screencasts</h2>

<p>The screencast runs all as one, and runs for 15 minutes.  They're branded as <a href="http://jqueryfordesigners.com/" title="jQuery for Designers - Tutorials and screencasts">jQuery for Designers</a>, but it was so they still had the Creative Commons license on them.</p>

<p><a href="http://jqueryfordesigners.com/media/jquery-examples.mov">QuickTime screencast of jQuery examples</a> (<a href="http://jqueryfordesigners.com/video.php?f=jquery-examples.flv">alternative flash version</a>)</p>

<h2>Live Examples</h2>

<p>I've also included all the code for the examples:</p>

<ul>
<li><a href="http://remysharp.com/demo/dom-toolkit/tabs.html">Tabs</a></li>
<li><a href="http://remysharp.com/demo/dom-toolkit/namespace-events.html">Namespace events</a></li>
<li><a href="http://remysharp.com/demo/dom-toolkit/load.html">Simple Ajax</a></li>
<li><a href="http://remysharp.com/demo/dom-toolkit/effects.html">Effects</a></li>
</ul>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=u0UFsj"><img src="http://feeds.feedburner.com/~f/remysharp?i=u0UFsj" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/330652381" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/07/09/updated-jquery-talk/feed/</wfw:commentRss>
<enclosure url="http://jqueryfordesigners.com/media/jquery-examples.mov" length="31329615" type="video/quicktime" />
		<feedburner:origLink>http://remysharp.com/2008/07/09/updated-jquery-talk/</feedburner:origLink></item>
		<item>
		<title>How to detect if a font is installed (only using JavaScript)</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/329979705/</link>
		<comments>http://remysharp.com/2008/07/08/how-to-detect-if-a-font-is-installed-only-using-javascript/#comments</comments>
		<pubDate>Tue, 08 Jul 2008 17:06:57 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[css]]></category>

		<category><![CDATA[fonts]]></category>

		<category><![CDATA[javascript]]></category>

		<category><![CDATA[project]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/07/08/how-to-detect-if-a-font-is-installed-only-using-javascript/</guid>
		<description><![CDATA[In the pursuit of an idea I had recently, one tiny feature of the web site would be to detect whether the user had X font installed.

I've looked at flash solutions, since a SWF had access to enumerate the fonts, but ultimately if it could be done without flash, it would/should be faster and smarter.

So [...]]]></description>
			<content:encoded><![CDATA[<p>In the pursuit of an idea I had recently, one tiny feature of the web site would be to detect whether the user had X font installed.</p>

<p>I've looked at flash solutions, since a SWF had access to enumerate the fonts, but ultimately if it could be done without flash, it would/should be faster and smarter.</p>

<p>So here's my pure JavaScript (okay, and a little CSS) solution: <a href="http://remysharp.com/downloads/font.js">font.js</a></p>

<p><span id="more-234"></span></p>

<h2>Usage</h2>

<p>Include the <a href="http://remysharp.com/downloads/font.js">font.js</a> script in your page.</p>

<p>It relies on <a href="http://jquery.com">jQuery</a> so include that somewhere too.</p>

<pre><code>$(document).ready(function () {
  font.setup(); // run setup when the DOM is ready
});</code></pre>

<p>Then to test:</p>

<pre><code>font.isInstalled(fontname); // returns true or false</code></pre>

<p>Simple.</p>

<h2>Example</h2>

<p>I mention this little idea, and a fuller blog post on this later, but I've installed it and make use of it to preview a font-family list: </p>

<p><a href="http://font-family.com/sandbox/optima,monaco,courier,random">http://font-family.com/sandbox/optima,monaco,courier,random</a></p>

<h2>How It Works</h2>

<h3>Comic Sans to the Rescue</h3>

<p>For the first time in my career I've found a genuine use for Comic Sans. Due to it's huge Johnny Nomates status in typography circles, it's never been used as inspiration for any other fonts (I <em>may</em> generalising).</p>

<p>What makes this important is that: Comic Sans, in all it's glory, is actually unique.</p>

<p>From there it's a simple case of comparing the font in question against Comic Sans and if they match, it's not installed.</p>

<h3>Basic Matching</h3>

<p>Since we've no way to examine the font, nor look, programmatically, at the characters, we're limited to just checking the width of the rendered text.</p>

<p>As such, this can cause some false negatives if the font has characters exactly the same width as Comic Sans.  However, this is less likely due, again, to it's uniqueness.</p>

<h3>Tricks</h3>

<p>The key to a successful match are two tricks:</p>

<ol>
<li>Large fonts.  Having the font size cranked right up, increases the margin for errors, i.e. if there's a subtle difference that would be hidden between pixels, increasing the font exaggerates this making it easier to spot.</li>
<li>Use wide letters.  I'm using 'w' and 'm' against each other.  It's almost arbitrary, but again, it increases the margin for error, and hence asserting the font is installed.</li>
</ol>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=qpoCvj"><img src="http://feeds.feedburner.com/~f/remysharp?i=qpoCvj" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/329979705" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/07/08/how-to-detect-if-a-font-is-installed-only-using-javascript/feed/</wfw:commentRss>
		<feedburner:origLink>http://remysharp.com/2008/07/08/how-to-detect-if-a-font-is-installed-only-using-javascript/</feedburner:origLink></item>
		<item>
		<title>jQuery API Update: offline and anywhere</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/326759191/</link>
		<comments>http://remysharp.com/2008/07/04/jquery-api-update-offline-and-anywhere/#comments</comments>
		<pubDate>Fri, 04 Jul 2008 16:32:19 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[api]]></category>

		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/07/04/jquery-api-update-offline-and-anywhere/</guid>
		<description><![CDATA[I've been beavering away at the API behind the scenes for a little while, and if you follow me on Twitter you've probably already seen the API browser has already been upgraded.

The key change I've made is to de-couple the API search engine from the front end.  What this has resulted in, is a [...]]]></description>
			<content:encoded><![CDATA[<p>I've been beavering away at the API behind the scenes for a little while, and if you follow me on <a href="http://twitter.com/rem">Twitter</a> you've probably already seen the <a href="http://remysharp.com/jquery-api/">API browser</a> has already been upgraded.</p>

<p>The key change I've made is to de-couple the API search engine from the front end.  What this has resulted in, is a fairly simple API to create any number of bespoke front ends to the jQuery API browser.</p>

<p><span id="more-233"></span></p>

<p>Since the engine now runs separately, it's been easy to create different front ends.</p>

<p>So now the following front ends are available with the latest API:</p>

<ul>
<li><a href="http://remysharp.com/jquery-api/">Live API browser</a></li>
<li><a href="http://remysharp.com/visual-jquery/">Visual jQuery</a> (<a href="http://www.yehudakatz.com/">Yehuda Katz</a> will be releasing this to the original URL soon)</li>
<li><a href="http://remysharp.com/downloads/jquery-api-browser.air.zip">Offline AIR app</a></li>
</ul>

<p>There's no reason this can't be utilised within a Dashboard app (or update the existing one), implemented as an iGoogle widget and so on.</p>

<h2>Feedback for AIR app</h2>

<p>Currently there's no icon for the app - any designers out there want to contribute? Please get in touch.</p>

<p>Originally the app had a quick silver "smoke" look to it, but I've since changed it a straight window so it can be minimised, maximised, etc.</p>

<p>I'm also aware the window settings don't save and the auto updater might ask each time you open the app - there will be updates to fix this very soon!</p>

<h2>Build Your Own</h2>

<h3>Grab an SVN Copy</h3>

<p>I've got all the code in Google's SVN repo (though I'm also mirroring across in git - but it's pretty new to me).</p>

<p>If you're app is online, I strongly recommend pulling the code, or at the least the API database, directly out of the SVN repo to ensure you're running from the latest API.</p>

<ul>
<li><a href="http://jquery-api-browser.googlecode.com/svn/trunk/api-loader.js">api-loader.js</a></li>
<li><a href="http://jquery-api-browser.googlecode.com/svn/trunk/api-docs.js">api-docs.js</a> (this is 600k all on one line)</li>
<li><a href="http://jquery-api-browser.googlecode.com/svn/trunk/api-docs.xml">api-docs.xml</a> (required if you construct the database in the browser/app)</li>
</ul>

<p>If you're working offline, I encourage you to build in an auto update feature, if only to refresh the database.</p>

<h3>API Loader</h3>

<p>Before loading the database, you must import the <code>api-loader.js</code> library which holds the callback to mount the functions on the API database (since it's all contained within one object)</p>

<pre><code>&lt;script src=&quot;api-docs.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</code></pre>

<h3>API Database</h3>

<p>The API database is loaded in one of two ways:</p>

<ul>
<li>Import the pre-packaged JSON database</li>
<li>Converted from XML</li>
</ul>

<p>The pro of the JSON version is that the browser doesn't have to do any processing before the data is available.  The con is that it's almost twice the size of the raw XML version.  I'm looking at what I can do to optimising the JSON version.</p>

<h3>Loading the Database</h3>

<h4>JSON</h4>

<pre><code>&lt;script src=&quot;api-docs.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;</code></pre>

<h4>XML</h4>

<p>Call the <code>loadDocs</code> function when the DOM is ready.</p>

<p>There is an option to point the XML database to a different location via the <code>xmldoc</code> variable. If you've exported XML from the wiki (via <code>createjQueryXMLDocs.pl</code>) then the XML is compatible. For example, the API browser currently works with all the <a href="http://ui.jquery.com">jQuery Ui</a> documentation (I'll be adding this soon).</p>

<pre><code>$(loadDocs);</code></pre>

<h3>Using the Database</h3>

<p>The API database is a global variable called <code>jquerydocs</code>.</p>

<p>When the database is loaded it triggers a <code>api-load-complete</code> event on the document.</p>

<pre><code>$(document).bind('api-load-complete', function () {
  alert(jquerydocs.version);
});</code></pre>

<h3>jquerydocs</h3>

<p>The <code>jquerydocs</code> is an array with properties and a single find function.  Running a query on the database returns the same structure back filtered by the given string.</p>

<pre><code>&gt;&gt;&gt; console.dir(jquerydocs);

'categories' : [Object name=Core subcategories=[5], 8 more...],
'data' : [Object jquery1=Object, 226 more...],
'letters' : ["a", "b", "c", 20 more...],
'searchNames' : ["jquery1", "jquery2", "jquery3", 224 more...]
'startdoc' : "API",
'timestamp' : "2008-06-03T15:40:58Z"
'version' : "1.2.6"
'find': function()</code></pre>

<h3>Querying</h3>

<p>The best way to get familiar with the API database is to drop <code>jquerydocs</code> in to Firebug and look at the properties.</p>

<p>Generally everything is queried through the find function.</p>

<p>The find function takes two parameters, the second being optional:</p>

<ol>
<li>Query</li>
<li>Field filter</li>
</ol>

<p>If the field filter isn't passed in, the query will search for functions, selectors and properties that 'start with' the query string.</p>

<p>A few examples:</p>

<pre><code>// all API functions starting with 'trigg'
jquerydocs.find('trig');

// all functions in the category start with 'ajax'
jquerydocs.find('ajax', 'category');</code></pre>

<h3>Running Live Examples</h3>

<pre><code>// this would be the ID of the result
var id = 'trigger141',
  item = jquerydocs.data[id],
  i = 0,
  blank_iframe = '/index_blank.html',
  examples = $('exampleHolder'), // arbitrary placeholder
  html = [];

for (i = 0; i &lt; item.examples.length; i++) {
  if (item.examples[i].html) {
      html.push('&lt;iframe id="' 
        + item.examples[i].id 
        + '" src="' 
        + blank_iframe 
        + '"&gt;&lt;/iframe&gt;');
  }
}

examples.append(html.join(''));

// runExample is a helper in api-loader.js
runExample(item);

</code></pre>

<h3>Online/Offline Differences</h3>

<p>If you're running offline, you'll need to ensure the following:</p>

<ol>
<li>In api-loader.js, change where <code>example_jquery</code> is pointing to - currently it points to the Google jQuery copy 1.2.6.  If you're running online, this will update when the loader script is updated.</li>
<li>Again in api-loader.js, change where <code>blank_iframe</code> points to.  This is required for the live examples. If you don't run the examples in your code, then you shouldn't need to change this.</li>
</ol>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=YYMP7j"><img src="http://feeds.feedburner.com/~f/remysharp?i=YYMP7j" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/326759191" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/07/04/jquery-api-update-offline-and-anywhere/feed/</wfw:commentRss>
		<feedburner:origLink>http://remysharp.com/2008/07/04/jquery-api-update-offline-and-anywhere/</feedburner:origLink></item>
		<item>
		<title>maxlength plugin</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/323158008/</link>
		<comments>http://remysharp.com/2008/06/30/maxlength-plugin/#comments</comments>
		<pubDate>Mon, 30 Jun 2008 10:53:44 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Code]]></category>

		<category><![CDATA[jQuery]]></category>

		<category><![CDATA[plugin]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/06/30/maxlength-plugin/</guid>
		<description><![CDATA[It's a fairly common design pattern to want to limit the number of characters the user can input in a field whilst giving feedback to the user on how much they have left.

So I've built a little jQuery plugin to do the work for me.



Maxlength Plugin

The plugin simply reports back the number of characters left [...]]]></description>
			<content:encoded><![CDATA[<p>It's a fairly common design pattern to want to limit the number of characters the user can input in a field whilst giving feedback to the user on how much they have left.</p>

<p>So I've built a little jQuery plugin to do the work for me.</p>

<p><span id="more-232"></span></p>

<h2>Maxlength Plugin</h2>

<p>The plugin simply reports back the number of characters left the user has, with a few extra bells and whistles.</p>

<p><a href="http://remysharp.com/downloads/jquery.maxlength.js">Download the maxlength jQuery plugin</a></p>

<h2>Demonstration</h2>

<p><a href="http://remysharp.com/demo/maxlength.html">View the maxlength demo</a></p>

<p>This demo shows off two example, one limiting on characters and one limiting on words.</p>

<h2>Example Code</h2>

<pre><code>&lt;form action=&quot;/comment&quot;&gt;
  &lt;p&gt;Characters left: &lt;span class=&quot;charsLeft&quot;&gt;10&lt;/span&gt;&lt;/p&gt;
  &lt;textarea maxlength=&quot;10&quot; class=&quot;limited&quot;&gt;&lt;/textarea&gt;
&lt;/form&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  $('textarea.limited').maxlength({
    'feedback' : '.charsLeft'
  });
&lt;/script&gt;</code></pre>

<p>The default version of the maxlength plugin reads the <code>maxlength</code> attribute from the text element.  However, since this isn't a valid HTML attribute if added to a <code>textarea</code> you can configure the plugin to read the value from a hidden input:</p>

<pre><code>&lt;form action=&quot;/comment&quot;&gt;
  &lt;p&gt;Characters left: &lt;span class=&quot;charsLeft&quot;&gt;10&lt;/span&gt;&lt;/p&gt;
  &lt;textarea class=&quot;limited&quot;&gt;&lt;/textarea&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;maxlength&quot; value=&quot;10&quot; /&gt;
&lt;/form&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  $('textarea.limited').maxlength({
    'feedback' : '.charsLeft',
    'useInput' : true
  });
&lt;/script&gt;</code></pre>

<p>The plugin can be applied to <code>input</code> elements, but if you want to limit by words, rather than characters, you need to put the maxlength as a hidden input (otherwise the browser will use it's default behaviour to limit the user's input).</p>

<h3>Plugin Options</h3>

<ul>
<li>feedback - the selector for the element that gives the user feedback. Note that this will be relative to the form the plugin is run against.</li>
<li>hardLimit - whether to stop the user being able to keep adding characters. Defaults to true.</li>
<li>useInput - whether to look for a hidden input named 'maxlength' instead of the maxlength attribute. Defaults to false.</li>
<li>words - limit by characters or words, set this to true to limit by words. Defaults to false.</li>
</ul>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=qmm0Oi"><img src="http://feeds.feedburner.com/~f/remysharp?i=qmm0Oi" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/323158008" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/06/30/maxlength-plugin/feed/</wfw:commentRss>
		<feedburner:origLink>http://remysharp.com/2008/06/30/maxlength-plugin/</feedburner:origLink></item>
		<item>
		<title>Query String to Object via regex</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/318733677/</link>
		<comments>http://remysharp.com/2008/06/24/query-string-to-object-via-regex/#comments</comments>
		<pubDate>Tue, 24 Jun 2008 08:41:12 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Dump]]></category>

		<category><![CDATA[Code]]></category>

		<category><![CDATA[regex]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/06/24/query-string-to-object-via-regex/</guid>
		<description><![CDATA[Just sharing a nice little code snippet that makes use of regular expressions instead of loops for converting.



function getQuery(s) {
  var query = {};

  s.replace(/\b([^&#38;=]*)=([^&#38;=]*)\b/g, function (m, a, d) {
    if (typeof query[a] != 'undefined') {
      query[a] += ',' + d;
    } [...]]]></description>
			<content:encoded><![CDATA[<p>Just sharing a nice little code snippet that makes use of regular expressions instead of loops for converting.</p>

<p><span id="more-231"></span></p>

<pre><code>function getQuery(s) {
  var query = {};

  s.replace(/\b([^&amp;=]*)=([^&amp;=]*)\b/g, function (m, a, d) {
    if (typeof query[a] != 'undefined') {
      query[a] += ',' + d;
    } else {
      query[a] = d;
    }
  });

  return query;
}

// usage:
// var o = getQuery('maps.google.co.uk/maps?f=q&amp;q=brighton&amp;ie=UTF8&amp;iwloc=addr');
// console.dir(o);</code></pre>

<p>Note that if a query string key is found more than once, it's value is appended.  If you don't want this functionality, remove the test for <code>undefined</code>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=XB7MHi"><img src="http://feeds.feedburner.com/~f/remysharp?i=XB7MHi" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/318733677" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/06/24/query-string-to-object-via-regex/feed/</wfw:commentRss>
		<feedburner:origLink>http://remysharp.com/2008/06/24/query-string-to-object-via-regex/</feedburner:origLink></item>
		<item>
		<title>Feed of Events via Microformats</title>
		<link>http://feeds.feedburner.com/~r/remysharp/~3/314652252/</link>
		<comments>http://remysharp.com/2008/06/18/feed-of-events-via-microformats/#comments</comments>
		<pubDate>Wed, 18 Jun 2008 14:26:36 +0000</pubDate>
		<dc:creator>Remy Sharp</dc:creator>
		
		<category><![CDATA[Web]]></category>

		<category><![CDATA[microformats]]></category>

		<guid isPermaLink="false">http://remysharp.com/2008/06/18/feed-of-events-via-microformats/</guid>
		<description><![CDATA[A friend and colleague, Chris, has recently launched a gaming network site: Thumbslap which allows friends and the like to organise game meet ups online (or offline if they wish).

Pretty early on I could see how Microformats, particularly the hCalendar should be applied, but what Chris really wanted to do the job was an automatic [...]]]></description>
			<content:encoded><![CDATA[<p>A friend and colleague, Chris, has recently launched a gaming network site: <a href="http://thumbslap.com">Thumbslap</a> which allows friends and the like to organise game meet ups online (or offline if they wish).</p>

<p>Pretty early on I could see how <a href="http://microformats.org/" title="Microformats">Microformats</a>, particularly the <a href="http://microformats.org/wiki/hcalendar">hCalendar</a> should be applied, but what Chris really wanted to do the job was an automatic feed that your calendar software could subscribe to, and that would automatically updates, then, time permitting, Microformats could be added.</p>

<p>Ah ha! The solution lied entirely in Microformats!</p>

<p><span id="more-230"></span></p>

<p>I'm not sure how I missed this nugget because it's been there for a good while now, <em>and</em> it helps build exactly the functionality that Chris needed:</p>

<p><a href="http://feeds.technorati.com/events/">http://feeds.technorati.com/events/</a></p>

<p>Technorati offer the solution in two forms: one off download, and live subscribe, the difference being the protocol - http and webcal respectively.</p>

<p>By passing in my URL with hCalendar marked up Microformats, Technorati will parse the page and return it in an iCal format.</p>

<p>By linking to:</p>

<p><a href="webcal://feeds.technorati.com/events/http://2008.dconstruct.org/schedule/">webcal://feeds.technorati.com/events/http://2008.dconstruct.org/schedule/</a></p>

<p>I will have a live calendar in iCal of the <a href="http://2008.dconstruct.org">dConstruct</a> event schedule (which is actually where I spotted the webcal subscription method).  Alternatively I can add it manually (the URL) in my Google Calendar app.</p>

<p>No doubt Chris will be updating <a href="http://thumbslap.com">Thumbslap</a> to include the Microformats via the user's homepage shortly!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/remysharp?a=qH9wui"><img src="http://feeds.feedburner.com/~f/remysharp?i=qH9wui" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/remysharp/~4/314652252" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://remysharp.com/2008/06/18/feed-of-events-via-microformats/feed/</wfw:commentRss>
		<feedburner:origLink>http://remysharp.com/2008/06/18/feed-of-events-via-microformats/</feedburner:origLink></item>
	</channel>
</rss>
