<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Adventures in Programming</title>
	<atom:link href="http://shawntabai.com/wp/feed/" rel="self" type="application/rss+xml" />
	<link>http://shawntabai.com/wp</link>
	<description>With Captain Database</description>
	<lastBuildDate>Sat, 12 Nov 2011 00:36:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>QueryString in JavaScript</title>
		<link>http://shawntabai.com/wp/2011/11/11/querystring-in-javascript/</link>
		<comments>http://shawntabai.com/wp/2011/11/11/querystring-in-javascript/#comments</comments>
		<pubDate>Sat, 12 Nov 2011 00:36:00 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Regex]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[QueryString]]></category>
		<category><![CDATA[Text Parsing]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=167</guid>
		<description><![CDATA[Here&#8217;s a simple one: you want to access the querystring via JavaScript. It seems like it should be so easy to do, but unfortunately, it&#8217;s not. In fact, doing it well can require some pretty heavy lifting depending on your algorithm. Well, I figured out a pretty easy way to do it with regular expressions [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a simple one: you want to access the querystring via JavaScript. It seems like it should be so easy to do, but unfortunately, it&#8217;s not. In fact, doing it well can require some pretty heavy lifting depending on your algorithm.</p>
<p>Well, I figured out a pretty easy way to do it with regular expressions (big surprise). First of all, we&#8217;ll need this extension method for regex escaping:</p>
<pre class="syntax js">RegExp.escape = function(text) {
    return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, &quot;\\$&amp;&quot;);
};</pre>
<p>Then all we need is this little guy to do the rest:</p>
<pre class="syntax js">var queryString = function(sParam) {
	var oExp = new RegExp(&quot;[\?\&amp;]&quot; + RegExp.escape(sParam) + &quot;\=([^\?\&amp;]*)&quot;, &quot;ig&quot;);
	var aResult = oExp.exec(location.search);

	if (!aResult || aResult.length &lt; 2)
		return null;

	return decodeURIComponent(aResult[1]);
};</pre>
<p>As far as data parsing goes, that still ended up being fairly easy. <img src='http://shawntabai.com/wp/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/11/11/querystring-in-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wubi (Ubuntu Installer for Windows)</title>
		<link>http://shawntabai.com/wp/2011/10/05/wubi-ubuntu-installer-for-windows/</link>
		<comments>http://shawntabai.com/wp/2011/10/05/wubi-ubuntu-installer-for-windows/#comments</comments>
		<pubDate>Wed, 05 Oct 2011 10:50:37 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Reducing Entropy]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=147</guid>
		<description><![CDATA[People who listen to me rant know that I&#8217;m a big fan of minimalism. Not just because I&#8217;m a hippy who doesn&#8217;t like accumulating possessions (Bluetooth gadgets excepted), but because I want to live my life in a way that&#8217;s sustainable to both myself and my environment. I usually refer to this as reducing entropy, [...]]]></description>
			<content:encoded><![CDATA[<p>People who listen to me rant know that I&#8217;m a big fan of minimalism. Not just because I&#8217;m a hippy who doesn&#8217;t like accumulating possessions (Bluetooth gadgets excepted), but because I want to live my life in a way that&#8217;s sustainable to both myself and my environment. I usually refer to this as reducing entropy, which I think is an apt analogy. Well, it had recently been bugging me how reliant I was on Windows, which has an annoyingly high entropy level; even a simple task can almost never be done without extra time, processing, and complexity. The fact that I&#8217;ve been using it since I was 4 is a reasonable excuse, but it&#8217;s not cutting it any more.</p>
<p>So, I recently decided that I want to spend more time immersed in the Linux/Unix world. Not too long ago, I saw my usage of OSX go up dramatically, and I&#8217;ve been surprised at how much I dislike it. I really wanted to like it, I swear, but I just found it awkward and bulky in totally different ways than Windows (which is awkward and bulky in ways I&#8217;m slightly more accustomed to). So I decided to install Ubuntu on a machine at home, since that&#8217;s the Linux distro that everyone is always raving about. I was very quickly amazed. Before I even downloaded Ubuntu and started setting up a CD/USB installer, I came across something called Wubi that piqued my interest: <a href="http://www.ubuntu.com/download/ubuntu/windows-installer" target="_blank" rel="external">http://www.ubuntu.com/download/ubuntu/windows-installer</a></p>
<p>I&#8217;m sorry, did I hear that right? I don&#8217;t even need an install disc/drive? I can set up my new OS using a package installer that runs in my current OS? And it configures dual boot for me? Even if I don&#8217;t have any unpartitioned drive space? *face melt*</p>
<p>The only thing I really remember being daunting about Linux is setting it up, and this just made the first chapter of that story completely disappear. However, the other chapters (drivers, apps, plugins, etc.) were even easier than I had hoped too. Drivers were pretty much a non-issue as well, seeing that everything worked upon initial boot (I just needed a custom driver to enable 3D hardware acceleration on my video card, but that was a piece of cake). Apps were where I was really pleased though, since I didn&#8217;t have to open terminal one single time to get all my software installed and configured. I honestly found <a href="http://synergy-foss.org/" target="_blank" rel="external">Synergy</a> (a great tool, if you haven&#8217;t used it) much easier to set up on Linux than on Windows or OSX. Chrome was a little difficult to find in the universal package installer, but going to the Chrome web site found me a .deb installer that was just as easy anyways.</p>
<p>So what did I dislike? Well, the Wubi installer definitely has some less than ideal behavior. It&#8217;s a Python app, and has trouble with removable media, so it will bring up error messages when you run it saying the drive couldn&#8217;t be loaded. Don&#8217;t think it&#8217;s broken, fiddle with it, and kill the task (like I did). All you need to do is continue clicking OK until all the error messages are done; the program still runs as designed. After setup, getting Flash to work was harder than I would have liked. I think most of my difficulties stemmed from wanting to download installers and run them instead of using the universal package installer utility, so your mileage may vary, but I had to fiddle with it before getting it to work. What seemed to make it work right was installing browsers first, then installing the main Flash package. Don&#8217;t even try to download a Flash installer from the web; it will make you want to punch a baby in the face.</p>
<p>I was really pleased with this, and immediately set it up on my laptop too! So my laptop is now dual booting, and I used the System > Advanced system settings > Startup and Recovery dialog in Windows to make Ubuntu the primary boot option instead of Windows. Now I just need to format the drive on that home machine and reinstall with Ubuntu alone &#8212; I can&#8217;t believe how much better it&#8217;s gotten in the past few years! <img src='http://shawntabai.com/wp/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Also, one last sidenote: <a href="https://one.ubuntu.com/" target="_blank" rel="external">Ubuntu One</a> is a cloud drive solution that&#8217;s tightly integrated with the OS and gives you 5GB of storage for free! It also has native sync apps for Android, iOS, and Windows!</p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/10/05/wubi-ubuntu-installer-for-windows/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Web Application Icons (Favicon 2.0)</title>
		<link>http://shawntabai.com/wp/2011/10/01/web-application-icons-favicon-2-0/</link>
		<comments>http://shawntabai.com/wp/2011/10/01/web-application-icons-favicon-2-0/#comments</comments>
		<pubDate>Sat, 01 Oct 2011 21:13:31 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Cloud]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=129</guid>
		<description><![CDATA[These days, it&#8217;s becoming more and more common to deploy a solution as a web application. I&#8217;m not a huge fan of buzz word proliferation, but I won&#8217;t fault you for saying cloud-based apps (just keep it within reason). Anyhow, the differences in platform capabilities between web apps and native apps is growing increasingly thin, [...]]]></description>
			<content:encoded><![CDATA[<p>These days, it&#8217;s becoming more and more common to deploy a solution as a web application. I&#8217;m not a huge fan of buzz word proliferation, but I won&#8217;t fault you for saying cloud-based apps (just keep it within reason). Anyhow, the differences in platform capabilities between web apps and native apps is growing increasingly thin, so it makes sense to deploy our solutions as web apps &#8212; they&#8217;re more portable, more reliable, and more accessible.</p>
<p>The only problem is the discontinuity of the user experience. There are a lot of new features of HTML5 that allow your site to work more like an &#8220;app&#8221; and less like a &#8220;page&#8221;, but I wanted to focus on one in particular that doesn&#8217;t get a lot of attention: application icons. You know that stupid favicon.ico file that you have to put at the root of the domain? And that you still have to have a link tag in the page head for the browsers who <em>actually</em> follow the standard? But then they don&#8217;t recognize any icon size between 16&#215;16, because that&#8217;s all that&#8217;s in the standard? Well, thank the universe, the favicon is finally the red-headed step-child we always knew it was.</p>
<p>We now have a few different mechanisms for deploying these icons, and the best part: we can deploy multiple icons with different sizes!!! So we can finally have a cool little icon for the title bar, a spiffy medium icon for the home screen, and a mind-blasting hi-res icon for the launcher screen. Implementation is pretty easy too:</p>
<pre class="syntax html">&lt;link rel=&quot;shortcut icon&quot; href=&quot;favicon.ico&quot; /&gt;

&lt;link rel=&quot;apple-touch-icon&quot; href=&quot;apple-touch-icon.png&quot; /&gt;</pre>
<p>As you can probably tell, this syntax is not the accepted standard, but you should still use it for one good reason: it&#8217;s already been proliferated. Other devices seeking to match iPhone feature sets have begun reading this metadata as well &#8212; remember, fortune favors the prepared. Alternatively, you can also change the rel attribute to <code class="inline">apple-touch-icon-precomposed</code> if you don&#8217;t want to bother with all the extra gloss and effects that an iPhone app icon is supposed to have. Instead, it will do a decent job applying the effects for you.</p>
<p>So let&#8217;s use that technique, along with the standard mechanism that allows us to actually supply different icon sizes.</p>
<pre class="syntax html">&lt;link rel=&quot;shortcut icon&quot; href=&quot;favicon.ico&quot; /&gt;

&lt;link rel=&quot;apple-touch-icon-precomposed&quot; href=&quot;apple-touch-icon-precomposed.png&quot; /&gt;
&lt;link rel=&quot;icon&quot; sizes=&quot;57x57&quot; type=&quot;image/png&quot; href=&quot;app-icon-57.png&quot; /&gt;
&lt;link rel=&quot;icon&quot; sizes=&quot;114x114&quot; type=&quot;image/png&quot; href=&quot;app-icon-114.png&quot; /&gt;</pre>
<p>And now we have a healthy mix of different icon sizes! I love it when a standard comes together!</p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/10/01/web-application-icons-favicon-2-0/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Regex for Host File Entries</title>
		<link>http://shawntabai.com/wp/2011/09/21/regex-for-host-file-entries/</link>
		<comments>http://shawntabai.com/wp/2011/09/21/regex-for-host-file-entries/#comments</comments>
		<pubDate>Wed, 21 Sep 2011 20:13:34 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[Regex]]></category>
		<category><![CDATA[System Configuration]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=123</guid>
		<description><![CDATA[Since I&#8217;m rather addicted to regex, I always end up finding ways to use them for text parsing. I randomly felt the need to make one for host file entries: ^([^#\s]*)?\s*([^#\s]+\s*)*(#.*)?$ I know it might look like it&#8217;s more complicated than it needs to be, but this expression makes the entire parsing process incredibly simple. [...]]]></description>
			<content:encoded><![CDATA[<p>Since I&#8217;m rather addicted to regex, I always end up finding ways to use them for text parsing. I randomly felt the need to make one for host file entries:</p>
<p><code>^([^#\s]*)?\s*([^#\s]+\s*)*(#.*)?$</code></p>
<p>I know it might look like it&#8217;s more complicated than it needs to be, but this expression makes the entire parsing process incredibly simple. First of all, every line in a host file will match this expression (even the blank ones). Second, the capture groups are set up in a way that will make extracting the data elements simple: group 1 is the IP, each capture in group 2 is a host name, and group 3 is the comment.</p>
<p>Pretty simple, but makes the process pretty elegant.</p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/09/21/regex-for-host-file-entries/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Caching Data Operations Using Delegates</title>
		<link>http://shawntabai.com/wp/2011/09/19/caching-data-operations-using-delegates/</link>
		<comments>http://shawntabai.com/wp/2011/09/19/caching-data-operations-using-delegates/#comments</comments>
		<pubDate>Mon, 19 Sep 2011 23:02:02 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Delegates]]></category>
		<category><![CDATA[Lambda Expressions]]></category>
		<category><![CDATA[Reflection]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=110</guid>
		<description><![CDATA[Not too long ago, I had a situation where I wanted to enforce a caching policy on an SOA data model. While this is a pretty trivial alteration, it&#8217;s one of those situations where the code will only be as easy to maintain as it is generic. Luckily, insane levels of abstraction (among other things) [...]]]></description>
			<content:encoded><![CDATA[<p>Not too long ago, I had a situation where I wanted to enforce a caching policy on an SOA data model. While this is a pretty trivial alteration, it&#8217;s one of those situations where the code will only be as easy to maintain as it is generic. Luckily, insane levels of abstraction (among other things) are a specialty of mine.</p>
<p>Using an interface that defines all of the data operations, we can utilize delegates to streamline the caching process while still maintaining our façade pattern. That way, our caching mechanism can serve as a proxy, instead of just adding additional complexity.</p>
<p>First things first, we want to add our proxy method. Essentially, we&#8217;ll be able to call this method and pass the delegate that we want it to call for us. Instead of simply executing the method, we first need to check for it in the cache. After the value has been retrieved, we need to determine the TTL for the cache item and insert it (this part is using a custom <strong>CacheableAttribute</strong> class for annotation). So, here we go:</p>
<pre class="syntax csharp">/// &lt;summary&gt;
/// Gets the data for the specified method call from the appropriate source. If the
/// data is already present in cache, the method will not be invoked. If the data is
/// not cached, but cacheable, the return value from the data layer provider will be
/// cached.
/// &lt;/summary&gt;
/// &lt;typeparam name=&quot;T&quot;&gt;The return type of the data method.&lt;/typeparam&gt;
/// &lt;param name=&quot;oDataLayerFunc&quot;&gt;The IDataLayer method from which data will be retrieved.
/// This method can be specified by a function pointer, delegate, or lambda expression.&lt;/param&gt;
/// &lt;returns&gt;The value of the data method.&lt;/returns&gt;
public T Get&lt;T&gt;(Func&lt;IDataLayer, T&gt; oDataLayerFunc)
{
	//Get the optimized cache key from the function below
	string sKey = GetCacheKey(oDataLayerFunc);

	//Determine if the data is cacheable based on whether or not the type has the Cacheable
	//attribute applied to it
	TimeSpan oLifetime = CacheableAttribute.GetLifetime(typeof(T));

	//If the data is cacheable, use the delegate's key to search cache for it
	if (oLifetime != CacheLifetime.None)
	{
		object oCacheValue = Cache[sKey];

		//If the value was found in cache, don't execute the data layer method
		if (oCacheValue != null &amp;&amp; oCacheValue is T)
			return (T)oCacheValue;
	}

	//If the value couldn't be found in cache, invoke the data layer method
	T oDataValue = oDataLayerFunc.Invoke(DataLayer);

	//We also now want to look for a more specific cacheable attribute
	oLifetime = CacheableAttribute.GetLifetime(oDataValue);

	//If the data is cacheable, store the new value
	if (oLifetime != CacheLifetime.None)
	{
		Cache.Insert(sKey, oDataValue, null,
			DateTime.Now.Add(oLifetime), Cache.NoSlidingExpiration);
	}

	//Return the result
	return oDataValue;
}</pre>
<p>I know, it&#8217;s kind of crazy. But wait, there&#8217;s more! That GetCacheKey method I referenced, how are we actually going to implement that? We need to cache the value by what method is being called and what arguments are being passed to it. And still, we want it to be as automatic as possible, so that we don&#8217;t create a maintenance nightmare down the line.</p>
<p>The solution requires a bit of reflection, but it doesn&#8217;t go too deep:</p>
<pre class="syntax csharp">/// &lt;summary&gt;
/// Gets the cache key that should be used to identify the specified delegate.
/// &lt;/summary&gt;
/// &lt;param name=&quot;oFunction&quot;&gt;The delegate function to identify.&lt;/param&gt;
/// &lt;returns&gt;A string containing a unique identifier for the delegate and its
/// arguments.&lt;/returns&gt;
public static string GetCacheKey(Delegate oFunction)
{
	//To make this easier to visualize, there are several comments along the way
	//referring to what the current key is when the StringBuilder is updated.
	//These all assume that the delegate is a call to IDataLayer.GetRegions(4)

	//The delegate will always be a member of IDataLayer, so all we need to
	//identify the method is its reflected name
	StringBuilder sbKey = new StringBuilder();
	sbKey.Append(oFunction.Method.Name);
	//Current key: &lt;GetRegions&gt;b__8

	//If we can identify information in the target object, these are essentially
	//query parameters/arguments -- they should be part of the cache key
	if (oFunction.Target != null)
	{
		//Get metadata about the target object and its fields
		object oTarget = oFunction.Target;
		Type tTargetType = oTarget.GetType();
		FieldInfo[] aMembers = tTargetType.GetFields();

		//All of the arguments must be added to the key
		//i.e. GetRegions(4) should have a different cache key than GetRegions(1)
		foreach (FieldInfo f in aMembers)
		{
			//Add a delimiter between arguments
			sbKey.Append(';');
			//Current key: &lt;GetRegions&gt;b__8;

			object oValue = f.GetValue(oTarget);
			//If the argument has a value, add it with delimiter characters escaped
			if (oValue != null)
			{
				sbKey.Append(oValue.ToString().Replace(&quot;;&quot;, @&quot;\;&quot;));
				//Current key: &lt;GetRegions&gt;b__8;4
			}
		}
	}

	//The compiled string is a unique method call identifier
	return sbKey.ToString();
	//Final key: &lt;GetRegions&gt;b__8;4
}</pre>
<p>I know, you probably feel like you just watched Inception for the first time. But if you examine/test it until you figure out how it all works, I think you&#8217;ll see the elegance of it (or at least the reason why I have insomnia).</p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/09/19/caching-data-operations-using-delegates/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Regex for US Phone Numbers</title>
		<link>http://shawntabai.com/wp/2011/09/15/regex-for-us-phone-numbers/</link>
		<comments>http://shawntabai.com/wp/2011/09/15/regex-for-us-phone-numbers/#comments</comments>
		<pubDate>Thu, 15 Sep 2011 18:00:05 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[Regex]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=80</guid>
		<description><![CDATA[I know that this is something people often say you can&#8217;t use regex for, but they&#8217;re wrong. If you want one magical regex that works for every possible country, extension, and format, then maybe that&#8217;s unreasonable. But with the right levels of internationalization, it&#8217;s certainly possible. So here is what I could come up with, [...]]]></description>
			<content:encoded><![CDATA[<p>I know that this is something people often say you can&#8217;t use regex for, but they&#8217;re wrong. If you want one magical regex that works for every possible country, extension, and format, then maybe that&#8217;s unreasonable. But with the right levels of internationalization, it&#8217;s certainly possible.</p>
<p>So here is what I could come up with, assuming that it should only work with US phone numbers:</p>
<p><code>1?[-(). ]{0,2}(\d{3})[-(). ]{0,2}(\d{3})[-(). ]{0,2}(\d{4})</code></p>
<p>I think this is a pretty good level of accuracy. It&#8217;ll support many formats, and the capture groups will also break up the area code, prefix, and line number for you. Now all I need to do is learn every other phone number format in the world&hellip;</p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/09/15/regex-for-us-phone-numbers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Option Helpers for Chrome Apps &amp; Extensions</title>
		<link>http://shawntabai.com/wp/2011/09/13/option-helpers-for-chrome-apps-and-extensions/</link>
		<comments>http://shawntabai.com/wp/2011/09/13/option-helpers-for-chrome-apps-and-extensions/#comments</comments>
		<pubDate>Wed, 14 Sep 2011 05:59:46 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[Chrome Apps/Extensions]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=87</guid>
		<description><![CDATA[I always find it funny how writing an app or extension for Chrome is so easy to start, but so hard to finish. Why is it that loading/editing/saving options is as much work as the features those options control? Kind of a pain. Well, in the extension I&#8217;m currently working on [mysterious foreshadowing], I wrote [...]]]></description>
			<content:encoded><![CDATA[<p>I always find it funny how writing an app or extension for Chrome is so easy to start, but so hard to finish. Why is it that loading/editing/saving options is as much work as the features those options control? Kind of a pain. Well, in the extension I&#8217;m currently working on [mysterious foreshadowing], I wrote a bit of code to make that process a little less barbaric.</p>
<p>Essentially, I just want to have easy access to load and save my options, without making some big process out of it. Also, since the options pages in Chrome don&#8217;t have save buttons, I&#8217;d ideally like to do the same; edit a setting and it&#8217;s automatically saved. So here&#8217;s a simple version of implementing such a device:</p>
<pre class="syntax js">var options =
{
	//It's good to have default options, in case a setting doesn't yet exist
	_default: { },
	get: function()
	{
		var opt;
		//If they have options saved, load them
		if (!localStorage[&quot;options&quot;])
			opt = { };
		else
			opt = JSON.parse(localStorage[&quot;options&quot;]);

		//For any option that doesn't have a value, we'll just load in the default
		for (var o in options._default)
		{
			if (!opt[o])
				opt[o] = options._default[o];
		}

		return opt;
	},
	set: function(optionsFunction)
	{
		var opt = options.get();

		//this next line is where a good idea turns into pure genius
		var result = optionsFunction.call(opt);

		//Now just wrap it up, save it, and return the result
		localStorage.options = JSON.stringify(opt);
		return result;
	}
};</pre>
</p>
<p>Did I blow your mind? Is your face melted? Of course not, because I haven&#8217;t yet shown you how fully awesome this is in implementation:</p>
<pre class="syntax js">//Get an option value
$(&quot;#name&quot;).val(options.get().itemName);

//Set an option value
options.set(function() {
	this.itemName = $(&quot;#name&quot;).val();
});</pre>
</p>
<p>That&#8217;s right &#8212; one simple operation to get and set options. It would obviously get a little longer than this when loading multiple values, but the structure scales nicely:</p>
<pre class="syntax js">options.set(function() {
	this.itemName = $(&quot;#name&quot;).val();
	this.itemQuantity = $(&quot;#qty&quot;).val();
	this.itemPrice = $(&quot;#price&quot;).val();
});</pre>
<p>I can think of no better way to describe this than with a quote I hold near and dear to my heart: &#8220;Good is awesome, but awesome is TOTALLY AWESOME!&#8221;</p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/09/13/option-helpers-for-chrome-apps-and-extensions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Data APIs in Google Spreadsheets</title>
		<link>http://shawntabai.com/wp/2011/09/08/data-apis-in-google-spreadsheets/</link>
		<comments>http://shawntabai.com/wp/2011/09/08/data-apis-in-google-spreadsheets/#comments</comments>
		<pubDate>Thu, 08 Sep 2011 14:57:49 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[Application Scripting]]></category>
		<category><![CDATA[APIs]]></category>
		<category><![CDATA[Google Docs]]></category>
		<category><![CDATA[Spreadsheet]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=82</guid>
		<description><![CDATA[I just found out about the coolest feature in Google Docs: in a spreadsheet, you can use formulas to pull data from various Google APIs! I&#8217;m not at all surprised that one can do this integration, but very pleasantly surprised that it can be done as easily as with a formula. Say you&#8217;d like to [...]]]></description>
			<content:encoded><![CDATA[<p>I just found out about the coolest feature in Google Docs: in a spreadsheet, you can use <em>formulas</em> to pull data from various Google APIs! I&#8217;m not at all surprised that one can do this integration, but very pleasantly surprised that it can be done as easily as with a formula.</p>
<p>Say you&#8217;d like to create a spreadsheet to determine special facts about your stock portfolio (estimated taxes, net profit/loss, etc). Wouldn&#8217;t it be nice to be able to lay it out exactly how you want to in a spreadsheet? When I got to the part about the current quotes, I was ready to put on my scripting trousers, but it turned out to be totally unnecessary. All I needed was this:</p>
<p><code>=GoogleFinance("GOOG", "price")</code></p>
<p>How awesome is that?! For more details on this function, look at the <a title="GoogleFinance Function" href="https://docs.google.com/support/bin/answer.py?answer=155178" target="_blank">function details</a> or the <a title="Google Function Examples" href="https://spreadsheets.google.com/pub?key=pzLh3kFGO3Cw_nZZVZj0_HA&amp;output=html&amp;gid=0&amp;single=true" target="_blank">example page</a>. Also, a quick sidenote: there are <a title="Google Docs Help" href="https://docs.google.com/support/bin/static.py?page=table.cs&amp;topic=25273&amp;tab=1240289" target="_blank">other data services available through formulas</a>.</p>
<p>Now all I needed was a vesting schedule for stock plans, to have this be even more useful! By creating a simple table with dates and vesting volume, I was then able to write a VLOOKUP formula to automatically determine my current number of shares! Nothing fancy, see:</p>
<p><code>=VLookup(Now(), A2:B22, 2, true)</code></p>
<p>In case that&#8217;s not 100% clear, all I had to do was put my vesting schedule in a table (A2:B22) with the vesting date in the first column and the volume in the second column (thus the 2 in the third argument). The use of Now() in the first argument and true in the fourth argument are to make sure that we look up the volume that corresponds to the current date (it doesn&#8217;t have to match, just be the closest without going over).</p>
<p>I love automation, especially when it helps me quickly view information that I shouldn&#8217;t need to calculate on my own (that&#8217;s what computers are for). Hope it helps you too!</p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/09/08/data-apis-in-google-spreadsheets/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Toast Notifications Using jQuery</title>
		<link>http://shawntabai.com/wp/2011/09/06/toast-notifications-using-jquery/</link>
		<comments>http://shawntabai.com/wp/2011/09/06/toast-notifications-using-jquery/#comments</comments>
		<pubDate>Wed, 07 Sep 2011 06:37:26 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[Mobile]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[UI]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=62</guid>
		<description><![CDATA[So there are often times when we need a quick UI cue to let the user know something. &#8220;Hey, you know that button you clicked? Well I took care of it. Just thought you should know. Stay Cool. Cheers, Your Browser.&#8221; You get the idea. Often times, a toast can be a great way of [...]]]></description>
			<content:encoded><![CDATA[<p>So there are often times when we need a quick UI cue to let the user know something. &#8220;Hey, you know that button you clicked? Well I took care of it. Just thought you should know. Stay Cool. Cheers, Your Browser.&#8221; You get the idea. Often times, a <a href="http://developer.android.com/guide/topics/ui/notifiers/index.html#Toast" target="_blank">toast</a> can be a great way of doing this.</p>
<p>For those of you who might not be familiar with the idea of toast notifications, it&#8217;s an elegantly simple idea: a box with the notification text fades in, it stays for a moment, then it fades out. No dismissing, no modaling, no ambiguity.</p>
<p>Well, doing this in JavaScript really isn&#8217;t all that hard. In fact, with jQuery, it&#8217;s downright simple.</p>
<p>Here&#8217;s my JavaScript:</p>
<pre class="syntax javascript">	function toast(sMessage)
	{
		var container = $(document.createElement(&quot;div&quot;));
		container.addClass(&quot;toast&quot;);

		var message = $(document.createElement(&quot;div&quot;));
		message.addClass(&quot;message&quot;);
		message.text(sMessage);
		message.appendTo(container);

		container.appendTo(document.body);

		container.delay(100).fadeIn(&quot;slow&quot;, function()
		{
			$(this).delay(2000).fadeOut(&quot;slow&quot;, function()
			{
				$(this).remove();
			});
		});
	}</pre>
<p>And here&#8217;s my CSS:</p>
<pre class="syntax css">.toast {display: none; position: fixed; z-index: 99999; width: 100%; text-align: center; bottom: 2em;}

.toast .message {display: inline-block; color: #fff; padding: 5px; border-radius: 5px; box-shadow: 2px 2px 2px #666; -webkit-box-shadow: 2px 2px 2px #666; font-family: &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: .8em; background: #282324; background-image: -webkit-gradient(linear,left top,left bottom, color-stop(0, #554434), color-stop(1, #282324));}
</pre>
<p>Easy, clean, and awesome! Here&#8217;s a demo.</p>
<p><script type="text/javascript">
function toast(sMessage)
{
	var container = jQuery(document.createElement("div"));
	container.addClass("toast");
	var message = jQuery(document.createElement("div"));
	message.addClass("message");
	message.text(sMessage);
	message.appendTo(container);
	container.appendTo(document.body);
	container.delay(100).fadeIn("slow", function()
	{
		jQuery(this).delay(2000).fadeOut("slow", function()
		{
			jQuery(this).remove();
		});
	});
}
</script></p>
<style type="text/css">
.toast {display: none; position: fixed; z-index: 99999; width: 100%; text-align: center; bottom: 2em;}
.toast .message {display: inline-block; color: #fff; padding: 5px; border-radius: 5px; box-shadow: 2px 2px 2px #666; -webkit-box-shadow: 2px 2px 2px #666; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: .8em; background: #282324; background-image: -webkit-gradient(linear,left top,left bottom, color-stop(0, #554434), color-stop(1, #282324));}
</style>
<p><button type="button" onclick="toast('Here, have some toast!')">Try Me</button></p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/09/06/toast-notifications-using-jquery/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Upgrading to jQuery Mobile Beta 1</title>
		<link>http://shawntabai.com/wp/2011/07/18/upgrading-to-jquery-mobile-beta-1/</link>
		<comments>http://shawntabai.com/wp/2011/07/18/upgrading-to-jquery-mobile-beta-1/#comments</comments>
		<pubDate>Tue, 19 Jul 2011 04:42:17 +0000</pubDate>
		<dc:creator>Captain Database</dc:creator>
				<category><![CDATA[Mobile]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://shawntabai.com/wp/?p=51</guid>
		<description><![CDATA[So the jQuery Mobile Beta 1 has been out for a little while, but there are a couple gotchas I ran into when implementing it. First of all, this may sound obvious, but remember to upgrade to the appropriate version of jQuery as well (I used 1.6.2 to correct my issues). I hadn&#8217;t realized that [...]]]></description>
			<content:encoded><![CDATA[<p>So the jQuery Mobile Beta 1 has been out for a little while, but there are a couple gotchas I ran into when implementing it.</p>
<p>First of all, this may sound obvious, but remember to upgrade to the appropriate version of jQuery as well (I used 1.6.2 to correct my issues). I hadn&#8217;t realized that my previous version was even out of date, but it was causing some major issues on any page with checkboxes.</p>
<p>Also on the subject of checkboxes, there&#8217;s a bug in the way they&#8217;re handled. Whenever you have multiple checkboxes with the same name (which is perfectly legal, useful, and awesome), it will make them mutually exclusive. I thought this was funny, because if I had wanted radio buttons, I would have asked for radio buttons. Some Googling ran me into this article: <a href="https://github.com/jquery/jquery-mobile/issues/1944">https://github.com/jquery/jquery-mobile/issues/1944</a></p>
<p>The fix mentioned there works great. Here&#8217;s a snippet of the jQuery file after modification:</p>
<p><code><span class="comment">// input set for common radio buttons will contain all the radio<br />
// buttons, but will not for checkboxes. clearing the checked status<br />
// of other radios ensures the active button state is applied properly</span><br />
<span class="important"><span class="keyword">if</span> (inputtype === <span class="literal">"radio"</span>)</span><br />
&nbsp;&nbsp;self._getInputSet().not(input).prop(<span class="literal">'checked'</span>, <span class="keyword">false</span>);<br />
self._updateAll();<br />
<span class="keyword">return</span> <span class="keyword">false</span>;<br />
</code></p>
<p>Other than that, it was a pretty easy upgrade. jQuery Mobile rocks, and I can&#8217;t wait to see what they bring in the future!</p>
]]></content:encoded>
			<wfw:commentRss>http://shawntabai.com/wp/2011/07/18/upgrading-to-jquery-mobile-beta-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

