<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>Pixel Acres &#187; Programming</title>
	<atom:link href="http://f6design.com/journal/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://f6design.com/journal</link>
	<description>Adventures in web and graphic design</description>
	<lastBuildDate>Wed, 28 Dec 2011 22:41:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>Build a parallax scrolling website interface with jQuery and CSS</title>
		<link>http://f6design.com/journal/2011/08/06/build-a-parallax-scrolling-website-interface-with-jquery-and-css/</link>
		<comments>http://f6design.com/journal/2011/08/06/build-a-parallax-scrolling-website-interface-with-jquery-and-css/#comments</comments>
		<pubDate>Sun, 07 Aug 2011 02:36:52 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML/XHTML]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/?p=2141</guid>
		<description><![CDATA[Parallax scrolling website interfaces have been popping up all over the place recently. I didn&#8217;t want to miss out on the fun, so I have put together a parallax scrolling demo built using jQuery and CSS. Parallax what? Even if you&#8217;re not familiar with the term &#8220;parallax scrolling&#8221; you will certainly be familiar with the [...]]]></description>
			<content:encoded><![CDATA[<p>Parallax scrolling website interfaces have been popping up all over the place recently. I didn&#8217;t want to miss out on the fun, so I have put together a parallax scrolling demo built using jQuery and CSS.</p>
<p><a href="http://f6design.com/projects/parallax-scrolling/"><img src="http://f6design.com/journal/wp-content/uploads/2011/08/parallax-screenshot-578w.jpg" alt="Parallax scrolling interface" width="578" height="423" class="contentImg" /></a></p>
<h3>Parallax what?</h3>
<p>Even if you&#8217;re not familiar with the term &#8220;parallax scrolling&#8221; you will certainly be familiar with the technique. <a href="http://en.wikipedia.org/wiki/Parallax_scrolling">Parallax scrolling</a> is a 2d animation process that creates an illusion of depth by animating foreground layers faster than background layers. When you observe the landscape from a moving car, objects closer to the car appear to pass you faster than scenery further away. Parallax scrolling uses the same principle to trick the viewer into thinking they are observing a 3d scene.</p>
<h3>Demo and Download</h3>
<p>My demo web page shows one approach to building a vertical parallax scrolling interface:</p>
<p><a href="http://f6design.com/projects/parallax-scrolling/">View demo</a><br />
<a href="http://f6design.com/projects/parallax-scrolling/parallax-scrolling-demo.zip">Download source</a></p>
<p>You can scroll in the usual fashion, use the navigation menu at the right-hand side of the page, or the next/prev buttons that appear underneath each article. As you scroll, the page&#8217;s four content layers are animated independently of one another to create an illusion of depth.</p>
<p>The scrolling looks smoothest in Safari (at least that&#8217;s the case on my PC), but my demo should work in any modern browser.</p>
<p><em><strong>Disclaimer 1:</strong> Because this is just an experiment I&#8217;ve not spent any time optimising the demo to work on mobile devices. I wanted to keep the demo lean &#8216;n&#8217; mean, and not clutter it by sniffing mobile browsers and forking my code. On a production site you&#8217;d want to ensure that the site degrades gracefully on mobile devices, where scroll events and fixed positioning might work in unexpected ways.</em></p>
<p><em><strong>Disclaimer 2:</strong> The navigation menu in my demo is inspired by the menu on the Nike <a href="http://www.nikebetterworld.com/">Better World</a> website. If you plan on implementing a similar menu on a production site, please be aware of its origin.</em></p>
<h3>How it works</h3>
<p>The articles and background layers are given a fixed positioned with CSS, and assigned a <code>z-index</code> so that the foreground layers appear above the background layers. The four layers are: small clouds, large clouds, balloon/landscape images, articles.</p>

<div class="wp_syntax"><div class="code"><pre class="css" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* foreground (ballons/landscape) */</span>
<span style="color: #cc00cc;">#parallax-bg3</span> <span style="color: #00AA00;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">z-index</span><span style="color: #00AA00;">:</span> <span style="color: #cc66cc;">3</span><span style="color: #00AA00;">;</span>
    <span style="color: #000000; font-weight: bold;">position</span><span style="color: #00AA00;">:</span> <span style="color: #993333;">fixed</span><span style="color: #00AA00;">;</span>
    <span style="color: #000000; font-weight: bold;">left</span><span style="color: #00AA00;">:</span> <span style="color: #933;"><span style="color: #cc66cc;">50</span>%</span><span style="color: #00AA00;">;</span> <span style="color: #808080; font-style: italic;">/* align left edge with center of viewport */</span>
    <span style="color: #000000; font-weight: bold;">top</span><span style="color: #00AA00;">:</span> <span style="color: #cc66cc;">0</span><span style="color: #00AA00;">;</span>
    <span style="color: #000000; font-weight: bold;">width</span><span style="color: #00AA00;">:</span> <span style="color: #933;">940px</span><span style="color: #00AA00;">;</span>
    <span style="color: #000000; font-weight: bold;">margin-left</span><span style="color: #00AA00;">:</span> <span style="color: #933;">-470px</span><span style="color: #00AA00;">;</span> <span style="color: #808080; font-style: italic;">/* move left by half element's width */</span>
<span style="color: #00AA00;">&#125;</span></pre></div></div>

<p>Within each layer individual content elements are absolutely positioned. This was the most fiddly part of the process, since the elements need to positioned in such a way that they align in a pleasing manner when the user scrolls to any of the four articles. In this case it was really just a process of trial and error.</p>

<div class="wp_syntax"><div class="code"><pre class="css" style="font-family:monospace;"><span style="color: #cc00cc;">#bg3-1</span> <span style="color: #00AA00;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">position</span><span style="color: #00AA00;">:</span> <span style="color: #993333;">absolute</span><span style="color: #00AA00;">;</span>
    <span style="color: #000000; font-weight: bold;">top</span><span style="color: #00AA00;">:</span> <span style="color: #933;">-111px</span><span style="color: #00AA00;">;</span>
    <span style="color: #000000; font-weight: bold;">left</span><span style="color: #00AA00;">:</span> <span style="color: #933;">355px</span><span style="color: #00AA00;">;</span>
<span style="color: #00AA00;">&#125;</span>
<span style="color: #cc00cc;">#bg3-2</span> <span style="color: #00AA00;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">position</span><span style="color: #00AA00;">:</span> <span style="color: #993333;">absolute</span><span style="color: #00AA00;">;</span>
    <span style="color: #000000; font-weight: bold;">top</span><span style="color: #00AA00;">:</span> <span style="color: #933;">812px</span><span style="color: #00AA00;">;</span>
    <span style="color: #000000; font-weight: bold;">left</span><span style="color: #00AA00;">:</span> <span style="color: #933;">321px</span><span style="color: #00AA00;">;</span>
<span style="color: #00AA00;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* etc... */</span></pre></div></div>

<p>A few lines of jQuery control the parallax effect, triggered by a scroll event. I was surprised how easy this was to achieve, it is literally just a handful of lines of code.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$<span style="color: #009900;">&#40;</span>window<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">bind</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'scroll'</span><span style="color: #339933;">,</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>e<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
    parallaxScroll<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #003366; font-weight: bold;">function</span> parallaxScroll<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
    <span style="color: #003366; font-weight: bold;">var</span> scrolled <span style="color: #339933;">=</span> $<span style="color: #009900;">&#40;</span>window<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">scrollTop</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#parallax-bg1'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">css</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'top'</span><span style="color: #339933;">,</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">-</span><span style="color: #009900;">&#40;</span>scrolled<span style="color: #339933;">*</span>.25<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'px'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#parallax-bg2'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">css</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'top'</span><span style="color: #339933;">,</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">-</span><span style="color: #009900;">&#40;</span>scrolled<span style="color: #339933;">*</span>.5<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'px'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    $<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#parallax-bg3'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">css</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'top'</span><span style="color: #339933;">,</span><span style="color: #009900;">&#40;</span><span style="color: #CC0000;">0</span><span style="color: #339933;">-</span><span style="color: #009900;">&#40;</span>scrolled<span style="color: #339933;">*</span>.75<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #3366CC;">'px'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>As you can see the CSS <code>top</code> property is used to move each layer as the user scrolls. The foreground layer is always aligned to the top of the document, while the movement of other layers is adjusted according to their depth. The lower a layer sits in the stack, the less distance it is moved.</p>
<p>The rest of the jQuery is concerned with controlling the navigation menus. When the user clicks a navigation button the page scrolls to the top of the associated article. In the event that the user has JavaScript disabled, regular HTML anchor links still allow the page to be navigated, but without any fancy pants animations.</p>
<h3>Next steps</h3>
<p>I&#8217;m sure there are plenty of other approaches to parallax scrolling, and hopefully my experiment provides a starting point for your own explorations of the technique.</p>
<h3>Update</h3>
<p>I have updated the demo so that each parallax layer is given a fixed, rather than absolute, position. This approach gives a smoother scrolling effect.</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2011/08/06/build-a-parallax-scrolling-website-interface-with-jquery-and-css/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
		<item>
		<title>FormBuilder updated to v1.5</title>
		<link>http://f6design.com/journal/2011/07/18/formbuilder-updated-to-v1-5/</link>
		<comments>http://f6design.com/journal/2011/07/18/formbuilder-updated-to-v1-5/#comments</comments>
		<pubDate>Tue, 19 Jul 2011 07:10:26 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Toolbox]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/?p=2079</guid>
		<description><![CDATA[It&#8217;s been almost exactly four years since I updated my FormBuilder PHP class, but believe it or not I have been slowly modifying and improving the class during the intervening years. I figured it was high time I rolled those improvements into the public version of the class, so here&#8217;s what&#8217;s new in version 1.5 [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been almost exactly four years since I updated my <a href="http://f6design.com/journal/2007/04/27/formbuilder-html-forms-made-simple/">FormBuilder</a> PHP class, but believe it or not I have been slowly modifying and improving the class during the intervening years. I figured it was high time I rolled those improvements into the public version of the class, so here&#8217;s what&#8217;s new in version 1.5 of FormBuilder:</p>
<ul>
<li>Better handling of checkbox results in the <code>emailResults</code> method.</li>
<li>A custom form submit URL can be passed to the FormBuilder constructor. Useful when using FormBuilder in an environment that is performing URL rewriting.</li>
<li>Replaced deprecated <code>ergei</code> functions with <code>preg_match</code>.</li>
<li>Checkbox field types are correctly processed when field is not mandatory, and the user didn&#8217;t check any of the available options.</li>
<li>Added new field type: file (for file uploads). Note that files are currently <em>not</em> emailed when using the <code>emailResults</code> method. Any handling of the uploaded files should be accomplished manually by accessing PHP&#8217;s <code>$_Files</code> array.</li>
<li>The textbox and textarea field types now accept an optional <code>defaultvalue</code> parameter.</li>
<li>Fixed a bug that meant checkboxes had a CSS class of &#8216;fbheckbox&#8217; instead of &#8216;fbcheckbox&#8217;.</li>
</ul>
<p>If you encounter any problems with the new version please let me know.</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2011/07/18/formbuilder-updated-to-v1-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Monokai theme for phpDesigner</title>
		<link>http://f6design.com/journal/2011/04/27/monokai-theme-for-phpdesigner/</link>
		<comments>http://f6design.com/journal/2011/04/27/monokai-theme-for-phpdesigner/#comments</comments>
		<pubDate>Wed, 27 Apr 2011 22:15:42 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML/XHTML]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Toolbox]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/?p=1364</guid>
		<description><![CDATA[Last year I created a port of the Monokai syntax highlighting theme for phpDesigner and posted it in the phpDesigner forums, but I think it deserves a proper home so I&#8217;m archiving it here on Pixel Acres. PHP example: HTML example: CSS example: JavaScript example: I&#8217;m not sure who designed the Monokai theme originally, but [...]]]></description>
			<content:encoded><![CDATA[<p>Last year I created a port of the Monokai syntax highlighting theme for <a href="http://www.mpsoftware.dk/phpdesigner.php">phpDesigner</a> and posted it in the <a href="http://www.facebook.com/topic.php?uid=108672675347&#038;topic=11050">phpDesigner forums</a>, but I think it deserves a proper home so I&#8217;m archiving it here on Pixel Acres.</p>
<h4>PHP example:</h4>
<p><img src="http://f6design.com/journal/wp-content/uploads/2011/04/monokai_theme_php.png" alt="" title="monokai_theme_php" width="450" height="389" class="alignnone size-full wp-image-1377" /></p>
<h4>HTML example:</h4>
<p><img src="http://f6design.com/journal/wp-content/uploads/2011/04/monokai_theme_html.png" alt="" title="monokai_theme_html" width="450" height="179" class="alignnone size-full wp-image-1378" /></p>
<h4>CSS example:</h4>
<p><img src="http://f6design.com/journal/wp-content/uploads/2011/04/monokai_theme_css.png" alt="" title="monokai_theme_css" width="450" height="224" class="alignnone size-full wp-image-1381" /></p>
<h4>JavaScript example:</h4>
<p><img src="http://f6design.com/journal/wp-content/uploads/2011/04/monokai_theme_js.png" alt="" title="monokai_theme_js" width="450" height="258" class="alignnone size-full wp-image-1383" /></p>
<p>I&#8217;m not sure who designed the Monokai theme originally, but it has cropped up in <a href="http://macromates.com/">Textmate</a>, <a href="http://notepad-plus-plus.org/">Notepad++</a>, <a href="http://www.sublimetext.com/">Sublime</a> &#8211; where it is the default colour scheme &#8211; and no doubt a bunch of other editors as well.</p>
<p>My version of Monokai is based on a Notepad++ theme, but I have standardised the color of variables (green) and symbols (white) across all the languages.</p>
<p>I don&#8217;t actually use phpDesigner much these days &#8211; I&#8217;ve jumped ship for <a href="http://www.aptana.com/products/studio3">Aptana Studio 3</a> &#8211; but I still think it is a great code editor, and hopefully someone out there will get some use from my theme.</p>
<h3>Download</h3>
<p><a href="http://f6design.com/projects/f6-monokai-phpd-theme/f6_monokai_phpd_theme.rar">Download Monokai theme for phpDesigner</a></p>
<p>Installation and uninstallation instructions are included with the theme. The theme has syntax highlighting colors for the four languages I work with: HTML, CSS, JavaScript and PHP.</p>
<h3>Important</h3>
<p>There is currently a bug in phpDesigner that sometimes causes the ‘separator’ colors to revert to their defaults (e.g. the color of  tags reverts to black). The only sure-fire way to get things back to normal is to open the phpDesigner preferences dialog, and click ‘OK’.</p>
<p>Apparently this bug will be fixed in the next release of phpDesigner (yay!).</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2011/04/27/monokai-theme-for-phpdesigner/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Display recent Twitter tweets using PHP</title>
		<link>http://f6design.com/journal/2010/10/07/display-recent-twitter-tweets-using-php/</link>
		<comments>http://f6design.com/journal/2010/10/07/display-recent-twitter-tweets-using-php/#comments</comments>
		<pubDate>Fri, 08 Oct 2010 01:26:28 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/?p=1151</guid>
		<description><![CDATA[If you&#8217;ve ever wanted to display your latest Twitter tweets on a website, here is method to do that using PHP. My approach has the following features: Tweets are cached to avoid exceeding Twitter&#8217;s limit of 150 requests for a user&#8217;s RSS feed per hour A fallback is provided in case the twitter feed fails [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve ever wanted to display your latest Twitter tweets on a website, here is method to do that using PHP. My approach has the following features:</p>
<ul>
<li>Tweets are cached to avoid exceeding Twitter&#8217;s limit of 150 requests for a user&#8217;s RSS feed per hour</li>
<li>A fallback is provided in case the twitter feed fails to load</li>
<li>Replies (tweets beginning with @) can optionally be ignored</li>
<li>A configuration parameter allows you to specify how many tweets are displayed</li>
<li>Dates can optionally be displayed in &#8220;Twitter style&#8221;, e.g. &#8220;12 minutes ago&#8221;</li>
<li>You can edit the HTML that wraps your tweets, tweet status and meta information</li>
<li>The username which prepends each tweet in Twitter RSS feeds is automatically stripped</li>
</ul>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
&nbsp;
<span style="color: #009933; font-style: italic;">/**
 * TWITTER FEED PARSER
 * 
 * @version	1.1.1
 * @author	Jonathan Nicol
 * @link	http://f6design.com/journal/2010/10/07/display-recent-twitter-tweets-using-php/
 * 
 * Notes:
 * We employ caching because Twitter only allows their RSS feeds to be accesssed 150
 * times an hour per user client.
 * --
 * Dates can be displayed in Twitter style (e.g. &quot;1 hour ago&quot;) by setting the 
 * $twitter_style_dates param to true.
 * 
 * Credits:
 * Hashtag/username parsing based on: http://snipplr.com/view/16221/get-twitter-tweets/
 * Feed caching: http://www.addedbytes.com/articles/caching-output-in-php/
 * Feed parsing: http://boagworld.com/forum/comments.php?DiscussionID=4639
 */</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">function</span> display_latest_tweets<span style="color: #009900;">&#40;</span>
	<span style="color: #000088;">$twitter_user_id</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$cache_file</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'./twitter.txt'</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$tweets_to_display</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">100</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$ignore_replies</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$twitter_wrap_open</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'&lt;h2&gt;Latest tweets&lt;/h2&gt;&lt;ul id=&quot;twitter&quot;&gt;'</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$twitter_wrap_close</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'&lt;/ul&gt;'</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$tweet_wrap_open</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'&lt;li&gt;&lt;span class=&quot;status&quot;&gt;'</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$meta_wrap_open</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'&lt;/span&gt;&lt;span class=&quot;meta&quot;&gt; '</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$meta_wrap_close</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'&lt;/span&gt;'</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$tweet_wrap_close</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'&lt;/li&gt;'</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$date_format</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'g:i A M jS'</span><span style="color: #339933;">,</span>
	<span style="color: #000088;">$twitter_style_dates</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
	<span style="color: #666666; font-style: italic;">// Seconds to cache feed (1 hour).</span>
	<span style="color: #000088;">$cachetime</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">60</span><span style="color: #339933;">*</span><span style="color: #cc66cc;">60</span><span style="color: #339933;">;</span>
	<span style="color: #666666; font-style: italic;">// Time that the cache was last filled.</span>
	<span style="color: #000088;">$cache_file_created</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">@</span><span style="color: #990000;">file_exists</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$cache_file</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> ? <span style="color: #339933;">@</span><span style="color: #990000;">filemtime</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$cache_file</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #666666; font-style: italic;">// A flag so we know if the feed was successfully parsed.</span>
	<span style="color: #000088;">$tweet_found</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #666666; font-style: italic;">// Show file from cache if still valid.</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">time</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">-</span> <span style="color: #000088;">$cachetime</span> <span style="color: #339933;">&lt;</span> <span style="color: #000088;">$cache_file_created</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
		<span style="color: #000088;">$tweet_found</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">;</span>
		<span style="color: #666666; font-style: italic;">// Display tweets from the cache.</span>
		<span style="color: #339933;">@</span><span style="color: #990000;">readfile</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$cache_file</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>	
&nbsp;
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
&nbsp;
		<span style="color: #666666; font-style: italic;">// Cache file not found, or old. Fetch the RSS feed from Twitter.</span>
		<span style="color: #000088;">$rss</span> <span style="color: #339933;">=</span> <span style="color: #339933;">@</span><span style="color: #990000;">file_get_contents</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'http://twitter.com/statuses/user_timeline/'</span><span style="color: #339933;">.</span><span style="color: #000088;">$twitter_user_id</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'.rss'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$rss</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
			<span style="color: #666666; font-style: italic;">// Parse the RSS feed to an XML object.</span>
			<span style="color: #000088;">$xml</span> <span style="color: #339933;">=</span> <span style="color: #339933;">@</span><span style="color: #990000;">simplexml_load_string</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$rss</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
			<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$xml</span> <span style="color: #339933;">!==</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
				<span style="color: #666666; font-style: italic;">// Error check: Make sure there is at least one item.</span>
				<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">count</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$xml</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">channel</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">item</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
					<span style="color: #000088;">$tweet_count</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span>
&nbsp;
					<span style="color: #666666; font-style: italic;">// Start output buffering.</span>
					<span style="color: #990000;">ob_start</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
					<span style="color: #666666; font-style: italic;">// Open the twitter wrapping element.</span>
					<span style="color: #000088;">$twitter_html</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$twitter_wrap_open</span><span style="color: #339933;">;</span>
&nbsp;
					<span style="color: #666666; font-style: italic;">// Iterate over tweets.</span>
					<span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$xml</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">channel</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">item</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$tweet</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
						<span style="color: #666666; font-style: italic;">// Twitter feeds begin with the username, &quot;e.g. User name: Blah&quot;</span>
						<span style="color: #666666; font-style: italic;">// so we need to strip that from the front of our tweet.</span>
						<span style="color: #000088;">$tweet_desc</span> <span style="color: #339933;">=</span> <span style="color: #990000;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$tweet</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">description</span><span style="color: #339933;">,</span><span style="color: #990000;">strpos</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$tweet</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">description</span><span style="color: #339933;">,</span><span style="color: #0000ff;">&quot;:&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span><span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
						<span style="color: #000088;">$tweet_desc</span> <span style="color: #339933;">=</span> <span style="color: #990000;">htmlspecialchars</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$tweet_desc</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
						<span style="color: #000088;">$tweet_first_char</span> <span style="color: #339933;">=</span> <span style="color: #990000;">substr</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$tweet_desc</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
						<span style="color: #666666; font-style: italic;">// If we are not gnoring replies, or tweet is not a reply, process it.</span>
						<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$tweet_first_char</span><span style="color: #339933;">!=</span><span style="color: #0000ff;">'@'</span> <span style="color: #339933;">||</span> <span style="color: #000088;">$ignore_replies</span><span style="color: #339933;">==</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
							<span style="color: #000088;">$tweet_found</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #339933;">;</span>
							<span style="color: #000088;">$tweet_count</span><span style="color: #339933;">++;</span>
&nbsp;
							<span style="color: #666666; font-style: italic;">// Add hyperlink html tags to any urls, twitter ids or hashtags in the tweet.</span>
							<span style="color: #000088;">$tweet_desc</span> <span style="color: #339933;">=</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'/(https?:\/\/[^\s&quot;&lt;&gt;]+)/'</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'&lt;a href=&quot;$1&quot;&gt;$1&lt;/a&gt;'</span><span style="color: #339933;">,</span><span style="color: #000088;">$tweet_desc</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
							<span style="color: #000088;">$tweet_desc</span> <span style="color: #339933;">=</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'/(^|[\n\s])@([^\s&quot;\t\n\r&lt;:]*)/is'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'$1&lt;a href=&quot;http://twitter.com/$2&quot;&gt;@$2&lt;/a&gt;'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$tweet_desc</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
							<span style="color: #000088;">$tweet_desc</span> <span style="color: #339933;">=</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'/(^|[\n\s])#([^\s&quot;\t\n\r&lt;:]*)/is'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'$1&lt;a href=&quot;http://twitter.com/search?q=%23$2&quot;&gt;#$2&lt;/a&gt;'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$tweet_desc</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
 							<span style="color: #666666; font-style: italic;">// Convert Tweet display time to a UNIX timestamp. Twitter timestamps are in UTC/GMT time.</span>
							<span style="color: #000088;">$tweet_time</span> <span style="color: #339933;">=</span> <span style="color: #990000;">strtotime</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$tweet</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">pubDate</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>	
 							<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$twitter_style_dates</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
								<span style="color: #666666; font-style: italic;">// Current UNIX timestamp.</span>
								<span style="color: #000088;">$current_time</span> <span style="color: #339933;">=</span> <span style="color: #990000;">time</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
								<span style="color: #000088;">$time_diff</span> <span style="color: #339933;">=</span> <span style="color: #990000;">abs</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$current_time</span> <span style="color: #339933;">-</span> <span style="color: #000088;">$tweet_time</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
								<span style="color: #b1b100;">switch</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$time_diff</span><span style="color: #009900;">&#41;</span> 
								<span style="color: #009900;">&#123;</span>
									<span style="color: #b1b100;">case</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$time_diff</span> <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">60</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span>
										<span style="color: #000088;">$display_time</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$time_diff</span><span style="color: #339933;">.</span><span style="color: #0000ff;">' seconds ago'</span><span style="color: #339933;">;</span>                  
										<span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>      
									<span style="color: #b1b100;">case</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$time_diff</span> <span style="color: #339933;">&gt;=</span> <span style="color: #cc66cc;">60</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #000088;">$time_diff</span> <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">3600</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span>
										<span style="color: #000088;">$min</span> <span style="color: #339933;">=</span> <span style="color: #990000;">floor</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$time_diff</span><span style="color: #339933;">/</span><span style="color: #cc66cc;">60</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
										<span style="color: #000088;">$display_time</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$min</span><span style="color: #339933;">.</span><span style="color: #0000ff;">' minutes ago'</span><span style="color: #339933;">;</span>                  
										<span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>      
									<span style="color: #b1b100;">case</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$time_diff</span> <span style="color: #339933;">&gt;=</span> <span style="color: #cc66cc;">3600</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #000088;">$time_diff</span> <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">86400</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span>
										<span style="color: #000088;">$hour</span> <span style="color: #339933;">=</span> <span style="color: #990000;">floor</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$time_diff</span><span style="color: #339933;">/</span><span style="color: #cc66cc;">3600</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
										<span style="color: #000088;">$display_time</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'about '</span><span style="color: #339933;">.</span><span style="color: #000088;">$hour</span><span style="color: #339933;">.</span><span style="color: #0000ff;">' hour'</span><span style="color: #339933;">;</span>
										<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$hour</span> <span style="color: #339933;">&gt;</span> <span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span> <span style="color: #000088;">$display_time</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">'s'</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#125;</span>
										<span style="color: #000088;">$display_time</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">' ago'</span><span style="color: #339933;">;</span>
										<span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>          
									<span style="color: #b1b100;">default</span><span style="color: #339933;">:</span>
										<span style="color: #000088;">$display_time</span> <span style="color: #339933;">=</span> <span style="color: #990000;">date</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$date_format</span><span style="color: #339933;">,</span><span style="color: #000088;">$tweet_time</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
										<span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>
								<span style="color: #009900;">&#125;</span>
 							<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
 								<span style="color: #000088;">$display_time</span> <span style="color: #339933;">=</span> <span style="color: #990000;">date</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$date_format</span><span style="color: #339933;">,</span><span style="color: #000088;">$tweet_time</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
 							<span style="color: #009900;">&#125;</span>
&nbsp;
							<span style="color: #666666; font-style: italic;">// Render the tweet.</span>
							<span style="color: #000088;">$twitter_html</span> <span style="color: #339933;">.=</span> <span style="color: #000088;">$tweet_wrap_open</span><span style="color: #339933;">.</span><span style="color: #000088;">$tweet_desc</span><span style="color: #339933;">.</span><span style="color: #000088;">$meta_wrap_open</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'&lt;a href=&quot;http://twitter.com/'</span><span style="color: #339933;">.</span><span style="color: #000088;">$twitter_user_id</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'&quot;&gt;'</span><span style="color: #339933;">.</span><span style="color: #000088;">$display_time</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'&lt;/a&gt;'</span><span style="color: #339933;">.</span><span style="color: #000088;">$meta_wrap_close</span><span style="color: #339933;">.</span><span style="color: #000088;">$tweet_wrap_close</span><span style="color: #339933;">;</span>
&nbsp;
						<span style="color: #009900;">&#125;</span>
&nbsp;
						<span style="color: #666666; font-style: italic;">// If we have processed enough tweets, stop.</span>
						<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$tweet_count</span> <span style="color: #339933;">&gt;=</span> <span style="color: #000088;">$tweets_to_display</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
							<span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>
						<span style="color: #009900;">&#125;</span>
&nbsp;
					<span style="color: #009900;">&#125;</span>
&nbsp;
					<span style="color: #666666; font-style: italic;">// Close the twitter wrapping element.</span>
					<span style="color: #000088;">$twitter_html</span> <span style="color: #339933;">.=</span> <span style="color: #000088;">$twitter_wrap_close</span><span style="color: #339933;">;</span>
					<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$twitter_html</span><span style="color: #339933;">;</span>
&nbsp;
					<span style="color: #666666; font-style: italic;">// Generate a new cache file.</span>
					<span style="color: #000088;">$file</span> <span style="color: #339933;">=</span> <span style="color: #339933;">@</span><span style="color: #990000;">fopen</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$cache_file</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'w'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
					<span style="color: #666666; font-style: italic;">// Save the contents of output buffer to the file, and flush the buffer. </span>
					<span style="color: #339933;">@</span><span style="color: #990000;">fwrite</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$file</span><span style="color: #339933;">,</span> <span style="color: #990000;">ob_get_contents</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> 
					<span style="color: #339933;">@</span><span style="color: #990000;">fclose</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$file</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> 
					<span style="color: #990000;">ob_end_flush</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
				<span style="color: #009900;">&#125;</span>
			<span style="color: #009900;">&#125;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span> 
	<span style="color: #666666; font-style: italic;">// In case the RSS feed did not parse or load correctly, show a link to the Twitter account.</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #000088;">$tweet_found</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$twitter_wrap_open</span><span style="color: #339933;">.</span><span style="color: #000088;">$tweet_wrap_open</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'Oops, our twitter feed is unavailable right now. '</span><span style="color: #339933;">.</span><span style="color: #000088;">$meta_wrap_open</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'&lt;a href=&quot;http://twitter.com/'</span><span style="color: #339933;">.</span><span style="color: #000088;">$twitter_user_id</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'&quot;&gt;Follow us on Twitter&lt;/a&gt;'</span><span style="color: #339933;">.</span><span style="color: #000088;">$meta_wrap_close</span><span style="color: #339933;">.</span><span style="color: #000088;">$tweet_wrap_close</span><span style="color: #339933;">.</span><span style="color: #000088;">$twitter_wrap_close</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
display_latest_tweets<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'YOUR_TWITTER_ID'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<h3>Usage</h3>
<p>You should edit the Twitter ID in the function call above before using the function (it appears at the very bottom of the code snippet).</p>
<p>You probably also want to edit the location where the twitter feed is cached &#8211; by default it is written to the root level of your domain. To change the location, modify the <code>$cache_file</code> variable, or pass the new location as a function parameter.</p>
<h3>Notes</h3>
<p>Twitter feeds may contain UTF-8 characters. I have found that running PHP&#8217;s <code>utf_decode</code> method on tweets didn&#8217;t have the expected result, so my recommendation is to instead <a href="http://www.w3.org/International/O-charset">set the charset of your HTML page to UTF-8</a>. Really we should all be doing this anyway.</p>
<h3>Credits</h3>
<p>The hashtag/username parsing in my example is from <a href="http://snipplr.com/view/16221/get-twitter-tweets/">Get Twitter Tweets</a> by <a href="http://snipplr.com/users/gripnrip/">gripnrip</a>.</p>
<p>My RSS parsing is based on replies in the forum discussion <a href="http://boagworld.com/forum/comments.php?DiscussionID=4639">&#8220;embedding twitter tweets&#8221;</a> on the Boagworld website.</p>
<p>The file caching is based on the AddedBytes article <a href="http://www.addedbytes.com/articles/caching-output-in-php/">&#8220;Caching output in PHP&#8221;</a>.</p>
<h3>Changelog</h3>
<h4>v1.1.1, 28 January 2011</h4>
<ul>
<li>Fixed bug in the logic that pluralises the number of hours since a tweet</li>
</ul>
<h4>v1.1, 14 January 2011</h4>
<ul>
<li>Fixed URL parsing regular expression to make it much more liberal. It will no longer choke on hyphens.</li>
<li>Added an optional parameter $twitter_style_dates which will format dates the same way as the Twitter website, e.g. &#8220;12 minutes ago&#8221;. It is set to false by default.</li>
<li>Dates are now formatted the same as on the Twitter website, e.g. &#8220;3:48 PM Jan 14th&#8221;. This can be overridden using the $date_format parameter.</li>
<li>The username parser will no longer include a trailing colon as part of the username, so the autolink for a string like &#8220;@username: hello!&#8221; won&#8217;t get messed up.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2010/10/07/display-recent-twitter-tweets-using-php/feed/</wfw:commentRss>
		<slash:comments>66</slash:comments>
		</item>
		<item>
		<title>Sunset: A syntax highlighting theme for phpDesigner</title>
		<link>http://f6design.com/journal/2010/09/29/sunset-a-syntax-highlighting-theme-for-phpdesigner/</link>
		<comments>http://f6design.com/journal/2010/09/29/sunset-a-syntax-highlighting-theme-for-phpdesigner/#comments</comments>
		<pubDate>Thu, 30 Sep 2010 07:19:24 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML/XHTML]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Toolbox]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/?p=1057</guid>
		<description><![CDATA[My weapon of choice for code editing is the excellent program phpDesigner, but every so often I like to test drive a different editor to see what I might be missing out on. Recently I spent some time playing with Notepad++, and one feature that jumped out at me was the ability to choose from [...]]]></description>
			<content:encoded><![CDATA[<p>My weapon of choice for code editing is the excellent program <a href="http://www.mpsoftware.dk/">phpDesigner</a>, but every so often I like to test drive a different editor to see what I might be missing out on. Recently I  spent some time playing with <a href="http://notepad-plus-plus.org/">Notepad++</a>, and one feature that jumped out at me was the ability to choose from a large number of pre-installed <a href="http://en.wikipedia.org/wiki/Syntax_highlighting">syntax highlighting</a> themes.</p>
<p>When I switched back to phpDesigner, the default blue-on-white color scheme seemed a tad boring, so I decided it was time to pimp my IDE! Unfortunately user created themes for phpDesigner are thin on the ground, which left me no option but to make my own.</p>
<h4>PHP example:</h4>
<p><img src="http://f6design.com/journal/wp-content/uploads/2010/09/sunset_theme_php.png" alt="Sunset theme for phpDesigner - PHP code" width="450" height="389" /></p>
<h4>HTML example:</h4>
<p><img src="http://f6design.com/journal/wp-content/uploads/2010/09/sunset_theme_html.png" alt="Sunset theme for phpDesigner - HTML markup" width="450" height="164" /></p>
<h4>CSS example:</h4>
<p><img src="http://f6design.com/journal/wp-content/uploads/2010/09/sunset_theme_css.png" alt="Sunset theme for phpDesigner - CSS" width="450" height="224" /></p>
<h4>JavaScript example:</h4>
<p><img src="http://f6design.com/journal/wp-content/uploads/2010/09/sunset_theme_js.png" alt="Sunset theme for phpDesigner - JavaScript code" width="450" height="258" /></p>
<p>I&#8217;ve named my theme Sunset, and it is inspired by the <a href="http://alexgorbatchev.com/SyntaxHighlighter/manual/themes/midnight.html">Midnight theme</a> for <a href="http://alexgorbatchev.com/SyntaxHighlighter/">SyntaxHighlighter</a>. If any phpDesigner fans are looking for a light-on-dark color scheme to spruce up their code editor, perhaps Sunset will fit the bill.</p>
<h3>Download</h3>
<p><a href="http://www.f6design.com/projects/f6-sunset-phpd-theme/f6_sunset_phpd_theme_1.2.rar">Download Sunset theme for phpDesigner</a></p>
<p>Installation and uninstallation instructions are included with the theme. Currently the theme has syntax highlighting colors for the four languages I work with: HTML, CSS, JavaScript and PHP.</p>
<h3>Important</h3>
<p>There is currently a <a href="http://www.facebook.com/topic.php?topic=12989&#038;post=148620&#038;uid=108672675347">bug in phpDesigner</a> that sometimes causes the &#8216;separator&#8217; colors to revert to their defaults (e.g. the color of <code>&lt;?php ?&gt;</code> tags reverts to black). The only sure-fire way to get things back to normal is to open the phpDesigner preferences dialog, and click &#8216;OK&#8217;. You might also try closing all your open documents and restarting the application. Hopefully this bug will be fixed in a future version of phpDesigner.</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2010/09/29/sunset-a-syntax-highlighting-theme-for-phpdesigner/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Ajax form validation using FormBuilder</title>
		<link>http://f6design.com/journal/2007/12/14/ajax-form-validation-using-formbuilder/</link>
		<comments>http://f6design.com/journal/2007/12/14/ajax-form-validation-using-formbuilder/#comments</comments>
		<pubDate>Sat, 15 Dec 2007 00:06:00 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[News & Reviews]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Toolbox]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/2007/12/14/ajax-form-validation-using-formbuilder/</guid>
		<description><![CDATA[Over at roScripts there is a nice tutorial explaining how to modify my FormBuilder PHP class so that validation is performed unobtrusively using AJAX. Check it out.]]></description>
			<content:encoded><![CDATA[<p>Over at roScripts there is a <a href="http://www.roscripts.com/Unobtrusive_Ajax_PHP_form_validation-188.html">nice tutorial</a> explaining how to modify my <a href="http://f6design.com/journal/2007/04/27/formbuilder-html-forms-made-simple/">FormBuilder PHP</a> class so that validation is performed unobtrusively using AJAX. Check it out.</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2007/12/14/ajax-form-validation-using-formbuilder/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>The trouble with content management systems</title>
		<link>http://f6design.com/journal/2007/11/30/the-trouble-with-content-management-systems/</link>
		<comments>http://f6design.com/journal/2007/11/30/the-trouble-with-content-management-systems/#comments</comments>
		<pubDate>Fri, 30 Nov 2007 21:14:13 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[HTML/XHTML]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Web Design]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/2007/11/30/the-trouble-with-content-management-systems/</guid>
		<description><![CDATA[When I started out as a web designer, content management systems belonged strictly to the realm of big budget websites. For everyone else, it was perfectly normal for a web designer to manually update a site whenever a change needed to be made. Clients didn&#8217;t expect a CMS to be included with their website, and [...]]]></description>
			<content:encoded><![CDATA[<p>When I started out as a web designer, content management systems belonged strictly to the realm of big budget websites. For everyone else, it was perfectly normal for a web designer to manually update a site whenever a change needed to be made. Clients didn&#8217;t expect a CMS to be included with their website, and web designers didn&#8217;t offer the option. Times have certainly changed, and in an age of blogs, Facebook, and MySpace, clients expect to be able to take control of their website&#8217;s content.</p>
<p>For most web designers, especially those who work solo, a custom built content management system is still a tall order. Fortunately there are numerous commercial and open source content management systems available, which offer a practical and affordable means of wrangling content. However, a &#8220;one size fits all&#8221; content management system that doesn&#8217;t address a site&#8217;s specific content requirements can introduce as many problems as it solves.</p>
<h3>One page, one blob</h3>
<p>The easier a content management system (CMS) is to configure and use, the more restrictive it is likely to be. HTML and CSS offer a rich toolset for describing and presenting all kinds of content, yet most CMSs cannot be configured to address the myriad types of content a content editor needs to display. Instead, the content editor is given the ability to modify just one &#8220;blob&#8221; of content per webpage. This format suits the majority of webpages, but assumes that the content editor is happy to have just one block of plain text per page, or that they are comfortable styling text with regular HTML tags, or a &#8216;simplified&#8217; variation such as <a href="http://daringfireball.net/projects/markdown/">markdown</a>. For the novice, HTML is hardly child&#8217;s play, and trusting a client to write compliant markup is optimistic to say the least. That&#8217;s where WYSIWYG (What You See Is What You Get, pronounced &#8220;wizzywig&#8221;) editors come to the rescue, and that&#8217;s when problems start to creep in.</p>
<h3>The WYSIWYG problem</h3>
<p>The WYSIWYG editor is at the heart of most modern content management systems, if not by design, then because web designers install one themselves. They are used as a &#8220;cure all&#8221; by CMS developers and web designers alike, because they provide content editors with a powerful interface for styling content, using a toolset that replicates the desktop word processors with which they are already familiar. The two most popular WYSIWG editors are <a href="http://tinymce.moxiecode.com/">TinyMCE</a> and <a href="http://www.fckeditor.net/">FCKEditor</a>. Both use Javascript to enhance a regular HTML textarea, and do so via a menu of buttons similar to that found in Microsoft Word. This probably sounds ideal &#8211; a website&#8217;s content editor can take total ownership of their content, styling it in any way they please. But content editors are precisely that: editors. They are not designers, and shouldn&#8217;t have to &#8211; or be <em>allowed</em> to &#8211; make decisions that affect the design of their content. Sadly, this is exactly the power that WYSIWYG editors put at their fingertips.</p>
<p><img class="contentImg matte" src='http://f6design.com/journal/wp-content/uploads/2007/11/tinymce.jpg' alt='Tinymce' /></p>
<p class="caption">Tinymce in all its glory. Watch your client destroy your layout in mere seconds!</p>
<p>Let loose with a WYSIWYG editor a content editor can mix and match font size, face and color, and even add tables, images, background colors, and emoticons to a webpage. If you think they will have the self control to exercise restraint, think again. Even the ability to alter the appearance of text can have disastrous results. I have seen a content editor use a WYSIWG editor to set body copy in 16 point, 12 point, and 10 point at various places on the same page, and in three different typefaces. Another client inexplicably changed the color of list items to exactly the same blue used throughout the site to indicate hyperlinks. The impact such misguided decisions have on usability and design should be obvious.</p>
<p>Of course the blame doesn&#8217;t lie with the content editor. They don&#8217;t have training as a graphic designer, nor can we expect them  to grasp the subtleties of interactive design. The problem is that WYSIWYG editors mix the <em>description</em> of content (headings, lists, blockquotes) with its <em>design</em> (typefaces, colors, and point sizes). Web designers have long understood the importance of separating content from presentation, but WYSIWYG editors snatch that separation away from us.</p>
<h3>Dial it back</h3>
<p>Fortunately, most WYSIWYG editors allow you to limit their functionality to suit your needs. This can either be done using a CMS&#8217;s administration interface, or by editing a configuration file. When faced with a WYSIWYG editor I dial its functionality right back, leaving only the core set of tools needed to effectively describe content. Does a content editor need to specify background colors for text? Of course not. The size of text? Nope. Paragraph alignment? Uh-uh. Tables? Lets not even go there. I don&#8217;t even let a content editor underline text. Why? Because underlined text looks like a link.</p>
<p>Headings (h1, h2 etc), bold, italic, ordered lists, unordered lists, blockquotes, and hyperlinks: these should provide sufficient scope to describe most types of content. Let your regular CSS stylesheets style the output, and you&#8217;ll ensure the rendered content fits seamlessly with your existing website layout.</p>
<h3>The copy and paste problem</h3>
<p>WYSIWYG editors present another problem I haven&#8217;t touched on yet. Usually a website&#8217;s copy isn&#8217;t typed directly into a content management system, it is composed in a word processor, then pasted into the CMS. When this happens the formatting of the source text is brought across too, warts and all. Despite your best efforts to limit your client&#8217;s ability to style content, with one keystroke they can inadvertently bypass your safeguards. There are two solutions to this problem. The first involves education. Both FCKEditor and TinyMCE include a &#8216;paste as plain text&#8217; button, that gives users the option of stripping all formatting from text as it is pasted into the WYSIWYG editor. The only problem is that your client needs to remember to use the button. The second option is more pro-active. Both FCKEditor and TinyMCE have a configuration setting that forces content to be pasted as plain text.</p>
<h3>What about web standards?</h3>
<p>A criticism often leveled at WYSIWYG editors is that they produce spaghetti code. In part, this is true. The only way a WYSIWYG editor can define the color of text, for instance, is by applying an inline style to your markup. But if you limit the WYSIWYG to a few core capabilities, as I suggested above, there is far less that can go wrong. The developers of WSIWYG editors are mindful of the need for standards compliance, and I have found that in most cases it isn&#8217;t hard to get both TinyMCE and FCKEditor to generate tidy markup.</p>
<h3>Other options</h3>
<p>Building a CMS from scratch is a huge task. I ought to know &#8211; most of my websites are powered by a CMS I built myself. Unless you are a competent programmer and have a heap of time up your sleeve (or just enjoy a challenge!), building your own CMS isn&#8217;t an option I would recommend lightly, but the advantages of the DIY route certainly pay off in the long run.</p>
<p>Building your own CMS gives you absolute control over how the software functions, allowing you to customize it to your own needs with fine grained control. When something breaks, you know how to fix it. If you want to add a new feature to the CMS, you just go ahead and add it, rather than submitting a feature request to the software vendor and crossing your fingers.</p>
<p>Another option to consider is <a href="http://xstandard.com/">XStandard</a>, a WYSIWYG editor that touts its standards compliance and accessibility features. The big drawback I can see with XStandard is that it requires the content editor to install the XStandard application on their local machine. By contrast, a javascript based WYSIWYG editor makes your website editable from any computer sporting a modern web browser.</p>
<p>Whichever approach you take to content management, so long as you give careful thought to both your client&#8217;s needs and the pitfalls of giving them free rein, it is possible to strike a balance that keeps everyone happy. Giving clients the ability to edit their website&#8217;s content, and creating beautiful, standards compliant websites needn&#8217;t be mutually exclusive goals.</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2007/11/30/the-trouble-with-content-management-systems/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>FormBuilder update: send variables by email</title>
		<link>http://f6design.com/journal/2007/07/14/formbuilder-update-send-variables-by-email/</link>
		<comments>http://f6design.com/journal/2007/07/14/formbuilder-update-send-variables-by-email/#comments</comments>
		<pubDate>Sun, 15 Jul 2007 00:54:34 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/2007/07/14/formbuilder-update-send-variables-by-email/</guid>
		<description><![CDATA[I have made another update to my FormBuilder class. Version 1.4 features a new function, emailResults, which can be used to automatically send the user-submitted form variables to any email address. A number of readers asked for this functionality, which eliminates the need to write your own form processing routine. Have fun!]]></description>
			<content:encoded><![CDATA[<p>I have made another update to my <a href="http://f6design.com/journal/2007/04/27/formbuilder-html-forms-made-simple/">FormBuilder</a> class. Version 1.4 features a new function, <code>emailResults</code>, which can be used to automatically send the user-submitted form variables to any email address. A number of readers asked for this functionality, which eliminates the need to write your own form processing routine. Have fun!</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2007/07/14/formbuilder-update-send-variables-by-email/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Securing PHP Contact Forms</title>
		<link>http://f6design.com/journal/2006/12/09/securing-php-contact-forms/</link>
		<comments>http://f6design.com/journal/2006/12/09/securing-php-contact-forms/#comments</comments>
		<pubDate>Sun, 10 Dec 2006 05:59:36 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/2006/12/09/securing-php-contact-forms/</guid>
		<description><![CDATA[One of the great benefits of PHP is that it is quick and easy for non-programmers to learn the basics of the language and begin to add server-side logic to their websites. This simplicity is a double edged sword, as many novice programmers are unaware of PHP&#8217;s security vulnerabilities and inadvertently create web applications that [...]]]></description>
			<content:encoded><![CDATA[<p>One of the great benefits of PHP is that it is quick and easy for non-programmers to learn the basics of the language and begin to add server-side logic to their websites. This simplicity is a double edged sword, as many novice programmers are unaware of PHP&#8217;s security vulnerabilities and inadvertently create web applications that are an easy target for hackers and spammers. Most <a href="http://www.ilovejackdaniels.com/php/writing-secure-php/">PHP security holes</a> are well documented, but a newer and lesser known vulnerability is header injection, a cunning exploit whereby a spammer hijacks a website&#8217;s contact form and uses it to send bulk unsolicited email.</p>
<h3>How they do it</h3>
<p>An email is composed simply of raw text that is formatted using a standardized syntax. When we add a contact form to a website we allow our visitors to supply data such as their name, email address, subject and message, which we then format and send using PHP&#8217;s mail function. For instance:</p>
<pre><code>&lt;?php

  $recipient = 'recipient@mydomain.com';
  $subject = $_POST["subject"];
  $message = $_POST["message"];
  $headers = "From: ".$_POST["email"];
  mail($recipient,$subject,$message,$headers);

?&gt;</code></pre>
<p>will produce the following raw email data:</p>
<pre><code>To: recipient@mydomain.com
Subject: Hello
From: sender@theirdomain.com

Hi, I love your site.</code></pre>
<p>Seems harmless enough, right? We have hard coded the recipient&#8217;s email address in PHP, so it would be natural to assume that a visitor couldn&#8217;t use our contact form to send an email to anyone except the intended recipient.</p>
<p><strong>Wrong!</strong></p>
<p>Because we include the visitor&#8217;s email address in the email header, it is surprisingly simple for a malicious user to insert additional headers in the email field. They can do this in the following manner:</p>
<pre><code>sender@theirdomain.com%0ABcc:recipient@anotherdomain.com</code></pre>
<p>%0A is the hexidecimal representation of a linefeed, and forces a line break in the email&#8217;s header block. Now the raw email will look like this:</p>
<pre><code>To: recipient@mydomain.com
Subject: Hello
From: sender@theirdomain.com
Bcc: recipient@anotherdomain.com

Hi, I love your site.</code></pre>
<p>In this basic example the spammer has simply inserted a single additional recipient. In practice they will use more advanced injection techniques to hide your original message and display their own in its place, and will also insert the email addresses of thousands of hapless recipients. Now your form is being used to send spam.</p>
<p>As the hard coded recipient of the form, the website owner (your client) will become aware that something fishy is going on when they begin to receive large amounts of spam, apparently originating from their own site. Shortly your web host will also notice the high volume of spam emails being sent from your client&#8217;s domain, and shut the site down. They will send you an irate email requesting that your patch your insecure PHP scripts or find another hosting company. Needless to say, this chain of events is bad for your relationship with both your web host and your client!</p>
<h3>The solution</h3>
<p>Protecting against header injection attacks is a simple matter of validating user supplied data. Primarily, header injection relies on the attacker being able to include linebreaks in strings that will appear in an email&#8217;s header. By testing for the presence of these linebreaks in user-supplied data we can stop the spammer in their tracks. I have written a small function to simplify this process &#8211; it accepts as its parameter an array of the header fields we need to test:</p>
<pre><code>&lt;?php

function checkFields($values){
	$injection = false;
	for ($n=0;$n&lt;count($values);$n++){
		if (eregi("%0A",$values[$n]) || eregi("%0D",$values[$n]) || eregi("\\r",$values[$n]) || eregi("\\n",$values[$n])){
			$injection = true;
		}
	}
	return $injection;
}

$from = $_POST['from'];
$email = $_POST['email'];
$result = checkFields(Array($from,$email));
if ($result==true){
	die("Header injection detected!");
}

?&gt;</code></pre>
<h3>How to tell if your form is being exploited</h3>
<p>To test a form to see if it is vulnerable, a spammer will first try and inject a single bcc header containing a throwaway email address (usually an AOL address). They then check that AOL address to see if they received the test email. If so, they will begin using your form as a spam relay.</p>
<p>The only flaw in this otherwise perfect plan is that the email&#8217;s intended recipient (whose email address is hard coded into your PHP form handling script) will also be receiving these probe emails.</p>
<p>So if one of your clients complains they are getting a lot of &#8216;spam&#8217; sent to them via their contact form, sit up and pay attention. There is a chance the &#8216;spam&#8217; they mention is actual a spammer&#8217;s probe emails. Ask your client to send you a few examples of the emails they are receiving. A probe will contain text that looks something like this:</p>
<pre><code>said

Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: an wan that makes me proud iv ailey f
bcc: onemoreaddress@hotpop.com

934522c25fb7a2507489e97c4043af14</code></pre>
<h3>I&#8217;m still getting annoying probe emails</h3>
<p>Even though your form validation may already be filtering header fields for line breaks, you may still receive a spammer&#8217;s probe emails. This is because the spammer &#8211; or more precisely their &#8216;spambot&#8217; &#8211; is attempting to blindly inject into <em>every</em> form field it finds, including the &#8216;message&#8217; field (the probe example I gave above was actually the result of a spammer&#8217;s attempt to inject into the message field of a client&#8217;s contact form). The message field doesn&#8217;t pose a security risk since it does not appear in the email&#8217;s header, but unless you validate it as well then the form&#8217;s intended recipient will continue receiving probe emails. Of course the message field can legitimately contain linebreaks, so you will need to validate it for other telltale strings such as &#8220;Bcc:&#8221; or &#8220;Content-Type:&#8221; both of which are common in injection attacks. Make sure your validation ignores case, since an email header is case insensitive.</p>
<h3>Conclusion</h3>
<p>Validate, validate, validate. And don&#8217;t trust your website visitors, not all of them are good guys!</p>
<h3>Further reading</h3>
<h4><a href="http://www.securephpwiki.com/index.php/Email_Injection">Email Injection</a></h4>
<p>For a really detailed description of email header injection in PHP, check out the Secure PHP wiki page on the topic.</p>
<h4><a href="http://www.anders.com/projects/sysadmin/formPostHijacking/">Form Post Hijacking</a></h4>
<p>When header injection attacks became widespread a couple of my sites were compromised. I found this article useful in helping me understand the problem.</p>
<p><strong>UPDATE 12 Dec 2006:</strong> I realized that I had forgotten to double escape the \ characters in my validation function when I posted this article, so they weren&#8217;t showing up. Whoops! I have updated the function accordingly.</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2006/12/09/securing-php-contact-forms/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Image source swapping, CSS, and Safari</title>
		<link>http://f6design.com/journal/2006/09/29/image-source-swapping-css-and-safari/</link>
		<comments>http://f6design.com/journal/2006/09/29/image-source-swapping-css-and-safari/#comments</comments>
		<pubDate>Fri, 29 Sep 2006 23:56:44 +0000</pubDate>
		<dc:creator>Jonathan</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML/XHTML]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://f6design.com/journal/2006/09/29/image-source-swapping-css-and-safari/</guid>
		<description><![CDATA[Last week I was putting the finishing touches on a small website I created for a friend. Specifically, I was jazzing up the image gallery with an ‘Image loading…’ animation, so that visitors knew to hang around while a new image loaded. In the process I made an interesting discovery about the way Safari (Safari [...]]]></description>
			<content:encoded><![CDATA[<p>Last week I was putting the finishing touches on a small website I created for a friend. Specifically, I was jazzing up the image gallery with an ‘Image loading…’ animation, so that visitors knew to hang around while a new image loaded. In the process I made an interesting discovery about the way Safari (Safari 1.2 at any rate) handles javascript image source swapping.</p>
<p>The image gallery was controlled by a Javascript which swapped the <code>src</code> parameter of a placeholder <code>img</code> tag each time the user selected a new image.</p>
<p>In my HTML markup I had a basic image tag with an ID applied to it:</p>
<pre><code>&lt;img id="placeholderimage" alt="" /&gt;</code></pre>
<p>When the user selected a new image (by clicking ‘next’ or ‘prev’ in my image gallery navigation), a javascript was triggered to load a jpeg into the placeholder:</p>
<pre><code>document.getElementById('placeholderimage').src = "mynewpic.jpg";</code></pre>
<p>The problem with this method is that while the new image loads into the placeholder, the old image remains on the page. To the user it appears as if nothing is happening. The solution is to hide the image while it loads, and show the user some sort of load indicator while they wait. When the image has completed loading, reveal it again and hide the load indicator.</p>
<p>The specifics of hiding/revealing the load indicator are beyond the scope of this article, so I won’t bore you with them here. Instead I’ll demonstrate the method by which I hid and revealed the image, as that is what caused problems in Safari.</p>
<p>To hide the image, I used javascript to apply the CSS style <code>display:none</code> before loading the new jpeg. I also created an <code>onload</code> event handler for my image, to detect when it had finished loading. Inside my <code>onload</code> function I revealed the image again:</p>
<pre><code>document.getElementById('placeholderimage').onload=function(){
        document.getElementById('placeholderimage').style.display = "block";
};
document.getElementById('placeholderimage').style.display = "none";
document.getElementById('placeholderimage').src = "mynewpic.jpg";</code></pre>
<p>Easy peasy. The image is removed from the document flow when it starts to load, and returned when it finishes.</p>
<p>Everything seemed to be rocking nicely, until I fired up the site Safari 1.2. The image was hiding as expected, but my <code>onload</code> function was never evoked. After some head scratching I traced the problem to my use of <code>display:none</code> when hiding the image. I think Safari’s logic is that because the image is no longer part of the document flow, there is no point bothering to load it. Unlike other browsers, Safari never initiated the image load, and that’s why the <code>onload</code> never fired.</p>
<p>Armed with this new knowledge, we have a couple of &#8220;Safari friendly&#8221; options at our displosal:</p>
<ul>
<li>
<h5>Use visibility not display</h5>
<p>If we use <code>visibility = "hidden";</code> instead of <code>display = "none";</code> to hide the image, everything works fine. The problem with using <code>visibility</code> is that the image is not removed from the document flow, meaning a gap is left on the page where the image used to be. Depending on the layout of your image gallery that may not be an issue for you &#8211; it may in fact be desirable. If it is an issue, you can set the height of the image (or it’s containing element if there is one) to zero, then use the onload handler to reset it to it’s correct height. Fiddly, but it works.
</li>
<li>
<h5>Move the image outside the viewport</h5>
<p>If your image has the CSS property <code>position:absolute</code>, then you should be able to move it off screen by setting either it’s <code>top</code> or <code>left</code> property to a suitable low value, for example: <code>left: -1000em</code>; That ought to place the image off screen out of sight.</li>
</ul>
<p>There may be other alternatives, and you can choose the one that suits you best. The important thing to remember is don’t use <code>display:none</code> to hide images while you swap their image source, or risk the wrath of Safari users!</p>
<p>For anyone interested to see the final implementation of my image gallery, <a href="http://www.archivedestruct.com/documentation/pr/images.php">here it is</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://f6design.com/journal/2006/09/29/image-source-swapping-css-and-safari/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

