<?xml version="1.0"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Donat Studios</title>
    <link>http://donatstudios.com/</link>
    <description></description>
    <atom:link rel="self" type="application/rss+xml" href="http://donatstudios.com/feed.rss"/>
    <image>
      <url>http://donatstudios.com/images/site/moustache.png</url>
      <link>http://donatstudios.com/</link>
      <title>Donat Studios</title>
    </image>
    <item>
      <guid>http://donatstudios.com/UNIX-Keyboards</guid>
      <title>The Joys of UNIX Keyboards</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Tue, 18 Jun 2013 12:10:51 CST</pubDate>
      <link>http://donatstudios.com/UNIX-Keyboards</link>
      <description><![CDATA[<h2>I fell in love with a dead keyboard layout.</h2>

<p>A decade or so ago while helping a friends father clean out an old building, we came across an ancient Sun Microsystems server. We found it quite curious as <em>everything</em> about it was different from what we were used to.  The command line was black on white, - the connectors were strange and foreign, and most notably the keyboard layout was bizarre.</p>

<p>We never did much with it; turning it on made all the lights in his home dim, and our joint knowledge of UNIX was limited to say the least.  It sat in his bedroom for years supporting his television at the foot of his bed.</p>

<p>I never forgot that keyboard though. The thought that there was this alternative layout out there seemed really intriguing to me.</p>

<h2>I have been ruined for all other keyboards…</h2>

<p>After starting at my current job where Mac is the main platform, I found myself unhappy with Apple keyboards; they are flat, bland, and completely unsatisfying to type on.  I dug out an old AppleDesign keyboard, and used that for several months but I still needed something more.</p>

<p>I read about Happy Hacking Keyboard, and after some deliberation decided to give it a try. I purchased the <a href="http://donatstudios.com/amzn.com/B0000U1DJ2">Happy Hacking Keyboard Lite2</a> because while I thought I could learn to accept most of the layout changes, the lack of arrow keys on the "Professional" model made me squirm a little.</p>

<p>The Happy Hacking Keyboard or "HHKB", much like the Sun Microsystem keyboard I had found years earlier features what is known as a UNIX layout. It was designed with typing at a terminal in mind.</p>

<h2>A Better Layout</h2>

<p>For those unfamiliar with UNIX keyboards, there are a number of distinctions.</p>

<img src="http://donatstudios.com/assets/43/hhkb.png" alt="Happy Hacking Keyboard" style=" display: block; max-width: 50%;float: right;margin-left: 22px;margin-bottom: 22px">

<ul>
<li><p>The <strong>Control</strong> key is moved to the <strong>Caps Lock</strong> keys usual position. I find this amazingly ergonomic as this is much easier to hit common commands than its usual location, often without ever leaving home row. I can't imagine why anyone thought <strong>Caps Lock</strong> should have had this prominent of placement.</p></li>
<li><p>The <strong>Meta</strong> (<strong>◆ / ⌘ / Windows</strong>) key is in its rightful place next to the space bar, as on a Mac keyboard.</p></li>
<li><p>The <strong>Backspace</strong> key is moved down a row to be directly above the <strong>Enter</strong> key. This change is my absolute favorite.  Having a small reach, my hand would previously have leave home row to hit backspace, but on a UNIX board I have no issue hitting it with my pinky with the rest of my fingers remaining firmly on home row.</p></li>
<li><p><strong>Escape</strong> is also easily in reach of home row, being placed where you would usually find <strong>~</strong>. I could have done without this change, but it is fine.</p></li>
<li><p>The <strong>~</strong> and <strong>\</strong> have been moved to the usual location of the <strong>Backspace</strong>.  Being almost exclusively on UNIX systems I don't mind the <strong>\</strong> being harder to reach, but I could understand a Windows user finding this irritating.</p></li>
</ul>


<p>The HHKB is a minimal version of the UNIX keyboard, and to this end the <strong>F Keys</strong> are mapped to <strong>Fn + 1, 2, 3, etc</strong> I find the HHKB to very strongly encourage you to stay on home row, as you can hit almost any key on the keyboard without leaving it. I also find my typing accuracy and speed to have actually improved thanks to its use.</p>

<p>The Happy Hacking Keyboard features one more change I strongly did not care for. The <strong>Backspace</strong> key by <em>default</em> performs <strong>Forward Delete</strong> but luckily this can be easily changed by flipping a toggle switch on the back of the board.</p>

<h2>Closing</h2>

<p>For <em>anyone</em> who spends a lot of time at the terminal, I highly recommend this keyboard. You are able to do more of what you need without ever fully leaving home row, every important key is easily within reach.</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Beyond-Compare-with-Git-in-OSX</guid>
      <title>Use Beyond Compare as a Git Diff/Merge Tool in OS X</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Tue, 13 Nov 2012 12:07:03 CST</pubDate>
      <link>http://donatstudios.com/Beyond-Compare-with-Git-in-OSX</link>
      <description><![CDATA[<p>I switched from Windows to Mac for work a little over a year ago (I've been using Macs at home for years) and there is one program I still could not live without - Scooter Software's wonderful <a target="_blank" href="http://www.scootersoftware.com/">Beyond Compare</a>.  I've tried every single diff tool available and <strong>none</strong> have lived up to Beyond Compare. It truly lives up to its name.</p>

<p>I've had Beyond Compare running in <a target="_blank" href="http://winebottler.kronenberg.org/">WineBottler</a> for a while now, but was never able to get passing arguments to work correctly.  Today I had a revelation.  On creating the bottle it asks you for arguments to pass the executable, I had not provided any.  Ends up it then passes the first argument as an empty string, which prevented the first argument being a file! Huzzah, an easy fix, pass it an innocuous argument! So here are some directions on getting it set up!</p>

<h2>Beyond Compare Installation / Configuration</h2>

<p>The first step is to <a target="_blank" href="http://winebottler.kronenberg.org/">download</a> WineBottler if you don't already have it. Just drag the <em>WineBottler.app</em> and <em>Wine.app</em> into your <code>/Applications</code> directory. Then <a target="_blank" href="http://www.scootersoftware.com/download.php">download</a> the Windows version of the Beyond Compare Installer and hang onto it.</p>

<p>Launch <em>WineBottler.app</em>. In the left pane choose "Create Custom Prefixes", then for the <strong><em>Install File</em></strong>, choose the Beyond Compare Installer downloaded earlier.  To my knowledge no Winetricks are necessary.  Check the <strong><em>Self-contained</em></strong> checkbox to make a stand alone application.</p>

<p><strong>IMPORTANT</strong>: WineBottler passes a blank argument if you leave the <strong><em>Runtime Arguments</em></strong> blank, which Beyond Compare interprets as the first filename - an annoying bug that took me months to figure out. To get around this we can pass it an innocuous argument (<a href="http://www.scootersoftware.com/help/index.html?command_line_reference.html">see arguments</a>).  I chose <code>/nobackups</code> because I don't want backups and they are turned off by default anyway.</p>

<div style="max-width: 962px;"><img src="http://donatstudios.com/assets/42/winebottler_beyond_compare_settings.png" width="100%"></div>


<p>Click <strong>Install</strong>.  It will ask you to save your App, do so in <code>/Applications</code>. I named mine as <code>BeyondCompare.app</code> and paths later will reflect this.</p>

<p>Follow the installer. After the installer completes Beyond Compare may pop up if you've left the launch checkbox checked.  Just close the window. Choose <strong><code>BCompare.exe</code></strong> when it asks you for the default executable (<code>BComp.exe</code> is for batch scripting).</p>

<h2>Git Configuration</h2>

<p>The paths in the following presume you named your app <code>BeyondCompare.app</code> so these may be adjusted as necessary.</p>

<p>Add the following to your <code>~/.gitconfig</code> file. Remove anything existing that contradicts these settings. I've named the diff and merge tools <code>bc3wb</code> as git already has baked in support for the Windows version, which it calls <code>bc3</code>.</p>

<h3>Beyond Compare 3 Professional</h3>

<pre><code>[diff]
    tool = bc3wb
[difftool "bc3wb"]
    cmd = /Applications/BeyondCompare.app/Contents/MacOS/WineBottlerStarter \"$LOCAL\" \"$PWD/$REMOTE\"
[merge]
    tool = bc3wb
[mergetool "bc3wb"]
    cmd = /Applications/BeyondCompare.app/Contents/MacOS/WineBottlerStarter \"$PWD/$LOCAL\" \"$PWD/$REMOTE\" \"$PWD/$BASE\" \"$PWD/$MERGED\"
    keepBackup = false
    trustExitCode = false
</code></pre>

<h3>Beyond Compare 2 / 3 Standard</h3>

<pre><code>[diff]
    tool = bc3wb
[difftool "bc3wb"]
    cmd = /Applications/BeyondCompare.app/Contents/MacOS/WineBottlerStarter \"$LOCAL\" \"$PWD/$REMOTE\"
</code></pre>

<p><strong>Note</strong>: I am unable to get the mergetool portion working in Beyond Compare 2 or 3 Standard as WineBottler is interpreting <code>/savepath=</code> as a filepath and converting it to <code>z:\savepath=</code> rather than properly passing the arguments.  If anyone has any advice on how to fix this I would be ecstatic.</p>

<p>Voila, now you can <code>git mergetool</code> and <code>git difftool</code> it up!</p>

<div style="max-width: 1229px;"><img src="http://donatstudios.com/assets/42/winebottler_beyond_compare_3way_merge.png" width="100%"></div>
]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/PHP-Static-Nonsense</guid>
      <title>Static::? More like Lies::</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Mon, 22 Oct 2012 12:28:30 CST</pubDate>
      <link>http://donatstudios.com/PHP-Static-Nonsense</link>
      <description><![CDATA[<p>In my professional work, we have a system that utilizes __call and __callStatic for caching of certain method calls. We have been running into a problem where calling an undefined method via <code>::</code> within the class itself will trigger <code>__call</code> rather than the expected <code>__callStatic</code>.</p>

<h2>The Problem</h2>

<p>The following code example illustrates the issue.</p>

<pre><code>&lt;?php

class foo {
    function __call($name, $args){
        echo "__call " . $name . PHP_EOL;
    }

    public static function __callStatic($name, $args) {
        echo "__callStatic " . $name . PHP_EOL;
    }

    public function make_call_self() {
        self::ted();
    }

    public function make_call_static() {
        static::ted();
    }

    function make_call_classname() {
        foo::bob();
    }
}

$x = new foo();
$x-&gt;make_call_self();
$x-&gt;make_call_static();
$x-&gt;make_call_classname();
foo::sam();
</code></pre>

<p>The above returns the following when executed in PHP 5.3+ (excluding PHP 5.3.3, we'll get to this later)</p>

<pre><code>__call ted
__call ted
__call bob
__callStatic sam
</code></pre>

<p><a href="http://3v4l.org/gIP1H">See Example</a></p>

<p>As you can see, all of those ideally should have triggered <code>__callStatic</code> but all save the last <code>foo:::sam()</code> triggered call.  If you click the link to the example execution above, it shows that this did in fact work in PHP 5.3.3 but has since been broken.</p>

<p>I was just about to post this as a bug on PHP.net, but my friend <a href="http://joelclermont.com/">Joel Clermont</a> brought to my attention several cases of this being submitted in the past.  One example being fairly infamous.  Remember how I mentioned it working in 5.3.3? It was actually <em>fixed</em> in that release in response to a bug report. The problem apparently is that it broke a lot of things to do with the at the time new late static binding, and was rolled back in the next release.</p>

<p>From the <a href="https://bugs.php.net/bug.php?id=45159">horses mouth</a>, via colder@php.net</p>

<blockquote><p>You're misunderstanding the meaning of static::foo();</p>

<p>it doesn't mean "call foo statically" but rather "use runtime information to get the class currently called", and do a static(or not) call to the method of that class.</p></blockquote>

<p>So static doesn't mean static apparently.</p>

<h2>Understanding</h2>

<p>After quite a bit of reading and a fair deal of explination by my friend Joel, I came to understand that <code>::</code>, unlike C++, doesn't mean <em>static</em> at all. Rather it is a "<a href="http://php.net/manual/en/language.oop5.paamayim-nekudotayim.php">Scope Resolution Operator</a>" which determines scope from "context" meaning that when called from within an instance of an object, it makes instance calls if possible, rather than static calls. Infact, in 5.3+ you can even do <code>$this::</code>.  The consequence of this is that <code>self::</code> nor <code>static::</code> are necessarily static calls.</p>

<p>This all rubs me wrong; I don't see the benefit to this behavior on the users end.  Perhaps there is some backend optimization this allows.  Even if that were the case I still feel like you could short circuit undefined self:: calls to __callStatic without breaking it.</p>

<h2>Solutions</h2>

<p>There were several "solutions" given, none of which are ideal.</p>

<ul>
<li>Call __callStatic directly.</li>
<li>Use <a href="http://us.php.net/manual/en/function.forward-static-call.php">forward_static_call</a>.</li>
<li>Call the class from within a lambda. (After some testing I see that this does not work in PHP 5.4+)</li>
</ul>


<p>None of these are particularly clean nor ideal. Instead I would like PHP to either add a new call like <code>stat::</code> or simply fix <code>self::</code> and <code>static::</code> to trigger the proper call.  The latter of course would be ideal, especially as <code>static</code> has the word <em>static</em> in its name.</p>

<h2>See</h2>

<ul>
<li>https://bugs.php.net/bug.php?id=62333</li>
<li>https://bugs.php.net/bug.php?id=45159</li>
</ul>
]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Late-Definition-of-PHP-Class-Members</guid>
      <title>Late Definition of PHP Class Members</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Mon, 20 Aug 2012 17:29:13 CST</pubDate>
      <link>http://donatstudios.com/Late-Definition-of-PHP-Class-Members</link>
      <description><![CDATA[<p>Working on a class in our application, I discovered that it was being included before a number of constants it used for its members were even defined.  Puzzled on how this had never been a problem I started to experiment; echoing the member at the bottom of the class file after the class closed showed the expected <code>CONSTANT_NAME</code>.  I was very confused at runtime the value of the member in the constructor and elsewhere in the class appeared correct.</p>

<p>To test what all behaves in this manner I threw together this simple set of three tests including instance members, static members, and class constants.</p>

<pre><code>&lt;?php

class foo {
    public $aaa = TEST_CONSTANT;
}

class bar {
    static $bbb = TEST_CONSTANT;
}

class baz {
    const ccc = TEST_CONSTANT;
}

define('TEST_CONSTANT', "Weird Result\n");

$x = new foo();

echo $x-&gt;aaa;
echo bar::$bbb;
echo baz::ccc;
</code></pre>

<p>Returns the following:</p>

<pre><code>Weird Result
Weird Result
Weird Result
</code></pre>

<p>They all behave in the same manner, indicating that the members and class constants are not defined until the classes are invoked.</p>

<p>This is further demonstrated by the following example:</p>

<pre><code>&lt;?php

class foo {
    public $aaa = TEST_CONSTANT;
}

$x = new foo();
echo $x-&gt;aaa;

define('TEST_CONSTANT', "Weird Result. \n");

$y = new foo();
echo $y-&gt;aaa;
</code></pre>

<p>Which returns:</p>

<pre><code>TEST_CONSTANTTEST_CONSTANT
</code></pre>

<p>So by touching the foo class, its initial value for $aaa has been locked in at what <code>TEST_CONSTANT</code> is at that time.  Some testing indicates this also stands true for static methods and class constants as well.</p>

<p>This is a strange behavior I wanted to draw attention to.  I certainly wouldn&rsquo;t <strong>try</strong> to write something that was dependent on it, although our application currently seems to be.</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Developer-Resum%C3%A9-Interview-Advice</guid>
      <title>Web Developer Resum&#xE9;/Interview Advice</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Thu, 19 Jul 2012 20:20:37 CST</pubDate>
      <link>http://donatstudios.com/Developer-Resum%C3%A9-Interview-Advice</link>
      <description><![CDATA[<p>As Lead Developer at my previous company, I interviewed quite a few Developers. I wanted to provide some advice to Developers on how to come off more positively.</p>

<p>For the purposes of this post <strong><em>I</em></strong>, <strong><em>me</em></strong> and all other pronouns referring to myself refer to a hiring professional.</p>

<h2>Some Basics Up Front</h2>

<p>Google yourself. Google your email address, I will! There will be lots of Googling before you&rsquo;re hired. I am well versed in the darker Google arts, and odds are I will find you. If I don&rsquo;t find you, that&rsquo;s almost as detrimental as finding something fairly negative; you are interviewing for a Web related job, but don&rsquo;t have a presence on the Web?</p>

<p>I will look at your Facebook / Twitter if they&rsquo;re open to the public.  This can actually be a very good thing if your posts are nerdy.</p>

<p>Try not to have the top result for your name be the police report for the night you spent in jail.  This is strangely common in my experience. Demonstrate some SEO skills, buy yourname.name! Do something to get that big red mark off the first page.</p>

<p>One of the most important things I can recommend is having a website.  Not only will it ideally outrank your jail time on Google presuming your name&rsquo;s all over it, but ideally it&rsquo;s a technology website where you talk about experiences and things you&rsquo;ve learned you want to share with others. This helps support your resume and your interview.  This is very important, it brings substance to the game.  It shows your interest in technology goes beyond making money.  Also, lots of bonus points for not using Wordpress, I know it&rsquo;s tempting, but build something your self or use something more exotic.  Any slob with a 56k can throw together a Wordpress site.  Be sure to list it on your resume. I don&rsquo;t care how bad it is, fix it up, that&rsquo;s no excuse. If you have your name, or your email address or anything else I can surmise about you, I will find it. If I find it, I&rsquo;ll presume you were hiding it from me - and that doesn&rsquo;t come off well.</p>

<p><strong>Have extracurricular code samples.</strong>  Using <em>Github</em> or other similar service, and more importantly including a link on your resume not only shows a willingness to stand by your code, but show a versed knowledge of SCM. If you don&rsquo;t build things in your own time, you&rsquo;re not learning as much as you could be.</p>

<p>The best thing I can recommend is writing your own framework.  All the best developers I know have at one time or another created their own framework. It is an amazing learning experience for you, and moreover it gives a huge amount of insight into the way you <em>think</em>, and the way you <em>prefer</em> to code</p>

<h2>Resume Basics</h2>

<p>Your resume exists to spark a conversation; it shouldn&rsquo;t be huge and it doesn&rsquo;t need every minute detail about every project you&rsquo;ve worked on. If it goes over four pages odds are I&rsquo;m not reading it all.</p>

<p>Leaving pieces vague helps me ask questions, which can help spark a conversation.  Often the most important bits are in questions <em>I didn&rsquo;t ask</em>.</p>

<p>Be painfully consistent. Spelling/casing the same technology multiple ways reflects poorly on you. If you can&rsquo;t be consistent in your resume, what is to say you&rsquo;d be consistent in your work?</p>

<p>Have the same name, address, email, and phone number on <strong>every</strong> document. Not only is inconsistency here really awkward when trying to contact you, but it helps me keep my notes on you organized. I may be interviewing several people that day and often these documents will be stacked on my desk between interviews.</p>

<p>Mention things you&rsquo;ve built from the ground up. While framework knowledge is useful in places looking for an expert in a specific framework, ground up work demonstrates a broader depth of knowledge.</p>

<p>Don&rsquo;t send your resume as a .txt file.  It&rsquo;s not cool.</p>

<h2>Interview Basics</h2>

<p>Always be on time, especially for a phone interview. Gathering people to make a call just for you to not pick up is infuriating.</p>

<p>When you are asked a question, elaborate! Go off on small tangents about why some piece worked or didn&rsquo;t.  Smile devilishly and mention how something saved the day!  That said, don&rsquo;t ramble, there is a fine balance.  Don&rsquo;t spend minutes answering what should have been a several second answer.</p>

<p>Bring samples of work not included on your resumé. These are incredible talking points.</p>

<ul>
<li>Be ready to identify what specific pieces you worked on.</li>
<li>Don&rsquo;t include very similar examples.</li>
</ul>


<p>Ahead of time consider interesting challenges faced working on each, you will be asked. If the answer is none, it&rsquo;s probably not worth including.</p>

<h2>Other Important Bits</h2>

<p>Try to not be nervous. Easier said than done, but <em>you don&rsquo;t need this job</em>, or at least act like you don&rsquo;t. That&rsquo;s not to say <em>be cocky</em>  - just that showing neediness as with a personal relationship, can scare people away.</p>

<p>Experience is far more important than education. Talk less about what you&rsquo;ve done in school and more about what you&rsquo;ve done professionally or even better on your own.</p>

<p>Don&rsquo;t drone on about wanting to work from home or needing quiet.  Every developer is a bit of an introvert, but going on about it indicates problems working with others to come.</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/PixelCircleGenerator</guid>
      <title>Pixel Circle / Oval Generator</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Wed, 20 Feb 2013 14:22:30 CST</pubDate>
      <link>http://donatstudios.com/PixelCircleGenerator</link>
      <description><![CDATA[<fieldset style="float: right; margin-left: 11px; text-align: center"><legend>Find this useful?</legend>Buy me a beer! <hr /> <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick" style>
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHLwYJKoZIhvcNAQcEoIIHIDCCBxwCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYAy4BMQR+gXgzT+VlMBgLYMmHmJUzkedAnz3pQKsB0ZJPBkPs8UGmCDeD3fwMMAEiMxqanNw+fX5XtRJp18ONZmM6NLXPy9MtWM5e4W4ZaaKwvYhg+BqjGec1IE0yuKHvwrb9uBIpYnYrhj9Ar4TKo+y3jDj0Jyeyb8F4MUu3X8BDELMAkGBSsOAwIaBQAwgawGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIRndc+ju3hn+AgYgXZRvvb1qCbLB5lLTjPDeue/6MBVENfs1z2jAUSFA69Ppvo3gkF9PKu0q+iwcIxSiKA3eJMVP6m4cayd1UFLSgN9rHbisgz3RRRqMMZXIoP7oZ45SXM+xl6TUKcGc7U14wRNuj5o1K8HqC60UZaHr37tiiubp/ngw9i5l73kHBoqajlS3yS0bioIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTMwMjE1MDIwNjQzWjAjBgkqhkiG9w0BCQQxFgQU6pFsKsltm2oPpMCSwqPuFB2OShEwDQYJKoZIhvcNAQEBBQAEgYAXkXscShHz6Q+ckNrEnCQGB5z24Y9I2aSGHS9V3jHNbD+fHyUiGor0X2D9DmlJMi3ksBgJIVnTnv/+0ju2yN8WX3178bsoK+yliHYPRQpi2fANfVGLgmr2A33w06o2xi2DLxjL10JTWpxVSJRKQ64h/neP8tdwym8PumZwB0rB8w==-----END PKCS7-----
">
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" style="border: none; padding: 0">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
</fieldset>
<p>When playing Minecraft I like making circular things.  I used to use a chart while I was building, but wanted to be able to make variable size ovals which is something I couldn't find a decent chart of or generator capable of, so I created this!</p>
<p><strong>February 18th, 2013 - Update:</strong> Fixed a problem with generator not working in Internet Explorer 9.</p>
<fieldset>
	 
	<center><small><a href="https://github.com/donatj/Circle-Generator" target="_blank">Fork my source on Github!</a></small></center>
</fieldset>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Simple-Single-Pass-Doubly-Linked-Flat-Tree-Building-Algorithm</guid>
      <title>Simple Single Pass Doubly Linked Flat Tree Building Algorithm</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Fri, 06 Jan 2012 15:55:47 CST</pubDate>
      <link>http://donatstudios.com/Simple-Single-Pass-Doubly-Linked-Flat-Tree-Building-Algorithm</link>
      <description><![CDATA[<p>A hierarchy is a common structure, especially in web development.&nbsp; The problem with hierarchies is they need to be flattened to be stored in a relational database, and then expanded again after being pulled out.&nbsp; </p>
<fieldset><legend>An Example Hierarchy</legend>
<ul>
  <li>Top level item</li>
  <ul>
    <li>Sub Item</li>
    <ul>
      <li>Sub Sub Item</li>
      <ul>
        <li>Sub Sub Sub Item</li>
      </ul>
    </ul>
    <li>Sibling of Sub Item</li>
  </ul>
  <li>Top level as well</li>
</ul>
</fieldset>
<p>&nbsp;</p>
<p>A common and simple way to store them is with a table structure similar to the following.</p>
<table border="1" cellspacing="0" cellpadding="0">
  <tr>
    <td width="77" valign="top"><p>id</p></td>
    <td width="163" valign="top"><p>parent_id</p></td>
    <td width="161" valign="top"><p>title</p></td>
  </tr>
  <tr>
    <td width="77" valign="top"><p>1</p></td>
    <td width="63" valign="top"><p>0</p></td>
    <td width="161" valign="top"><p>Top level item</p></td>
  </tr>
  <tr>
    <td width="77" valign="top"><p>2</p></td>
    <td width="63" valign="top"><p>0</p></td>
    <td width="161" valign="top"><p>Top level as well</p></td>
  </tr>
  <tr>
    <td width="77" valign="top"><p>3</p></td>
    <td width="63" valign="top"><p>1</p></td>
    <td width="161" valign="top"><p>Sub Item</p></td>
  </tr>
  <tr>
    <td width="77" valign="top"><p>4</p></td>
    <td width="63" valign="top"><p>1</p></td>
    <td width="161" valign="top"><p>Sibling of Sub Item</p></td>
  </tr>
  <tr>
    <td width="77" valign="top"><p>5</p></td>
    <td width="63" valign="top"><p>3</p></td>
    <td width="161" valign="top"><p>Sub Sub Item</p></td>
  </tr>
  <tr>
    <td width="77" valign="top"><p>6</p></td>
    <td width="63" valign="top"><p>6</p></td>
    <td width="161" valign="top"><p>Sub Sub Sub Item</p></td>
  </tr>
</table>
<p>Now there are several ways to return this to a multidimensional array or other navigable structure. A painfully common and naïve way would be recursive queries ala:</p>
<pre>
function fetch_items($parent = 0) {
	$data = array();
	$qry = mysql_query(&quot;select * from `tablename`&nbsp; where parent_id = &quot; . (int)$parent );
	while( $row = mysql_fetch_array($qry) ) {
		$data = array('data' =&gt; $row, 'children' =&gt; fetch_items($row['id']));
	}
	return $data;
}
  </pre>
<p>The problem with this approach is the sheer number of queries this approach uses. Our simple example hierarchy would execute 6 queries, whereas more complicated hierarchies would execute many more.</p>
<p>A better approach is to pull all the data out with a single query, and then process it into the structure we need.&nbsp; There are several ways to do this, but I find the following DOM like structure this generates incredibly useful.</p>
<pre>
function fetch_tree() {
	$data = array();
	$qrt&nbsp; = mysql_query(&quot;select * from `tablename` order by parent_id asc&quot;);
	while( $row = mysql_fetch_array($qry) ) {
		$data[ $row['id'] ]['data'] = $row;
		$data[ $row['parent_id'] ]['children'][ $row['id'] ] =&amp; $data[ $row['id'] ];
  		$data[ $row['id'] ]['parent'] =&amp; $data[ $row['parent_id'] ];
 	}
	return $data;
}
  </pre>
<p>Essentially, in a <strong>single pass</strong> through the data, this gets us an array of hierarchy elements, where rather than an element directly having its children, every entry has a pointer to its parent element as well as an array of pointers to its children. This is incredibly flexible, and as the tree is flattened it allows you to start at any point in the hierarchy by ID ala $data[<em>id</em>], and navigate your way up or down. Viewing any elements children is as simple as $data[<em>id</em>][ 'children'], and its parent as simple as $data[<em>id</em>]['parent'].</p>
<p>I use this basic methodology a ton in my work, personal and professional (the comments and sitemap of this site are powered by it!) but the concept seems foreign when I try to explain it to people. &nbsp;I hope this explanation sparks some ideas for people.</p>
]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Damn-Simple-PHP-ASCII-Art-Generator</guid>
      <title>Damn Simple PHP ASCII Art Generator</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Thu, 10 Nov 2011 07:18:43 CST</pubDate>
      <link>http://donatstudios.com/Damn-Simple-PHP-ASCII-Art-Generator</link>
      <description><![CDATA[<p>So yesterday I was bored and asked my friend what I should do.  She replied "I don't know, draw ASCII art or something!" and sitting there with the terminal open I kind of wondered what it would take to write one of those image to ASCII scripts.</p>
<p>20 minutes later I had the following PHP Shell Script! It works by simply sampling the saturation of every X<i>th</i> pixel and mapping that saturation to a character from an array. There is no nearest-neighboring or anything like that, just straight up sampling.</p>
 
<h2>Huzzah!</h2>
<a href="http://donatstudios.com/assets/36/AsciiJorie.png" rel="lightbox"><img src="http://donatstudios.com/assets/36/AsciiJorie.png" alt="ASCII JORIE is made of ASCII" style="margin: auto; display: block; width: 44%; float: right;" /></a>
<p>And there you have it! A whopping 35 lines of code. There is a ton of tweaking you can do like changing the array of characters, or adjusting the $scale variable which is simply how wide of a sample it makes.  Setting it to 1 for instance should sample every pixel on the Y and every other pixel on the X (so it scales propperly). To the right is a sample output of the script.</p>
<h2>Do You Trust Me?</h2>
<p>The script accepts local or remote image paths as the first argument.  The following is a simple usage example you can use to get you started.</p>
<pre class="terminal">./ascii.php "http://donatstudios.com/images/site/JesseDonat.jpg"</pre>
<p>It was a fun little project and I look forward to seeing if/what the internet comes up with for it. <a href="https://gist.github.com/1353237">Fork me on Github</a>, or post links in the comments!  Happy hacking!</p>
<br clear="all" />]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Lion-Photobooth-Repair</guid>
      <title>Mac OS X Lion / Mountain Lion Photo Booth Plist Rebuild/Repair Script</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Mon, 10 Sep 2012 16:41:30 CST</pubDate>
      <link>http://donatstudios.com/Lion-Photobooth-Repair</link>
      <description><![CDATA[<p>A while ago I upgraded from an aging MacBook Pro to a much more nubile MacBook Air. Delighted with the Air in every way, the only problem I encountered was the backup of the MacBook Pro I made I sold it was seemingly corrupted and would not mount.</p>

<p>I did though have a backup of my Photo Booth Photos and wanted them recognized by the application. Admittedly, it is simply a matter of entering them into the Recents.plist file, but having several hundred photos across three operating systems and three naming schemes (was that <em>really</em> necessary, Apple?) getting these into the proper order by hand would have been a huge burden.</p>

<p>Alas! A use for my PHP skills! I sat down and wrote a simple PHP based shell script you can use to create a new plist file with your images properly ordered.</p>

<p>This will back up your current Recents.plist as Recents.plist.bk.[time] so if you decide to roll it back, it will be there for you!</p>

<h2>Usage</h2>

<h3>To Begin With</h3>

<ul>
<li>Ensure all photos and movies you wanted loaded into Photo Booth are located in your Pictures/Photo Booth Library/Pictures/ folder.</li>
<li>Ensure the Photo Booth Application is <strong>closed</strong> to avoid overwriting our changes.</li>
</ul>


<h3>Simple Method</h3>

<p><small>Added September 10, 2012</small><br/>
Open Terminal and run the following command.</p>

<pre class="terminal">curl -Ls https://raw.github.com/gist/1108691/Photobooth_rebuild.sh.php | php</pre>


<h3>Git Method</h3>

<p>Simply executing <pre class="terminal">git clone https://gist.github.com/1108691.git PhotoBoothRepair</pre> should both download it and set it executable.</p>


<h3>Non-Git Method</h3>

<ul>
<li><a href="https://raw.github.com/gist/1108691/Photobooth_rebuild.sh.php">Save the script</a> somewhere accessible to you via Terminal.</li>
<li>Navigate in Terminal to the directory where you saved the shell script</li>
<li>Set the file executable by running <pre class="terminal">chmod +x Photobooth_rebuild.sh.php</pre></li>
<li>Execute the shell script <pre class="terminal">./Photobooth_rebuild.sh.php</pre></li></li>
<li>All Done - Fire up Photo Booth to see our results!</li>
</ul></li>
</ul>


<h2>Known Limitations</h2>

<ul>
<li>All Leopard format names arrive first simply for lack of anything to go on as they&rsquo;re just numbered.</li>
</ul>


<!-- script src="https://gist.github.com/1108691.js?file=Photobooth_rebuild.sh.php"></script -->




<p>Please comment, I welcome feedback!  Feel free to fork this code on Github - I'm open to any updates. I wrote it for my personal use but have been very pleased with the number of people that it has helped!</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/PhpED-Dark-Theme</guid>
      <title>Nusphere PhpED Dark Theme</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Thu, 07 Jul 2011 16:34:57 CST</pubDate>
      <link>http://donatstudios.com/PhpED-Dark-Theme</link>
      <description><![CDATA[<p>It would seem there is a fair deal of call for an updated version of my PhpED dark theme from colleagues as well as random folks on the internet who’ve seen screenshots of my PhpED installation.  I previously had my Dark Theme for 5 linked on my Review of PhpED, but I figured I could have a page dedicated to it.</p>

<fieldset><legend>Screenshots</legend>
<a href="http://donatstudios.com/assets/34/phped_php.png" rel="lightbox"><img src="http://donatstudios.com/assets/34/phped_php.png" alt="PhpED Dark Theme PHP" style="margin: auto; display: block; width: 30%; float: left;" /></a>
<a href="http://donatstudios.com/assets/34/phped_css.png" rel="lightbox"><img src="http://donatstudios.com/assets/34/phped_css.png" alt="PhpED Dark Theme CSS"  style="margin: auto; display: block; width: 30%; float: right;" /></a>
<a href="http://donatstudios.com/assets/34/phped_html.png" rel="lightbox"><img src="http://donatstudios.com/assets/34/phped_html.png" alt="PhpED Dark Theme HTML" style="margin: auto; display: block; width: 30%; margin: 0 auto;" /></a>
</fieldset>
<h2>Download and Installation</h2>
<p>The first step is to download the proper version.  <br />Click here to download the <a href="https://raw.github.com/gist/734872/57bea8d954110b7849bcea25851495d6a3f4ffa3/hl.cfg" target="_blank">PhpED 6 Version</a> or here to download the <a href="https://raw.github.com/gist/734872/69fc228a0c174f10a2aa571b0532113cdaa9bfeb/hl.cfg"  target="_blank">PhpED 5 Version</a>.</p>
<p>After you have downloaded the file you will need to replace your hl.cfg. </p><p>In Windows 7 or Vista this should be located in or around: <pre>C:\Users\<strong>YOUR_USER_NAME</strong>\AppData\[<em>Roaming</em> / <em>Local</em>]\NuSphere\PhpED\config</pre>
<br />Likewise in XP it should be located in something like: <pre>C:\Documents and Settings\<strong>YOUR_USER_NAME</strong>\Application Data\NuSphere\PhpED\config</pre>
</p><p>The final step is to simply change your background color to a dark color.  The background color is oddly saved in a different configuration file than the highlighting. This can be found in <strong>Tools</strong> &rArr; <strong>Settings</strong> &rArr; <strong>Editor</strong> &rArr; <strong>Appearance</strong></p>
<p>While you're in there you may want to tweak the settings some to your taste, but that is all there is to it.</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/MPO-to-JPEG-Stereo</guid>
      <title>MPO to Anaglyph/Stereo-JPEG</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Thu, 12 May 2011 23:44:27 CST</pubDate>
      <link>http://donatstudios.com/MPO-to-JPEG-Stereo</link>
      <description><![CDATA[<fieldset style="float: left; width: 52%; margin-bottom: 22px; margin-right: 22px;"><legend>Try it out!</legend>
	 
</fieldset>

<p style="clear:right;"><strong>Update May 12th, 2011</strong> Anaglyph ability added! I don't believe Red/Green is correct but I have a pair of glasses on the way to test it out.</p>
<p>Convert MPO files like those generated by the Nintendo 3DS into Stereoscopic JPEG Images or Anaglyph like you would use 3D glasses for.</p>
<p>Examples and more info to come!</p>
<p>While you're here, feel free to download the source below.   Source to anaglyph will be available soon after some slight refactoring.</p>

<fieldset style="margin-bottom: 22px; clear: both;"><legend>Gallery</legend>
<br />
<b>Warning</b>:  include(assets/0/gallery.php): failed to open stream: No such file or directory in <b>/var/www/donatstudios.com/public/corpus/modules/IncludeAsset.php</b> on line <b>8</b><br />
<br />
<b>Warning</b>:  include(): Failed opening 'assets/0/gallery.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in <b>/var/www/donatstudios.com/public/corpus/modules/IncludeAsset.php</b> on line <b>8</b><br />

</fieldset>

<table class="datatable" style="width: 100%" cellpadding="0" cellspacing="0">
<tr><th align="left">Build</th><th align="left">Date</th><th align="left">Message</th></tr><tr class="odd"><td align="left">0.1<br /><small><a href="http://github.com/donatj/PHP-JPGMPO-to-Stereo-JPEG/zipball/0.1" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2011-04-13T13:46:44-05:00" datetime="2011-04-13T13:46:44-05:00">Apr. 13, 2011</time></td><td align="left"><small>First Alpha<br />
</small></td></tr></table>
]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/WPF-VBNet-JumpList-Example</guid>
      <title>Simple WPF VB.Net JumpList Example</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Wed, 06 Apr 2011 23:00:31 CST</pubDate>
      <link>http://donatstudios.com/WPF-VBNet-JumpList-Example</link>
      <description><![CDATA[<p>Today I decided to spruce up a VB.Net application I wrote in
	college and still use almost daily. In the process of converting it
	from .Net 2 to .Net 4 I wanted to add a splash of Windows 7 goodness; there were some
	very simple operations I wanted to add to JumpLists.  The problem I ran into with all the examples I
	could find is that they either called entirely different applications (the Microsoft
	examples all called <i>Notepad</i>) or
	the example was in C#.  The
	application is in VB.Net and I intend to keep it that way, as it is my .Net
	language of choice.</p>
<p>The interesting problem with JumpLists is they are just
	shortcuts.  They call an executable with
	the specified arguments. Microsoft’s reasoning for this is that they are still
	available while the program is no longer running. The initial <i>execution</i> of an application placed in
	the taskbar registers the JumpLists, after which point they are cached by the
	system and continue to be available even when the application is no longer
	running. This potentially limits the usefulness of JumpLists as they do start a
	new instance of your application.  If
	anyone knows how to catch the arguments on a single instance application, I
	would be <i>very</i> interested to hear
	it.  The best I’ve come up with is simply
	passing messages to my current instance from a new instance and then
	terminating the second instance, but this is awkward to my eye.</p>
<p>XAML can be used to create JumpLists in the Application.xaml
	file, which many other examples will demonstrate – but the issue here is there
	is no way to get the path to the current executable via XAML, so you can only
	call <i>other</i> executables whom you know
	the full path to.</p>
<p>The answer then is to create our JumpLists dynamically.  The best place to do this is in your
	Application.xaml.vb file.  The New method
	will be used to instantiate and register the JumpLists, whereas the Startup
	event will be used to handle arguments passed to the new instance of our
	application.</p>
<p>Below is a very simple example of JumpLists not too far off from what I ended up going with in my own application.  Feel free to leave me comments as to any improvements or changes you would make, or go ahead and fork the gist.</p>

 ]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT</guid>
      <title>PHP User Agent Parser</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Tue, 29 Mar 2011 16:51:09 CST</pubDate>
      <link>http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT</link>
      <description><![CDATA[<fieldset><legend>Demo</legend>
<br />
<b>Warning</b>:  include(assets/0/index.php): failed to open stream: No such file or directory in <b>/var/www/donatstudios.com/public/corpus/modules/IncludeAsset.php</b> on line <b>8</b><br />
<br />
<b>Warning</b>:  include(): Failed opening 'assets/0/index.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in <b>/var/www/donatstudios.com/public/corpus/modules/IncludeAsset.php</b> on line <b>8</b><br />

</fieldset>

<br clear="all"/>
<div class="GithubFeed"><h2>Recent Activity</h2><h3><time class="entryDate" title="2013-05-28T17:25:55-05:00" datetime="2013-05-28T17:25:55-05:00">May. 28, 2013</time></h3><h4>Jesse Donat <small>(<a href="https://github.com/donatj" target="_blank">donatj</a>)</small></h4>&quot;Opera Next&quot; support added<hr />Missing \<hr />Playbook support added<hr />phpunit.xml added<hr /><h3><time class="entryDate" title="2013-05-20T16:12:07-05:00" datetime="2013-05-20T16:12:07-05:00">May. 20, 2013</time></h3>Unified casing of PLAYSTATION and Playstation to PlayStation<hr />Fixed a problem with the Lynx version0<hr />Test added for PSP Vita<hr />Support for Playstation Vita added<hr />composer.json added<hr /><h3><time class="entryDate" title="2013-05-10T14:21:45-05:00" datetime="2013-05-10T14:21:45-05:00">May. 10, 2013</time></h3>Updated readme to include build status<hr />.travis.yml added<hr />Tests converted to PhpUnit<hr /><h3><time class="entryDate" title="2013-04-15T20:08:44-05:00" datetime="2013-04-15T20:08:44-05:00">Apr. 15, 2013</time></h3>Merge branch 'master' of https://github.com/donatj/PhpUserAgent<hr />Used a better variable name.<hr />PHPDoc made more usual<hr /><h3><time class="entryDate" title="2013-01-14T13:51:55-06:00" datetime="2013-01-14T13:51:55-06:00">Jan. 14, 2013</time></h3>Update README.md<br />
<br />
BalckBerry =&gt; BlackBerry<hr /><h3><time class="entryDate" title="2013-01-10T16:11:51-06:00" datetime="2013-01-10T16:11:51-06:00">Jan. 10, 2013</time></h3>3DS support added<hr />WiiU support added<hr /><h3><time class="entryDate" title="2013-01-06T20:57:41-06:00" datetime="2013-01-06T20:57:41-06:00">Jan. 6, 2013</time></h3>Very happy license added<hr /><h3><time class="entryDate" title="2012-11-27T16:37:16-06:00" datetime="2012-11-27T16:37:16-06:00">Nov. 27, 2012</time></h3>Cleaned up merged code<hr />Merge pull request #8 from atomantic/Fix_For_CLI_Unit_Tests_And_Non_UI_Requests<br />
<br />
Unit Test Support<hr /><h4>Adam Eivy <small>(<a href="https://github.com/atomantic" target="_blank">atomantic</a>)</small></h4>now this method can be safely invoked by a command-line or server-side interaction without erroring out<hr /><h3><time class="entryDate" title="2012-10-22T13:08:53-05:00" datetime="2012-10-22T13:08:53-05:00">Oct. 22, 2012</time></h3><h4>Jesse Donat <small>(<a href="https://github.com/donatj" target="_blank">donatj</a>)</small></h4>Readme Updated<hr />Support for Chrome OS for Chromebooks added<hr />Another Kindle Fire Test Case Added<hr /><h3><time class="entryDate" title="2012-09-07T12:06:16-05:00" datetime="2012-09-07T12:06:16-05:00">Sep. 7, 2012</time></h3>Test style fixup<hr />Kindle Fire support fixed up<hr />CSS Cleanup<hr />Commented regex did not match preg<hr />slight logic cleanup<hr /></div>

<p>There are many User Agent string parsers out there, many PHP
	ones infact, but none of what I tested were able to do <i>all</i> of the following.</p>
<ol>
	<li>Correctly identify certain versions of IE7 and
	above that included a user string mentioning IE6 as follows
		<ol>
			<li><pre style="width: 389px">Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1))</li>
		</ol>
	</li>
	<li>Correctly identify various mobile platforms,
				Android and Windows Phone 7 in particular.</li>
	<li>Operates simply without large class structures.</li>
</ol>
<p>This is why I set about building my own.  I don’t guarantee it to be perfect, but it
	works for my needs. I <b>strongly</b> welcome <a href="https://github.com/donatj/PhpUserAgent" target="_blank">forking</a> and pull requests through <a href="https://github.com/donatj/PhpUserAgent" target="_blank">Github</a> for
	any changes.  </p>
<p> Comments are more than welcome below.</p>
<p>Known Limitations</p>
<ul>
	<li>Does not detect Ice Weasel as Firefox</li>
	<li>Probably many others not coming to mind right now.</li>
</ul>
<p>View the test suite of User Agents I'm currently running it against <a href="http://donatstudios.com/assets/30/PhpUserAgent/" target="_blank">here</a>.</p>

<div class="GithubFile"><pre><code><span style="color: #000000">
<span style="color: #0000BB">&lt;?php<br /><br /></span><span style="color: #FF8000">/**<br />&nbsp;*&nbsp;Parses&nbsp;a&nbsp;user&nbsp;agent&nbsp;string&nbsp;into&nbsp;its&nbsp;important&nbsp;parts<br />&nbsp;*&nbsp;<br />&nbsp;*&nbsp;@author&nbsp;Jesse&nbsp;G.&nbsp;Donat&nbsp;&lt;donatj@gmail.com&gt;<br />&nbsp;*&nbsp;@link&nbsp;https://github.com/donatj/PhpUserAgent<br />&nbsp;*&nbsp;@link&nbsp;http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT<br />&nbsp;*&nbsp;@param&nbsp;string&nbsp;$u_agent<br />&nbsp;*&nbsp;@return&nbsp;array&nbsp;an&nbsp;array&nbsp;with&nbsp;browser,&nbsp;version&nbsp;and&nbsp;platform&nbsp;keys<br />&nbsp;*/<br /></span><span style="color: #007700">function&nbsp;</span><span style="color: #0000BB">parse_user_agent</span><span style="color: #007700">(&nbsp;</span><span style="color: #0000BB">$u_agent&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">null&nbsp;</span><span style="color: #007700">)&nbsp;{&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;if(</span><span style="color: #0000BB">is_null</span><span style="color: #007700">(</span><span style="color: #0000BB">$u_agent</span><span style="color: #007700">)&nbsp;&amp;&amp;&nbsp;isset(</span><span style="color: #0000BB">$_SERVER</span><span style="color: #007700">[</span><span style="color: #DD0000">'HTTP_USER_AGENT'</span><span style="color: #007700">]))&nbsp;</span><span style="color: #0000BB">$u_agent&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">$_SERVER</span><span style="color: #007700">[</span><span style="color: #DD0000">'HTTP_USER_AGENT'</span><span style="color: #007700">];<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data&nbsp;</span><span style="color: #007700">=&nbsp;array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #DD0000">'platform'&nbsp;</span><span style="color: #007700">=&gt;&nbsp;</span><span style="color: #0000BB">null</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #DD0000">'browser'&nbsp;&nbsp;</span><span style="color: #007700">=&gt;&nbsp;</span><span style="color: #0000BB">null</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #DD0000">'version'&nbsp;&nbsp;</span><span style="color: #007700">=&gt;&nbsp;</span><span style="color: #0000BB">null</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;);<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;if(!</span><span style="color: #0000BB">$u_agent</span><span style="color: #007700">)&nbsp;return&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;</span><span style="color: #0000BB">preg_match</span><span style="color: #007700">(</span><span style="color: #DD0000">'/\((.*?)\)/im'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$u_agent</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$parent_matches</span><span style="color: #007700">)&nbsp;)&nbsp;{<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">preg_match_all</span><span style="color: #007700">(</span><span style="color: #DD0000">'/(?P&lt;platform&gt;Android|CrOS|iPhone|iPad|Linux|Macintosh|Windows(\&nbsp;Phone\&nbsp;OS)?|Silk|linux-gnu|BlackBerry|PlayBook|Nintendo\&nbsp;(WiiU?|3DS)|Xbox)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?:\&nbsp;[^;]*)?<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?:;|$)/imx'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$parent_matches</span><span style="color: #007700">[</span><span style="color: #0000BB">1</span><span style="color: #007700">],&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">PREG_PATTERN_ORDER</span><span style="color: #007700">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$priority&nbsp;</span><span style="color: #007700">=&nbsp;array(</span><span style="color: #DD0000">'Android'</span><span style="color: #007700">,&nbsp;</span><span style="color: #DD0000">'Xbox'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">array_unique</span><span style="color: #007700">(</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;</span><span style="color: #0000BB">count</span><span style="color: #007700">(</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">])&nbsp;&gt;&nbsp;</span><span style="color: #0000BB">1&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;</span><span style="color: #0000BB">$keys&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_intersect</span><span style="color: #007700">(</span><span style="color: #0000BB">$priority</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">])&nbsp;)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">reset</span><span style="color: #007700">(</span><span style="color: #0000BB">$keys</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}else{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">][</span><span style="color: #0000BB">0</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}elseif(isset(</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">][</span><span style="color: #0000BB">0</span><span style="color: #007700">])){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">][</span><span style="color: #0000BB">0</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'linux-gnu'&nbsp;</span><span style="color: #007700">)&nbsp;{&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'Linux'</span><span style="color: #007700">;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'CrOS'&nbsp;</span><span style="color: #007700">)&nbsp;{&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'Chrome&nbsp;OS'</span><span style="color: #007700">;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">preg_match_all</span><span style="color: #007700">(</span><span style="color: #DD0000">'%(?P&lt;browser&gt;Camino|Kindle(\&nbsp;Fire\&nbsp;Build)?|Firefox|Safari|MSIE|AppleWebKit|Chrome|IEMobile|Opera|OPR|Silk|Lynx|Version|Wget|curl|NintendoBrowser|PLAYSTATION\&nbsp;(\d|Vita)+)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?:;?)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(?:(?:[/&nbsp;])(?P&lt;version&gt;[0-9A-Z.]+)|/(?:[A-Z]*))%ix'</span><span style="color: #007700">,&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$u_agent</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">PREG_PATTERN_ORDER</span><span style="color: #007700">);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">0</span><span style="color: #007700">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">][</span><span style="color: #0000BB">0</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">0</span><span style="color: #007700">];<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Playstation&nbsp;Vita'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;)&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'PlayStation&nbsp;Vita'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'Browser'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;(</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Kindle&nbsp;Fire&nbsp;Build'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">||&nbsp;(</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Silk'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'Silk'&nbsp;</span><span style="color: #007700">?&nbsp;</span><span style="color: #DD0000">'Silk'&nbsp;</span><span style="color: #007700">:&nbsp;</span><span style="color: #DD0000">'Kindle'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'Kindle&nbsp;Fire'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;!(</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">])&nbsp;||&nbsp;!</span><span style="color: #0000BB">is_numeric</span><span style="color: #007700">(</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">0</span><span style="color: #007700">])&nbsp;)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Version'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;)];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;(</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'NintendoBrowser'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">||&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'Nintendo&nbsp;3DS'&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;&nbsp;=&nbsp;</span><span style="color: #DD0000">'NintendoBrowser'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;(</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Kindle'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'Kindle'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;(</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'OPR'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">||&nbsp;(</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Opera'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'Opera'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;(</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Version'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">];&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">][</span><span style="color: #0000BB">0</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'AppleWebKit'&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;(&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'Android'&nbsp;</span><span style="color: #007700">&amp;&amp;&nbsp;!(</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">0</span><span style="color: #007700">)&nbsp;)&nbsp;||&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Chrome'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;)&nbsp;)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'Chrome'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;(</span><span style="color: #0000BB">$vkey&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Version'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">$vkey</span><span style="color: #007700">;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'BlackBerry'&nbsp;</span><span style="color: #007700">||&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'PlayBook'&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'BlackBerry&nbsp;Browser'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;(</span><span style="color: #0000BB">$vkey&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Version'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">$vkey</span><span style="color: #007700">;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Safari'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;)&nbsp;)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'Safari'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;(</span><span style="color: #0000BB">$vkey&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'Version'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;))&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">$vkey</span><span style="color: #007700">;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">][</span><span style="color: #0000BB">0</span><span style="color: #007700">]&nbsp;==&nbsp;</span><span style="color: #DD0000">'MSIE'&nbsp;</span><span style="color: #007700">){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'IEMobile'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;)&nbsp;)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'IEMobile'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}else{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'MSIE'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">0</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'version'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;}elseif(&nbsp;</span><span style="color: #0000BB">$key&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #0000BB">array_search</span><span style="color: #007700">(&nbsp;</span><span style="color: #DD0000">'PLAYSTATION&nbsp;3'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$result</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;)&nbsp;!==&nbsp;</span><span style="color: #0000BB">false&nbsp;</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'platform'</span><span style="color: #007700">]&nbsp;=&nbsp;</span><span style="color: #DD0000">'PlayStation&nbsp;3'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">[</span><span style="color: #DD0000">'browser'</span><span style="color: #007700">]&nbsp;&nbsp;=&nbsp;</span><span style="color: #DD0000">'NetFront'</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;</span><span style="color: #0000BB">$data</span><span style="color: #007700">;<br /><br />}</span>
</span>
</code></pre></div>

<div style="text-align: center"><a href="https://github.com/donatj/PhpUserAgent" target="_blank">Source Availbile at Github as Well</a></div>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/RewriteRule_Generator</guid>
      <title>Batch RewriteRule Generator</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Mon, 28 Feb 2011 19:58:49 CST</pubDate>
      <link>http://donatstudios.com/RewriteRule_Generator</link>
      <description><![CDATA[<fieldset style="float: right; margin-left: 11px; text-align: center"><legend>Find this useful?</legend>Buy me a beer! <hr /> <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick" style>
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHLwYJKoZIhvcNAQcEoIIHIDCCBxwCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYAy4BMQR+gXgzT+VlMBgLYMmHmJUzkedAnz3pQKsB0ZJPBkPs8UGmCDeD3fwMMAEiMxqanNw+fX5XtRJp18ONZmM6NLXPy9MtWM5e4W4ZaaKwvYhg+BqjGec1IE0yuKHvwrb9uBIpYnYrhj9Ar4TKo+y3jDj0Jyeyb8F4MUu3X8BDELMAkGBSsOAwIaBQAwgawGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIRndc+ju3hn+AgYgXZRvvb1qCbLB5lLTjPDeue/6MBVENfs1z2jAUSFA69Ppvo3gkF9PKu0q+iwcIxSiKA3eJMVP6m4cayd1UFLSgN9rHbisgz3RRRqMMZXIoP7oZ45SXM+xl6TUKcGc7U14wRNuj5o1K8HqC60UZaHr37tiiubp/ngw9i5l73kHBoqajlS3yS0bioIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTMwMjE1MDIwNjQzWjAjBgkqhkiG9w0BCQQxFgQU6pFsKsltm2oPpMCSwqPuFB2OShEwDQYJKoZIhvcNAQEBBQAEgYAXkXscShHz6Q+ckNrEnCQGB5z24Y9I2aSGHS9V3jHNbD+fHyUiGor0X2D9DmlJMi3ksBgJIVnTnv/+0ju2yN8WX3178bsoK+yliHYPRQpi2fANfVGLgmr2A33w06o2xi2DLxjL10JTWpxVSJRKQ64h/neP8tdwym8PumZwB0rB8w==-----END PKCS7-----
">
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" style="border: none; padding: 0">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
</fieldset>
<p>I find myself often needing to set up a large number of 301 redirects from an excel file of old to new url’s. Until this point, writing these had been a fairly exhausting process as you need to be certain to escape every character that could be picked up by the regular expression engine, or risk unintended consequences. On top of that, writing these rewrites to catch GET parameters in any order is complex to say the least.  While working through a large list of 301s for a very picky server where <em>Redirect 301</em> style redirects were not an option, I began setting up RewriteRule's by hand and decided there had to be a better way. Voila! My 301 RewritRule Builder was born.
	</p><img src="http://donatstudios.com/assets/28/Old2New.png" alt="Excel Usage Example" style="float: right; padding-left: 11px;" />
<p>You can simply enter a list of tab or space delimited urls (copied from Excel works great) and it will spit out the generated RewriteRule’s for you. GET parameters on the old urls are broken up and built into multiple RewriteCond’s set up to work in any passed order. 	</p>
<p>I hope someone finds this useful.  Any comments are welcome below, of feel free to <a href="https://github.com/donatj/Mod-Rewrite-Rule-Generator" target="_blank">fork me on github</a>! </p>


<fieldset> <center><small><a href="https://github.com/donatj/Mod-Rewrite-Rule-Generator" target="_blank">Fork my source on Github!</a></small></center></fieldset>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Google_Killed_The_Internet</guid>
      <title>Opinion: Google Killed the Internet</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Sun, 19 Dec 2010 22:35:10 CST</pubDate>
      <link>http://donatstudios.com/Google_Killed_The_Internet</link>
      <description><![CDATA[<h2>Googles Page Speed &ldquo;Optimizations&rdquo; Make Learning Difficult</h2>
<p>If you, as I, grew up during the web bubble we call the 90&rsquo;s and were interested at all in web development my guess is that you probably didn&rsquo;t learn most of your skills from a book or a class.  You probably would run across a site that had something cool on it and ponder  &ldquo;Oh my, how does that work?&rdquo; right click, view source, and after some digging were enlightened.  I&rsquo;m certain this is how most of my generation learned their HTML/CSS/JavaScript skills, it is certainly how I did, and often how I continue though now I have tools to help me even further pick apart a page such as Firebug.</p>
<p>Where I take issue is CSS and more so JavaScript minifying. Google wants essentially all your CSS and JavaScript minified. This lessens the bandwidth <em>Google</em> has to use, but in the process makes the source complete illegible to a human being.</p>
<p>I personally, on <em>this site</em>, lose a fair deal of my Page Speed Score because &ldquo;Minifying <a href="http://donatstudios.com/js/general.js" target="_blank">http://donatstudios.com/js/general.js</a> could save 311B (23% reduction)&rdquo;.   My general.js file is at the moment 1.3 kilobytes; I am losing points off my Page Speed score over 311 bytes simply because I want my source code to remain legible.  Can we please get a sanity check on this, Google.  311 bytes is not going to kill you.</p>
<h2>SEO is Destroying the Spirit of the Internet</h2>
<p>Back in the hay day of Geocities if you did a search you were likely to get a few if not mostly amateur pages in your results.  They weren&rsquo;t usually <em>well designed</em> perse, but they were often <em>very</em> useful. I can recall of the top of my head a few instances where they saved the day.</p>
<p>In steps &ldquo;Search Engine Optimization&rdquo;. Corporations are gaga over paying people to dig through content, study their keywords, rewording things and saturating content with keywords (often at the cost of readability I might add).</p>
<p>Now let&rsquo;s compare that to an amateur, who&rsquo;s content is designed for <em>human consumption</em> rather than <em>googles</em> and now has no way to keep up in this arms race. There usually isn&rsquo;t the money nor the desire to pump into SEO and content development.  They just want to provide some useful information to the public.  These kinds of sites are becoming increasingly hard to find, with corporate sites taking the lion&rsquo;s share of the hits.</p>
<p>Frankly I believe a lot of great content is getting missed thanks to SEO.  The spirit of the internet years ago was driven around the fact that anyone could write something, and have it read by millions of people.  While this is still the case, I find it far less likely now than it has been in previous years.  I don&rsquo;t think there is a solution; I just find the corporatization of the web a little disheartening.</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Grid_CSS_Versus_The_Web</guid>
      <title>Grid Style Sheets vs. The Semantic Web</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Tue, 19 Oct 2010 17:43:21 CST</pubDate>
      <link>http://donatstudios.com/Grid_CSS_Versus_The_Web</link>
      <description><![CDATA[<h2>Grid Style Sheets Must Die</h2>
<p>I have been aware of Grid Style Sheets for quite a while, but I long ago dismissed them as a fad like CSS Resets.  Recently though it came to my attention that OSCommerce 2.3 switched to the <a target="_blank" href="http://960.gs/">960 Grid Style</a> to replace its table based layout system.  I spend most of my development time working with OSCommerce and thought it would be good to understand this change so I could decide whether or not to integrate it into our fork.</p>
<p>My general impression playing with the latest nightly of OSCommerce 2.3 from Github though is that I don&rsquo;t like it – maybe I&rsquo;m old school, but in my view Grid Style Sheets go against what we spent the previous decade fighting for, separation of our concerns&hellip; separating presentation and content.</p>
<h3><em>You&rsquo;re Doing it Wrong</em></h3>
<p>Let&rsquo;s talk about <a target="_blank" href="http://en.wikipedia.org/wiki/Separation_of_presentation_and_content">separation of presentation and content</a>.</p>
<p>Ideally, content and layout information should be entirely separate.  Class names should be used to <em>describe</em> the <em>data</em> they contain, and <em>nothing to do with</em> the style the class will contain.  You should be able to completely rearrange / restructure a page simply by switching out a stylesheet.  The HTML should strive to be as purely a data delivery mechanism as possible, similar to that of an XML request, and the CSS is there to make it something a human can digest.</p>
<p>Using a Grid stylesheet – your class names <em>purely</em> apply to your <em>specific</em> layout.  Class names like &ldquo;grid_1&rdquo; or &ldquo;container_12&rdquo; are not indicative of content in the slightest, but rather where they are placed on the grid that is your page. <s>Grid_1 will <em>always</em> be leftmost. Always. Might as well use &lt;left&gt;</s> <strong style="color:red">Correction</strong>: Grid_1 will always be one twelfth of your page wide (on a twelve Column Grid) , Grid_2 two twelfths, et cetera. Nothing to do with describing the content contained.</p>
<h3>Grid Style Sheets are <em>Extremely</em> Limiting</h3>
<p>With a well-structured CSS layout, deciding to move your navigation from the left to the right is as simple as flicking a switch.  Usually after changing one to a few lines of CSS, and its switched. It <em>cascades</em> across your entire site and everything floats and adjusts.  Same goes for altering the width, or even the entire flow of your document. You can quite easily decide your left navigation is now a top navigation with no alteration to the content.</p>
<p>Contrast this with a grid style sheet – to reposition <em>anything</em> you will have to <strong>edit the HTML</strong> – change the class you call.  Restructuring a site is a major task. They are essentially <strong><em>tables</em></strong> done in CSS with <em>all the <strong>drawbacks of tables</strong></em><strong> </strong>with few of the benefits (*cough* valign *cough*).</p>
<h3>Why Does this Matter? </h3>
<p>Flexibility, sustainability, scalability – these are not just buzz words – these are important attributes of a well-developed site – these are things a grid layout does not deliver.</p>
<p>Let&rsquo;s look at this from a prebuilt application perspective. In OSCommerce 2.3 the left navigation is classed grid_4 which places it 4 gridlines out of 16, and the content area is push_4 which aptly pushes it over 4.  That defines not only where my navigation appears, but also its size.  Its inflexible without changing HTML, and quite honestly wouldn&rsquo;t work for the <strong>majority</strong> of carts I develop.</p>
<h3>Final Thoughts</h3>
<p>Grids, from a design perspective are not just<em> useful, </em>they&rsquo;re downright<em> ideal</em>.  Clear alignment of elements creating strong visual lines is something strived for.  <strong>Consistent</strong> padding and margins are all very important – and all of these are honorable virtues of a grid style sheet.</p>
<p>I definitely understand the draw and the time saved in the initial build of a site, but a meticulously well engineered style sheet supporting semantic CSS will in the long run pay back the initial investment many times over.</p>
<h3>Post Script</h3>
<p>While writing this, I&rsquo;ve decided <em>I</em> really like the <em>concept</em> and <em>aim</em> of the grid style sheet – and would <em>love it</em> if it did not interfere with the semantic nature of CSS.  If I could simply do something like the following, I would be using it 10 minutes ago.</p>
<pre>#leftnav {

	imports: .grid2;

}</pre>
<p>W3C – I&rsquo;m looking your direction to see this happen.  Until then, I&rsquo;m sticking with my old fashioned meticulously crafted CSS.</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/lorem_ipsum_generator</guid>
      <title>Statistics Based Lorem Ipsum Generator</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Wed, 22 Sep 2010 15:02:25 CST</pubDate>
      <link>http://donatstudios.com/lorem_ipsum_generator</link>
      <description><![CDATA[<p>Several months ago I was wondering to myself if you could detect patterns in a list of words and then use those patterns to generate a new list of fake &ldquo;words&rdquo;. A kind of a <em>flavored</em> Lorem Ipsum generator, where I could change the feel of the sentences by switching out the data set.  I&rsquo;m sure thousands of developers have done this before me – but it was a fun thought experiment.</p>
<p>I threw together a little script in PHP to test the idea, and the results were kind of interesting so I figured I'd throw it on my site - Anyway, here are some examples in action – you can hit reload for a random ipsum!  The code and a sample dataset are below.</p>

<fieldset style="clear:both"><legend>Faux English Demo</legend> </fieldset>
<fieldset><legend>Faux Latin-y English Demo</legend> </fieldset>
<fieldset><legend>Faux Japanese Demo</legend> </fieldset>
<br clear="all" />
<br />
You need to supply a set of words for it to base the pattern off - here is my <strong><a target="_blank" href="https://gist.github.com/raw/592419/2c7eb9757e578f743796699f10433c53dab28413/words_example.php">sample dataset</a></strong>.
<br />
 ]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/FastPatch</guid>
      <title>Fast Patches with Git</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Fri, 27 Aug 2010 15:59:26 CST</pubDate>
      <link>http://donatstudios.com/FastPatch</link>
      <description><![CDATA[<p>In my work, I deal with a lot of very similar codebases - and often if I fix something in one project I'll want to fix it in many others.  For a long time this has meant popping open Beyond Compare, which works, but isn't the simplest solution.</p>
<p>I use git on my projects - but they're not similar enough just to be branches. Recently I came up with a way to patch one or more commits from a project to another easily.  Here is a simple shell script I wrote to handle the task.</p>
<p>Simply put, you can use it either by <pre class="terminal">fast-patch.sh /z/my_project</pre> to patch a project with the latest commit from the project in the working directory or with an optional paramater, specify a number of commits, eg <pre class="terminal">fast-patch.sh /z/my_project 5</pre></p>
<p>Also, it will generate a patch log to ~/fastPatch.log which I generally use directly for my commit message by way of <pre class="terminal">git commit -F ~/fastPatch.log</pre></p>
<p>Any comments on how I could improve it, or questions are quite welcome.</p>
 ]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Balanced_Take_on_Gzip</guid>
      <title>A Balanced Look at GZIP and the User Experience</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Mon, 16 Aug 2010 18:30:31 CST</pubDate>
      <link>http://donatstudios.com/Balanced_Take_on_Gzip</link>
      <description><![CDATA[<p>With Google recently starting to involve load time in search  rankings, there has been a lot of talk about GZIP.  Google Webmaster Tools exclaims "Compressing  the following resources with gzip could reduce their transfer size by <strong>X</strong>KB:". Many people listen to this, and go out looking for a quick fix without realizing that <strong>poor GZIP implementation has a strongly negative effect on  the overall user experience</strong>.</p>
<p>There are two main ways in PHP to go about implementing  GZIP. The first is <a href="http://php.net/manual/en/function.ob-gzhandler.php">ob_gzhandler</a>.   Basically all that is really involved in setting this up is adding <code>ob_start(&quot;ob_gzhandler&quot;);</code> anywhere  before headers are sent, and it will blindly handle this for you. </p>
<p>The second is <a href="http://www.php.net/manual/en/zlib.configuration.php#ini.zlib.output-compression" target="_blank">zlib.output_compression</a> which while entirely transparent and Zends states that <q cite="http://www.php.net/manual/en/function.ob-gzhandler.php">using zlib.output_compression is preferred over ob_gzhandler().</q></p>
<p>This method can have a huge downside for users.  It can for instance greatly exacerbate the sluggishness  of an already slow page or even site. Right now your probably saying to yourself "What? I  thought GZIP made everything faster. This guy is crazy.". Allow me to explain. </p>
<p>Let&rsquo;s say that you have a site with no all encompasing output  buffer.  As PHP works its way through  your script it occasionally flushes out its internal buffer to apache, which in turn flushes it out to the browser, allowing the browser to begin rendering the incomplete document. Also, let&rsquo;s say as an example you have a hold up slowing down your page in the footer of your site. The  user would already have the rest of the content and could  begin reading  and enjoying the site.</p>
<p>Now let&rsquo;s compare to a site GZIPed with an output buffer  callback, e.g. ob_gzhandler. After the output buffer is started, your code  begins to execute.  Everything echoed is  held in the buffer until the page is completely done and the output buffer closes, at which point it is  GZIPed and flushed out to the user.   The user recieves less bits, but that hold up in the footer we spoke of before halts up the entire  rendering process, and the user cannot see anything  until the footer completes.</p>
<p>For your viewing pleasure, a demonstration:</p>
<fieldset><legend>Side by Side Comparison</legend>
	<table>
		<tr>
			<th>Non-GZIPed</th>
			<th>GZIPed</th>
		</tr>
		<tr><td>
			 
		</td><td>
			 
		</td></tr>
	</table>
    <small>It has come to my attention that Webkit browsers wait for a full result in an iframe to begin rendering - so this example will not work in Safari/Chrome</small>
</fieldset>
<h2>Take Note</h2>
<p>I am not stating that all GZIP is bad. To the contrary, GZIP can be very beneficial and I absolutely sympathize with google wanting to cut down on their bandwidth. If you have Firebug or something similar you can note that the request for the Non-GZIPed example is a whopping 100 <strong>kilobytes</strong>, whereas the GZIPed examples request is a measily 323 <em><strong>bytes</strong></em>! The reason for this is I did not have enough content to get Apache to flush so to get around this I added the following line.</p>
<pre>echo str_repeat(' ', 100000); <em>//This creates 100kb worth of data, enough to trigger my copy of Apache to flush;</em></pre>
<p>Not only does this trick cause Apache to flush,  but it  exagerates the positive effect of GZIP. Simple patterns, or in this case large amounts of whitespace, will compress quite wondefully.</p>
<p>Uncompressed static files, JavaScript and CSS being two of the best examples compress quite well. The best way to do this is on an Apache server is with </p>
<pre>&lt;IfModule mod_deflate.c&gt;<br />
&lt;FilesMatch &quot;\.(js|css)$&quot;&gt;<br />
SetOutputFilter DEFLATE<br />
&lt;/FilesMatch&gt;<br />
&lt;/IfModule&gt;</pre>
<h2>Looking for a Solution</h2>
<p>I've posted a number of questions to Stack Overflow [<a href="http://stackoverflow.com/questions/2868167/any-way-to-chunk-gzip-with-apache-and-php">1</a>]  [<a href="http://stackoverflow.com/questions/2955180/mod-deflate-caching-question">2</a>]  researching this writing and actually found very limited help.</p>
<p>It appears murky whether or not the protocol even supports  the functionality of chunking GZIP results into separately compressed blocks,  as would be required to decompress portions before the page is done – you can  chunk a complete GZIP response, but the advantage of this is limited.</p>
<p> Lastly I have seen &ldquo;promises&rdquo; of ob_gzhandler caching the  results leading to a speed boost on a number of blogs – this is purely fiction and not to be trusted.</p>
<h2>Moral of the Story</h2>
<p>Understand what you&rsquo;re doing before you do it.  <em>Blindly</em> using output buffering for GZIP can have adverse effects on your site.  </p>
]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/SimpleCalendar</guid>
      <title>SimpleCalendar</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Wed, 10 Nov 2010 23:54:03 CST</pubDate>
      <link>http://donatstudios.com/SimpleCalendar</link>
      <description><![CDATA[<div class="GithubFeed"><h2>Recent Activity</h2><h3><time class="entryDate" title="2013-05-23T12:28:06-05:00" datetime="2013-05-23T12:28:06-05:00">May. 23, 2013</time></h3><h4>Jesse Donat <small>(<a href="https://github.com/donatj" target="_blank">donatj</a>)</small></h4>Slight example cleanup<hr />Fixed a couple notices thrown<hr /><h3><time class="entryDate" title="2013-05-16T21:36:10-05:00" datetime="2013-05-16T21:36:10-05:00">May. 16, 2013</time></h3>Added the much requested ability to offset by days!<hr />Example HTML cleanup<hr />Slight HTML cleanup<hr />Removed<hr />Time event added, CSS nicer<hr />Path in example fixed<hr /><h3><time class="entryDate" title="2013-03-12T14:43:45-05:00" datetime="2013-03-12T14:43:45-05:00">Mar. 12, 2013</time></h3>Updated README to reflect namespaces and Composer changes<hr />Class not nested correctly for composer<hr />Requires at least 5.3 due to the namespaces<hr />Added a composer.json file<hr />PSR-0-ified<hr /><h3><time class="entryDate" title="2013-03-07T21:58:24-06:00" datetime="2013-03-07T21:58:24-06:00">Mar. 7, 2013</time></h3>Prefix and Suffix Added<hr />Update README.md<hr />Slight docs improvment<hr />Executed code formatter on the library<hr />Compass code fixes for tbody<hr />Calendar changed to use localized day names by default.<hr />Lots of slight cleanup<hr />Basic repo restructuring<hr /><h3><time class="entryDate" title="2012-11-29T16:43:23-06:00" datetime="2012-11-29T16:43:23-06:00">Nov. 29, 2012</time></h3>Updated README to have better code sample display.<hr /><h3><time class="entryDate" title="2010-07-12T14:32:34-05:00" datetime="2010-07-12T14:32:34-05:00">Jul. 12, 2010</time></h3>Merge branch 'master' of github.com:donatj/SimpleCalendar<hr />Leftover Debugging Echo Removed<hr /><h3><time class="entryDate" title="2010-07-10T12:39:14-05:00" datetime="2010-07-10T12:39:14-05:00">Jul. 10, 2010</time></h3>Added doc blocks to addDailyHtml and clearDailyHtml<hr /><h3><time class="entryDate" title="2010-07-08T13:43:10-05:00" datetime="2010-07-08T13:43:10-05:00">Jul. 8, 2010</time></h3>- Fix for months that start on sunday<br />
- Fix for months that end on saturday<hr />Formatting changes to the calendars surrounding table.<hr />Removed an accidental print_r<hr /><h3><time class="entryDate" title="2010-07-01T17:42:49-05:00" datetime="2010-07-01T17:42:49-05:00">Jul. 1, 2010</time></h3>Event Modification Started<hr /><h3><time class="entryDate" title="2010-06-30T15:10:22-05:00" datetime="2010-06-30T15:10:22-05:00">Jun. 30, 2010</time></h3>Initial version of SimpleCalendar<hr /></div>
<p>SimpleCalendar is a very simple PHP calendar class written to be easy to use and flexible.</p>
<p>It includes basic event logic and is made to be modified to your particular needs.</p>
<p><small>More Details Soon</small></p>

<h4>Sample Usage</h4>
<p><pre style="width:52%"><code>&lt;?php
require_once('SimpleCalendar.php');
$calendar = new donatj\SimpleCalendar();

$calendar->setStartOfWeek('Sunday');

$calendar-&gt;addDailyHtml( 'Sample Event', 'today', 'tomorrow' );
$calendar-&gt;show(true);
?&gt;</code></pre></p>

<h4>Example Calendar</h4>
<br />
<b>Warning</b>:  include(assets/0/example.php): failed to open stream: No such file or directory in <b>/var/www/donatstudios.com/public/corpus/modules/IncludeAsset.php</b> on line <b>8</b><br />
<br />
<b>Warning</b>:  include(): Failed opening 'assets/0/example.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in <b>/var/www/donatstudios.com/public/corpus/modules/IncludeAsset.php</b> on line <b>8</b><br />


<p><a href="http://github.com/donatj/SimpleCalendar" target="_blank">Click here for the Github repository.</a></p>

<table class="datatable" style="width: 100%" cellpadding="0" cellspacing="0">
<tr><th align="left">Build</th><th align="left">Date</th><th align="left">Message</th></tr><tr class="odd"><td align="left">v0.4.1<br /><small><a href="http://github.com/donatj/SimpleCalendar/zipball/v0.4.1" target="_blank">Download</a></small></td><td align="left">&nbsp;</td><td align="left"><small>Slight example cleanup</small></td></tr><tr class=""><td align="left">v0.4.0<br /><small><a href="http://github.com/donatj/SimpleCalendar/zipball/v0.4.0" target="_blank">Download</a></small></td><td align="left">&nbsp;</td><td align="left"><small>Added the much requested ability to offset by days!</small></td></tr><tr class="odd"><td align="left">v0.3.2<br /><small><a href="http://github.com/donatj/SimpleCalendar/zipball/v0.3.2" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2013-03-12T14:34:28-05:00" datetime="2013-03-12T14:34:28-05:00">Mar. 12, 2013</time></td><td align="left"><small>Fixed a nesting issue<br />
</small></td></tr><tr class=""><td align="left">v0.3.1<br /><small><a href="http://github.com/donatj/SimpleCalendar/zipball/v0.3.1" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2013-03-12T14:25:21-05:00" datetime="2013-03-12T14:25:21-05:00">Mar. 12, 2013</time></td><td align="left"><small>Fixed a problem with the composer JSON<br />
</small></td></tr><tr class="odd"><td align="left">v0.3.0<br /><small><a href="http://github.com/donatj/SimpleCalendar/zipball/v0.3.0" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2013-03-12T14:19:08-05:00" datetime="2013-03-12T14:19:08-05:00">Mar. 12, 2013</time></td><td align="left"><small>PSR-0, Composer<br />
</small></td></tr><tr class=""><td align="left">v0.2.0<br /><small><a href="http://github.com/donatj/SimpleCalendar/zipball/v0.2.0" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2013-03-12T14:02:01-05:00" datetime="2013-03-12T14:02:01-05:00">Mar. 12, 2013</time></td><td align="left"><small>First tag in a long time, maintence build<br />
</small></td></tr><tr class="odd"><td align="left">v0.1.0<br /><small><a href="http://github.com/donatj/SimpleCalendar/zipball/v0.1.0" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2013-03-12T14:02:23-05:00" datetime="2013-03-12T14:02:23-05:00">Mar. 12, 2013</time></td><td align="left"><small>Initial Release<br />
</small></td></tr></table>
]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/JDZoom</guid>
      <title>JDZoom</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Wed, 10 Nov 2010 23:53:32 CST</pubDate>
      <link>http://donatstudios.com/JDZoom</link>
      <description><![CDATA[<div class="GithubFeed"><h2>Recent Activity</h2><h3><time class="entryDate" title="2010-10-25T11:26:30-05:00" datetime="2010-10-25T11:26:30-05:00">Oct. 25, 2010</time></h3><h4>Jesse Donat <small>(<a href="https://github.com/donatj" target="_blank">donatj</a>)</small></h4>Fixed an issue with cross domain images<hr /><h3><time class="entryDate" title="2010-10-08T09:34:28-05:00" datetime="2010-10-08T09:34:28-05:00">Oct. 8, 2010</time></h3>Fixed a regression where selector would only accept strings<hr /><h3><time class="entryDate" title="2010-10-07T23:58:52-05:00" datetime="2010-10-07T23:58:52-05:00">Oct. 7, 2010</time></h3>Restructured Repo per MooTools Forge requirements<hr />Updated README.md<hr />Added Options:<br />
<br />
Magnified Position Option Added<br />
options: fixed/float<br />
<br />
Cancel Click Option Added<br />
boolean, prevents default on an images anchor<br />
<br />
Misc Code Cleanup...<br />
<br />
Fixed &quot;endless.com&quot; style added<br />
<br />
index.html demo's updated - shows many different<br />
options and settings in action.<hr />Added escaping to css url() calls to prevent problems with images with<br />
spaces in file names.<hr />Large Rework of Functionality<br />
- Chrome compatability added<br />
- Zoom box to magnified result math corrected<br />
- Image preloaded<br />
- Div now hovers over image rather than replacing<br />
- Selector option added allowing an element, array of elements,<br />
or a string selector to be passed.<br />
- Internet Explorer Compatability fixes.<hr /><h3><time class="entryDate" title="2010-06-07T11:22:08-05:00" datetime="2010-06-07T11:22:08-05:00">Jun. 7, 2010</time></h3>README.md and .gitignore added<hr />Initial Commit<hr /></div>

<p>JDZoom is a MooTools powered hover zoom / magnifier similarly styled to the one featured on Endless.com.</p>
<p>It is supported by IE6+, Firefox 2.5+, Safari 2+ and Chrome</p>
<p>It does not interfere with click though, such it can be used in conjunction with almost any lightbox. In fact by altering the selector when you instantiate it, most lightboxes can be extended with JDZoom without touching the HTML at all. See the <a href="http://donatstudios.com/assets/18/Demo/" target="_blank">advanced demonstration</a> for more details.</p>
<p>JDZoom requires MooTools, and is Compatible with 1.2.5+ and 1.3+</p>
<p>It <strong>does not require</strong> any components of <i>MooTools More</i>.</p>

<p><a href="https://github.com/donatj/jdzoom" target="_blank">Click here for the Github repository.</a></p>

<fieldset style="width:300px"><legend>Demonstration</legend>
<a rel="jdzoom_fixed" href="http://donatstudios.com/assets/18/Demo/images/sakakibaraonsen.jpg"  title="Sakakibara Onsen, I've been naked here"><img src="http://donatstudios.com/assets/18/Demo/images/sakakibaraonsen.jpg" width="300" /></a>
<div style="text-align: center"><small><a href="http://donatstudios.com/assets/18/Demo/">View Advanced Demonstration</a></small></div>
</fieldset>
<h2>Example Source</h2>
<pre><code>
&lt;html&gt;
&lt;head&gt;
	&lt;link href="http://donatstudios.com/&lt;a href="https://github.com/donatj/jdzoom/blob/master/Demo/css/jdzoom.css">css/jdzoom.css</a>" rel="stylesheet" type="text/css" /&gt;
	&lt;script src=&quot;<a href="http://mootools.net/download" target="_blank">js/mootools.js</a>&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
	&lt;script src=&quot;<a href="https://github.com/donatj/jdzoom/blob/master/Source/jdzoom.js" target="_blank">js/jdzoom.js</a>&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
	&lt;script type=&quot;text/javascript&quot;&gt;<br>	window.addEvent('load',function() { <br>		var jdz = new JDZoom();<br>	});<br>	&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
	&lt;a rel="jdzoom" href="http://donatstudios.com/&lt;a href="http://donatstudios.com/assets/18/Demo/images/venus.jpg" target="_blank">images/venus.jpg</a>"&gt;&lt;img src="http://donatstudios.com/&lt;a href="http://donatstudios.com/assets/18/Demo/images/venus_thumb.jpg" target="_blank">images/venus_thumb.jpg</a>" /&gt;&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>

<table class="datatable" style="width: 100%" cellpadding="0" cellspacing="0">
<tr><th align="left">Build</th><th align="left">Date</th><th align="left">Message</th></tr><tr class="odd"><td align="left">v0.9.1<br /><small><a href="http://github.com/donatj/jdzoom/zipball/v0.9.1" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-10-07T22:36:20-05:00" datetime="2010-10-07T22:36:20-05:00">Oct. 7, 2010</time></td><td align="left"><small>version 0.9.1<br />
</small></td></tr><tr class=""><td align="left">v0.9<br /><small><a href="http://github.com/donatj/jdzoom/zipball/v0.9" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-10-07T18:50:04-05:00" datetime="2010-10-07T18:50:04-05:00">Oct. 7, 2010</time></td><td align="left"><small>version 0.9 - nearing release<br />
</small></td></tr><tr class="odd"><td align="left">v0.1<br /><small><a href="http://github.com/donatj/jdzoom/zipball/v0.1" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-07-27T00:18:46-05:00" datetime="2010-07-27T00:18:46-05:00">Jul. 27, 2010</time></td><td align="left"><small>Initial Alpha<br />
</small></td></tr></table>

]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/XML_Excel_Exporter</guid>
      <title>XML Excel Exporter</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Wed, 10 Nov 2010 16:54:58 CST</pubDate>
      <link>http://donatstudios.com/XML_Excel_Exporter</link>
      <description><![CDATA[<br />
<b>Warning</b>:  file_get_contents(http://github.com/api/v2/json/commits/list/donatj/CorpusPHP/master/Source/includes/classes/XSpreadsheet.php): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found
 in <b>/var/www/donatstudios.com/public/corpus/modules/Github/filefeed.php</b> on line <b>30</b><br />
<div class="GithubFeed"><h2>Recent Activity</h2><br />
<b>Warning</b>:  Invalid argument supplied for foreach() in <b>/var/www/donatstudios.com/public/corpus/modules/Github/filefeed.php</b> on line <b>41</b><br />
</div>

<p>This simple class allows you via PHP to export an XML Spreadsheet compatible with all versions of Excel 2003 and up.</p>

<p>The files generated will work with versions of Office 2003 and up. It is considered production safe, and is currently in use in a fair number of production sites.<p>

<p>Unlike CSV or HTML Table exports, <strong>you can have multiple worksheets of data!</strong> A sheets data can be added to the spreadsheet either by a MySQL query resource or as a 2 dimensional array.</p>

<h3>Known bugs:</h3>
<ul>
	<li>Office 2007/2010 bark about XLS file extensions despite refusing to open XML file extensions downloaded from the internet. (Anyone know a solution?)</li>
	<li>DOMDocument incorrectly converts/removes non-printable/low range characters (eg: &amp;#10;).</li>
</ul>

<h3>Limitations:</h3>
<ul>
	<li>File Format is not supported by:
	<ul>
		<li>Open Office</li>
		<li>Google Documents</li>
		<li>Microsoft Office Live</li>
	</ul>
	</li>
</ul>

<h2>MySQL Example</h2>
<pre>require('includes/classes/XSpreadsheet.php');
$spread = new XSpreadsheet($fname)
$spread-&gt;AddWorksheet('Products', mysql_query(&quot;Select * From products&quot;))
	-&gt;AddWorksheet('Categories', mysql_query(&quot;Select * From categories&quot;))
	-&gt;Generate()-&gt;Send();
</pre>

<h2>Array Example</h2>
<pre>require('includes/classes/XSpreadsheet.php');
$data = array(
	array( 'Column 1', 'Column 2', 'Column 3' ),
	array( 1, 2, 3 ),
);
 
$spread = new XSpreadsheet($fname)
$spread-&gt;AddWorksheet('Awesome Sheet', $data )
	-&gt;Generate()-&gt;Send();
</pre>

<a title="Click Here to Download" href="http://donatstudios.com/Github/redirect?link=https%3A%2F%2Fgithub.com%2Fdonatj%2FCorpusPHP%2Fraw%2Fmaster%2FSource%2Fincludes%2Fclasses%2FXSpreadsheet.php" ><button type="button" >Click Here to Download</button></a>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/No_App_P1_PhpED</guid>
      <title>There Isn't an App for&#x2026; - Part 1: PhpED</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Thu, 09 Dec 2010 10:25:44 CST</pubDate>
      <link>http://donatstudios.com/No_App_P1_PhpED</link>
      <description><![CDATA[<h2>&ldquo;There Isn't an App For That&rdquo; - Windows Apps I haven't been able to replace in Mac OS X</h2>
<p>For a very long time I was a Windows guy, I <em>was once</em> a DOS guy. I switched nearly exclusively to Mac for my home use about 4 years ago. I&rsquo;ve been very happy with it. Its stable, well engineered, and - my favorite part of all - consistent. Yet, in this time there remain a few applications I cannot seem to replace.</p>
<h3><a href="http://www.nusphere.com/">Nusphere PhpED</a></h3>
<p> I am a PHP Developer by trade, and PhpED for my money is by far the best development environment for the serious PHP developer. When I do develop on the Mac platform it is usually split between two applications &ndash; Coda and TextMate. They are fantastic text editors, don&rsquo;t misunderstand, but when you are developing highly classed applications a strong IDE becomes highly desirable. PhpED&rsquo;s IntelliSense style PHP auto completion is <strong>on par</strong> with and in some ways <strong>surpasses</strong> Visual Studios.</p>
<p>Applications like Dreamweaver, Coda, TextMate and Espresso by comparison provide very basic auto completion. Zend Studio, Aptana Studio and other Eclipse based solutions have varying qualities of auto completion, but are awkward due to Eclipses generality and slow and visually out of water due to being built on Java. Komodo comes the closest, but can be very slow at times. Weighing in at $299 its pricetag is also heavy.</p>
<p>To give you in an example, in my CorpusPHP Framework the user class is built specifically to be extended for the sites particular needs. The login class therefore has a member instance of <em>whatever</em> type of object extending user you might have. This member is dynamically set as an instance and therefore type cannot be determined by <em>most</em> editors and auto completion will not work.&nbsp; PhpED on the other hand has a solution. This brings me to my favorite PhpED feature - deep phpDocumentor integration.</p>
<pre>class Login {<br />
&nbsp;&nbsp;&nbsp;&nbsp;/**
&nbsp;&nbsp;&nbsp;&nbsp;* Object that holds the user object, brought over from the session
&nbsp;&nbsp;&nbsp;&nbsp;* @var User
&nbsp;&nbsp;&nbsp;&nbsp;*/
&nbsp;&nbsp;&nbsp;&nbsp;public $user;

&nbsp;&nbsp;&nbsp;&nbsp;&hellip;&nbsp;&hellip;&nbsp;&hellip;<br />
}</pre>
<p>The above code snippet shows the user member of the login class. It can be any type of user, for instance admin or limited. It will though be inherently be something that extends the user object.&nbsp; Using phpDocumentor syntax here we imply that the type is a <em>user</em> and since PhpED is smart, it auto completes based on this.&nbsp; If you are unfamiliar with phpDocumentor syntax you should <a href="http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_phpDocumentor.pkg.html" target="_blank">read up on it</a>, and begin marking your PHP up in this standardized syntax.</p>
<p> <img src="http://donatstudios.com/images/post/phpedAutocomplete.png" alt="Php Autocomplete" /></p>
<p> PhpED takes this integration further and will show descriptions of methods and functions as well as their parameters and return values based on their inline phpDocumentor documentation.</p>
<p> <img src="http://donatstudios.com/images/post/phpedCodeDescription.png" alt="PhpED phpDoc description" /></p>
<p> While auto complete is the PhpED killer feature in my book, it has many other excellent features.</p>
<p>&nbsp;Its internal debugger&nbsp; is second to none, and requires <em>no</em> setup unlike other debuggers. It installs its own copies of Apache and PHP 4 &amp; PHP 5.2 or 5.3 which it uses to debug. The debugger can output to an internal version of IE or Mozilla or any external browser. Its quite configurable, everything you would expect like ability to set $_GET, $_POST, etc variables, break points, runtime analysis.</p>
<p>Some smaller features I personally enjoy are for instance:</p>
<ul>
	<li>To-Do list shows you all the todo&rsquo;s throughout your project.</li>
	<li>Continually improving, high quality CSS/HTML auto completion.</li>
	<li>The ability to set a default character set on a per-project basis</li>
	<li>Non-Ascii range characters show up dark blue in variable and function names, making them obvious &ndash; which is particularly helpful when working on code from a Cyrillic coder whereas &Alpha; (Alpha) and A are <em>visually</em> identical but entirely different characters.</li>
	<li>Great support &ndash; Support will work with you to fix a problem.</li>
</ul>
<h2>Update 7/7/2011</h2>
<p>The dark PhpED Theme I use is now available <a href="http://donatstudios.com/PhpED-Dark-Theme">here for PhpED 5 and 6</a>.</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/CorpusPHP</guid>
      <title>CorpusPHP</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Wed, 02 Dec 2009 15:21:04 CST</pubDate>
      <link>http://donatstudios.com/CorpusPHP</link>
      <description><![CDATA[<div class="GithubFeed"><h2>Recent Activity</h2><h3><time class="entryDate" title="2013-05-16T22:01:34-05:00" datetime="2013-05-16T22:01:34-05:00">May. 16, 2013</time></h3><h4>Jesse Donat <small>(<a href="https://github.com/donatj" target="_blank">donatj</a>)</small></h4>Cache class made to serialize<hr /><h3><time class="entryDate" title="2013-04-26T11:41:20-05:00" datetime="2013-04-26T11:41:20-05:00">Apr. 26, 2013</time></h3>Corrections to the Github api modules pulled in<hr /><h3><time class="entryDate" title="2013-03-05T12:47:02-06:00" datetime="2013-03-05T12:47:02-06:00">Mar. 5, 2013</time></h3>See doesn't die.  Drop submits an error code.<hr /><h3><time class="entryDate" title="2012-10-15T10:33:25-05:00" datetime="2012-10-15T10:33:25-05:00">Oct. 15, 2012</time></h3>Update Source/corpus/modules/TwitterFeed.php<br />
<br />
Change in the twitter API url<hr /><h3><time class="entryDate" title="2012-09-05T15:49:26-05:00" datetime="2012-09-05T15:49:26-05:00">Sep. 5, 2012</time></h3>Update TODO<hr /><h3><time class="entryDate" title="2012-08-17T14:46:52-05:00" datetime="2012-08-17T14:46:52-05:00">Aug. 17, 2012</time></h3>Basic SASS/Compass support added<hr /><h3><time class="entryDate" title="2012-07-11T00:40:38-05:00" datetime="2012-07-11T00:40:38-05:00">Jul. 11, 2012</time></h3>Merge branch 'master' of github.com:donatj/CorpusPHP<br />
<br />
Conflicts:<br />
&nbsp;&nbsp;corpus.sql<hr />Last.fm module allowed to float on its own<hr />Comments upgraded to DonatStudios hierarchical system<hr />Tag cloud improvements from Donat Studios<hr />supplementary column added for supplementary JSON encoded data about a page<hr />db_dump shell script added to scripts<hr />SQL dump upgraded to 5.5 looking file<hr /><h3><time class="entryDate" title="2012-07-10T23:30:11-05:00" datetime="2012-07-10T23:30:11-05:00">Jul. 10, 2012</time></h3>Brfeadcrumb code given itemprops<hr />Comment fix<hr />More useful MySQL errors<hr /><h3><time class="entryDate" title="2012-07-09T11:24:16-05:00" datetime="2012-07-09T11:24:16-05:00">Jul. 9, 2012</time></h3>headers_list_assoc function allowing headers_list result a an associative array added<hr /><h3><time class="entryDate" title="2012-06-05T15:24:40-05:00" datetime="2012-06-05T15:24:40-05:00">Jun. 5, 2012</time></h3>Update master<hr /><h3><time class="entryDate" title="2012-06-01T09:41:13-05:00" datetime="2012-06-01T09:41:13-05:00">Jun. 1, 2012</time></h3>old template =&gt; layout correction<hr /><h3><time class="entryDate" title="2012-05-15T23:03:53-05:00" datetime="2012-05-15T23:03:53-05:00">May. 15, 2012</time></h3>HTML5 cleanup-y stuff<hr />Mediabox advance removed<hr />Basic namespace support roughed in to the autoloader<hr />Made to throw errors by default in silly environments<hr />Merge branch 'master' of github.com:donatj/CorpusPHP<hr />Fairly major overhaul of the way database connections work<br />
<br />
db:: is no longer a short name but writher an actual wrapper of Database, and Database it self is now abstract, allowing multiple connections ala idl @henderjon<hr /><h3><time class="entryDate" title="2012-05-13T03:45:44-05:00" datetime="2012-05-13T03:45:44-05:00">May. 13, 2012</time></h3>Added the ability to allow routes relative to Content rather than root<hr />Fixed a problem where conf.php files would execute multiple times<hr />Tiny modification to the corpus class that allows conf.php files to be used as basic routers<hr />Slight CSS restructuring<hr />Silly old button code traded out in favor of CSS buttons<hr /></div>
<p>CorpusPHP is setting out to be a world class PHP Framework. The aim is simple, to provide encapsulation, modularization, and helpful tools, all without changing the way you write PHP.</p>
<p><strong>Project Information, documentation, and a timeline for the first release available  availble soon</strong></p>
<p>Nightly's available at <a href="http://github.com/donatj/CorpusPHP" target="_new">GitHub</a>, or download the latest milestone below.
<table class="datatable" style="width: 100%" cellpadding="0" cellspacing="0">
<tr><th align="left">Build</th><th align="left">Date</th><th align="left">Message</th></tr><tr class="odd"><td align="left">v0.981<br /><small><a href="http://github.com/donatj/CorpusPHP/zipball/v0.981" target="_blank">Download</a></small></td><td align="left">&nbsp;</td><td align="left"><small>Optional $seperator paramter added to the breadcrumb generator.</small></td></tr><tr class=""><td align="left">v0.99<br /><small><a href="http://github.com/donatj/CorpusPHP/zipball/v0.99" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2011-05-29T01:08:40-05:00" datetime="2011-05-29T01:08:40-05:00">May. 29, 2011</time></td><td align="left"><small>version 0.99<br />
</small></td></tr><tr class="odd"><td align="left">v0.98<br /><small><a href="http://github.com/donatj/CorpusPHP/zipball/v0.98" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-09-07T15:39:52-05:00" datetime="2010-09-07T15:39:52-05:00">Sep. 7, 2010</time></td><td align="left"><small>Version 0.98<br />
</small></td></tr><tr class=""><td align="left">v0.92<br /><small><a href="http://github.com/donatj/CorpusPHP/zipball/v0.92" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-05-14T12:55:38-05:00" datetime="2010-05-14T12:55:38-05:00">May. 14, 2010</time></td><td align="left"><small>Version 0.92<br />
</small></td></tr><tr class="odd"><td align="left">v0.85<br /><small><a href="http://github.com/donatj/CorpusPHP/zipball/v0.85" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-02-09T00:38:18-06:00" datetime="2010-02-09T00:38:18-06:00">Feb. 9, 2010</time></td><td align="left"><small>Major changes in module handling, caching, and configuration.<br />
</small></td></tr><tr class=""><td align="left">v0.9<br /><small><a href="http://github.com/donatj/CorpusPHP/zipball/v0.9" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-02-18T13:32:38-06:00" datetime="2010-02-18T13:32:38-06:00">Feb. 18, 2010</time></td><td align="left"><small>Major steps made in maturity, approaching release candidate<br />
</small></td></tr><tr class="odd"><td align="left">v0.7<br /><small><a href="http://github.com/donatj/CorpusPHP/zipball/v0.7" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-02-05T10:55:39-06:00" datetime="2010-02-05T10:55:39-06:00">Feb. 5, 2010</time></td><td align="left"><small>v0.7 - Initial PHP 5.3 Support<br />
</small></td></tr><tr class=""><td align="left">v0.1<br /><small><a href="http://github.com/donatj/CorpusPHP/zipball/v0.1" target="_blank">Download</a></small></td><td align="left"><time class="entryDate" title="2010-02-01T00:26:52-06:00" datetime="2010-02-01T00:26:52-06:00">Feb. 1, 2010</time></td><td align="left"><small>Initial Corpus Release<br />
</small></td></tr></table>
]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Andrew_Gross</guid>
      <title>The Work of Andrew Gross</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Sat, 28 Nov 2009 15:21:09 CST</pubDate>
      <link>http://donatstudios.com/Andrew_Gross</link>
      <description><![CDATA[Details to Come]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/Donat_Studios_Launches</guid>
      <title>Donat Studios Officially Launches</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Mon, 26 Oct 2009 20:45:28 CST</pubDate>
      <link>http://donatstudios.com/Donat_Studios_Launches</link>
      <description><![CDATA[<p>After much ado, Donat Studios is finally live!</p>
<p>There are a few things left to complete. Eventually setting up the documentation and public git repository for CorpusPHP will certainly take up a fair amount of time. I'm still not quite sure what to put in my portfolio, I was thinking of for the time being just throwing a bunch of my sketches in there.</p>
<p>My reasoning and goals behind this site are slightly different than my other sites. More than anything I wanted a place where I could rant about everything web related. I have strong opinions on many topics, and where Oasisband.net is good for movies and politics, and PHPStandards is pretty much just raw anger directed at Zend, and my live journal is for my personal life. Donat Studios on the other hand will allow me to express my thoughts on web development, databases, html, php, ajax, design, basically everything I do at work.</p>
<p>Where does the name &quot;Donat Studios&quot; come from? I was getting some photos printed by a great place in St. Paul, &quot;White House Custom Color&quot; and they needed to know the name of my studio. I didn't have a studio, so I quickly made up &quot;Donat Studios&quot;. Later, I realized I liked the name, and it stuck.</p>
<p>Here's wishing my newest domain luck!</p>]]></description>
    </item>
    <item>
      <guid>http://donatstudios.com/JSONP</guid>
      <title>JSONP: Take It With a Grain of Salt</title>
      <author>noreply@donatstudios.com(Donat Studios)</author>
      <pubDate>Mon, 12 Oct 2009 16:36:35 CST</pubDate>
      <link>http://donatstudios.com/JSONP</link>
      <description><![CDATA[<p>I don&rsquo;t want anyone take this the wrong way.&nbsp; I am having a torrid affair with JSON. It is so much simpler to parse than XML, and is an all around exceptional way to represent data. I have one caveat though, and that is &ldquo;JSON with Padding&rdquo; or &ldquo;JSONP&rdquo; as it goes by.</p>
<p>While building <em>this site</em> I was trying to bring in images from my Flickr account, as you may have noticed. Building the module in PHP, I basically ran <em>file_get_contents</em> on the feeds url and passed that directly into json_decode. On print_r&rsquo;ing the results, it was null. It had failed.&nbsp; As a test I echoed the result of the file_get_contents to see what was up. It was succeeding at retrieving the feed, and at a glance it looked like JSON, yet it was failing to parse. I knew something was up. </p>
<p> Here is an example result set:</p>
<pre>jsonFlickrFeed({<br />                &quot;title&quot;: &quot;Uploads from donatj&quot;,<br />                &quot;link&quot;: &quot;http://www.flickr.com/photos/donatj/&quot;,<br />                &quot;description&quot;: &quot;&quot;,<br />                &quot;modified&quot;: &quot;2009-05-31T08:24:46Z&quot;,<br />                &quot;generator&quot;: &quot;http://www.flickr.com/&quot;,<br />                &quot;items&quot;: [<br />           {<br />                        &quot;title&quot;: &quot;STP81353&quot;,<br />                        &quot;link&quot;: &quot;http://www.flickr.com/photos/donatj/3581174864/&quot;,<br />                        &quot;media&quot;: {&quot;m&quot;:&quot;http://farm4.static.flickr.com/3568/3581174864_be1a44764e_m.jpg&quot;},<br />                        &quot;date_taken&quot;: &quot;2009-03-30T19:10:34-08:00&quot;,<br />                        &quot;description&quot;: &quot;&lt;p&gt;&lt;a href=\&quot;http://www.flickr.com/people/donatj/\&quot;&gt;donatj&lt;\/a&gt; posted a photo:&lt;\/p&gt; &lt;p&gt;&lt;a href=\&quot;http://www.flickr.com/<br />photos/donatj/3581174864/\&quot; title=\&quot;STP81353\&quot;&gt;&lt;img src=\&quot;http://farm4.static.flickr.com/3568/3581174864_be1a44764e_m.jpg\&quot; width=\&quot;180\&quot; height=\&quot;240\&quot; alt=\&quot;S<br />TP81353\&quot; /&gt;&lt;\/a&gt;&lt;\/p&gt; &quot;,<br />                        &quot;published&quot;: &quot;2009-05-31T08:24:46Z&quot;,<br />                        &quot;author&quot;: &quot;nobody@flickr.com (donatj)&quot;,<br />                        &quot;author_id&quot;: &quot;30444376@N08&quot;,<br />                        &quot;tags&quot;: &quot;&quot;<br />           }<br />        ]<br />})</pre><p>On further examination I noticed, as you should have noted, the data is wrapped in a function.&nbsp; My first thought was that this violates the <a href="http://www.ietf.org/rfc/rfc4627.txt?number=4627">JSON Specification</a>, so I immediately tweeted about how silly it was for Flickr to have an invalid JSON feed when their parent company Yahoo employs Douglas Crockford, the creator of JSON. I soon received a response from a colleague informing me that this was JSONP. He explained what it was, and how to use it in jQuery.</p>
<p>	It basically comes down to this:&nbsp; XMLHttpRequest will not, for cross site scripting protection, work across domains.&nbsp; Therefore, JSON cannot be directly retrieved nor parsed via JavaScript across domains.<br />
	This is where JSONP comes in.&nbsp; JSONP is essentially a regular JSON result set wrapped in a function.&nbsp; Rather than using XMLHttpRequest to retrieve the document, you dynamically attach a script tag to the page pointing the the JSONP document, which evokes a function call passing your JSON object to whatever function the JSONP is set to pass the data to, forgoing the need to eval anything.</p>
<p>I have a number of <em>criticisms</em> of this approach.</p>
<ul>
	<li>First and foremost, JSONP      is at its very heart remote code execution.&nbsp; You must have <em>absolute</em> faith not only in the intentions of the site you are      bringing the feed in from, but the security of said site as well, because      the code is in fact being <strong><em>executed</em></strong> by the browser with <em>no      chance</em> for putting regular expressions or other such safety mechanisms      in place. This makes your site and visitors <em>wide open</em> to <strong>any and      all</strong> malicious code a hacker may plant in the feed.</li>
	<li>JSONP is a language specific      data type. What I mean by this is that JSONP is designed <em>for</em> <strong>JavaScript</strong>, and even then <strong>strictly      in the context of a web browser</strong>.&nbsp;      There is no <em>need, none,</em> for JSONP anywhere else or in any other language (PHP, ASP.Net, Ruby on      Rails). Making a <em>feed</em> in a      language specific format is inherently in <em>bad form</em>.</li>
	<li>Adding to the last point,      other languages currently cannot naturally parse JSONP. As I ran into in      PHP, it is <em>not valid JSON</em> and      json_decode will not handle it.&nbsp; My colleagues      answer was to write a regular expression to remove the function, but this      is at best a hack.</li>
	<li>Mixing function and data      goes in the face of <em>separation of concerns.</em> You&rsquo;ve got logic in your data, you&rsquo;ve got data in your logic.&nbsp; Bad form, good sir, bad form.</li>
</ul>
<p>JSONP is in many cases an unsafe practice, and more over its just <em>evil</em>. It is a necessary evil in some cases, but evil none the less.</p>
<p>There is at <em>least</em> one<strong> safe alternative</strong> to JSONP, and I&rsquo;m willing to wager more. The simplest solution provided you have access to server side code is to download, sanitize, optionally cache, and finally pass locally any remote JSON to the client rather than having the clients machine directly load the feed. Yes, this is added overhead on your side, bur for much added <strong>security</strong>.</p>
<p>If nothing else, it is up to you to weigh the risks and benefits before blindly using JSONP.</p>]]></description>
    </item>
  </channel>
</rss>

