<?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>andre bluehs &#187; Webby</title>
	<atom:link href="http://andrebluehs.net/blog/category/webby/feed/" rel="self" type="application/rss+xml" />
	<link>http://andrebluehs.net/blog</link>
	<description>nerdy, webby, smelly?</description>
	<lastBuildDate>Thu, 29 Jul 2010 19:56:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Backups or The Dreaded `rm -rf /*`</title>
		<link>http://andrebluehs.net/blog/2010/07/backups-or-the-dreaded-rm-rf/</link>
		<comments>http://andrebluehs.net/blog/2010/07/backups-or-the-dreaded-rm-rf/#comments</comments>
		<pubDate>Thu, 29 Jul 2010 19:47:36 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>
		<category><![CDATA[backups]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=274</guid>
		<description><![CDATA[At my startup, we have a moderately sane deployment environment. Check-ins are automatically pushed to a staging server (And i&#8217;m notified via notifo with this script). When we want to push code to our live server, we manually move things. For a while that meant we had to run a svn export command by hand [...]]]></description>
			<content:encoded><![CDATA[<p>At my startup, we have a moderately sane deployment environment. Check-ins are automatically pushed to a staging server (And i&#8217;m <a href="http://andrebluehs.net/blog/2010/06/subversion-and-notifo-and-post-commit/">notified</a> via <a href="http://notifo.com">notifo</a> with <a href="http://github.com/notifo/Notifo-API-Libraries/tree/master/svn-hooks/">this script</a>). When we want to push code to our live server, we manually move things.</p>
<p>For a while that meant we had to run a
<pre class="brush: plain;">svn export</pre>
<p>command by hand once a day or so. (we are under heavy development still)</p>
<p>Eventually we needed a couple more things done when we moved code to the live server (among them run google&#8217;s <a href="http://code.google.com/closure/compiler/docs/gettingstarted_ui.html">closure compile</a>r to reduce our js footprint). So I wrote a script to handle these things. Apparently while testing, I got distracted and never finished it, because when i ran it, the code was this:</p>
<pre class="brush: bash;"># ... snipped ...
#
# code to assure you're running as root
#

if [ -z $2 ]
    then
        loc= '/path/to/code'
    else
        $loc = $2
fi

# nuke old files
rm -rf $loc/*

# ... snipped ... </pre>
<p>Now, to the astute, you will notice there is a bug here. Can you find it? It&#8217;s in the assignment of</p>
<pre class="brush: plain;">$loc = $2</pre>
<p>It SHOULD be similar to the loc assignment above it.</p>
<pre class="brush: plain;">loc=$2</pre>
<p>Well. This throws an error, but happily keeps moving on down the script. Next question: what happens here when $loc is an empty string &#8221;? That&#8217;s right. <strong>I just ran rm -rf /*as sudo on our main server.</strong></p>
<p>I <a href="http://twitter.com/helloandre/status/19318926067">realized quickly</a> what had happened. Errors whizz by about permissions, and finally I Ctrl+c&#8217;d it out of existence. Everything still in memory was intact. Our application was still running, I was still ssh&#8217;d in. So I took stock of my life, and tried to calm down. I had no idea what to do at this point; thoughts ranged from spending the next 12 hours slowly rebuilding everything (I had the latest copy of our code and db on my local computer, so our code was safe) to never returning my CEO&#8217;s emails again and abandoning our company. I initially did some fruitless googling for &#8216;restore rm -rf *&#8217; even though I knew &#8211; short of serious hard drive-level manipulation &#8211; I was NOT getting anything back. </p>
<p>Then I remembered that we are using <a href="https://www.jungledisk.com/">Jungle Disk</a> to do daily and weekly backup. I figured out that Media Temple (our awesome <a href="http://mediatemple.net/webhosting/dv/">host</a>) allows us to reinstall our OS if something like&#8230; this happens. So first I checked that we had a recent backup. We did. So i went into our dasboard, and clicked on the button to &#8216;Revert to Default&#8217; (MT&#8217;s oh-shit button). <a href="http://twitter.com/helloandre/status/19320699412">And waited</a>. After that was done, the first thing I reinstalled was Jungle Disk and set it up to recognize itself. Then I simply ran Jungle Disk&#8217;s restore program, and voilà! I installed a few more programs that we needed, and <strong>we were 100% back up and running in about 3 hours</strong>.</p>
<p>We back up our /etc, /var, and /home folders daily and our entire file system weekly, so not too much data was lost. As it turns out, this doesn&#8217;t include our Postfix maildir, so we lost about a week&#8217;s worth of email (personally, I use gmail and it downloads all the email, so I didn&#8217;t lose anything). We have since fixed this to save email daily. </p>
<p>This story is another among many to remind you to back your shit up. Fortunately we ended up ok; thanks in no small part to Jungle Disk.</p>
<p>If you have any questions about our staging=>live process (which is now Idiot Proof&copy;), or our backup strategy, drop me a line at hello at andrebluehs dot net. </p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=274" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2010/07/backups-or-the-dreaded-rm-rf/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Subversion and Notifo and Post-Commit</title>
		<link>http://andrebluehs.net/blog/2010/06/subversion-and-notifo-and-post-commit/</link>
		<comments>http://andrebluehs.net/blog/2010/06/subversion-and-notifo-and-post-commit/#comments</comments>
		<pubDate>Sat, 19 Jun 2010 21:52:39 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>
		<category><![CDATA[hooks]]></category>
		<category><![CDATA[notifo]]></category>
		<category><![CDATA[post-commit]]></category>
		<category><![CDATA[subversion]]></category>
		<category><![CDATA[svn]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=265</guid>
		<description><![CDATA[A service called Notifo just launched recently. They facilitate ridiculously simple push notifications. So I wanted to be notified when any of my developers checked in code, and what the message they sent was. This is useful for me because we all work remotely, and I can&#8217;t just walk over to their office to check [...]]]></description>
			<content:encoded><![CDATA[<p>A service called <a href="http://notifo.com">Notifo</a> just launched recently. They facilitate ridiculously simple push notifications. So I wanted to be notified when any of my developers checked in code, and what the message they sent was. This is useful for me because we all work remotely, and I can&#8217;t just walk over to their office to check in on them. So I wrote a couple-line post-commit hook to tell me these things. Then i figured other people would want it, so i made it prettier and put it up on github.</p>
<h3>Pick up the script at github <a href="http://github.com/helloandre/Notifo-Post-Commit-Hook">Notifo Post Commit Hook</a></h3>
<p>You&#8217;ll need to change the NAME and APISECRET variables, and then it will work straight out of the box. You can test it without having to actually check anything in by doing cd&#8217;ing into your repo&#8217;s &#8216;hooks&#8217; folder, and then run the command &#8216;./post-commit /path/to/your/repo &lt;a revision number&gt;&#8217;</p>
<h3>Some highlights from the code:</h3>
<p>Notifo requires that the data you send it be url-encoded (makes sense), but there is no command line utility for that, specifically. So I had to use a tiny little perl script I found on this stack overflow question that had the same <a href="http://stackoverflow.com/questions/296536/urlencode-from-a-bash-script">url encoding from a bash script</a> problem. I also put in handling an empty message (yes, some people suck).</p>
<p>Don&#8217;t forget to make sure you have curl installed. php5-curl (or equivalent apache plugin) will not do, you need to actually have the command-line tool for this to work. The other programs used i&#8217;m pretty sure come standard. Svnlook comes with a standard subversion command-line installation (which I assume you have if you&#8217;ve got a repo on there&#8230;).</p>
<p>Something i&#8217;d like to improve is to get the author and message in one call. However, i can&#8217;t figure out how to separate them with awk, or any other command line utility.</p>
<h3>Notes on Notifo</h3>
<p>I am super happy with this service. I&#8217;ve wanted to add push notifications to several of my projects for a while, but the barrier to entry with sending notifications is pretty steep. The only other comparable option is <a href="http://prowl.weks.net/">Prowl</a>, but it&#8217;s api page is sparse, with no examples, and it&#8217;s only for iPhone (And costs $2.99). Also, they do not allow the flexibility of Notifo. With Notifo, you can send <em>yourself</em> a notification, you do not have to have a registered application or service. This is what allows my script to work, as well as the <a href="https://chrome.google.com/extensions/detail/lgffhepmapgeepjnhchaabmaoijfcnhi">Chrome to Notifo</a> link sharing extension written by Notifo co-founder <a href="http://paulstamatiou.com/">Paul Stamatiou</a>.</p>
<p>Email me at hello at andrebluehs dot net with any questions.</p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=265" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2010/06/subversion-and-notifo-and-post-commit/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Coffin Nails and Native Mobile Apps</title>
		<link>http://andrebluehs.net/blog/2010/06/coffin-nails-and-native-mobile-apps/</link>
		<comments>http://andrebluehs.net/blog/2010/06/coffin-nails-and-native-mobile-apps/#comments</comments>
		<pubDate>Fri, 04 Jun 2010 19:17:30 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>
		<category><![CDATA[apple]]></category>
		<category><![CDATA[ipad]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[native ipad app]]></category>
		<category><![CDATA[native iphone app]]></category>
		<category><![CDATA[web app]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=243</guid>
		<description><![CDATA[One of my startups, Usable Health, is making an iPad-specific version of our web app to be installed in restaraunts. (Side note: watch our CEO talk at Ignite Atl) Since we were going to set-and-forget these applications, we needed a way to prevent people from going to other sites (as this is a web app [...]]]></description>
			<content:encoded><![CDATA[<p>One of my startups, <a href="http://usablehealth.com/">Usable Health</a>, is making an iPad-specific version of our web app to be installed in restaraunts. (Side note: <a href="http://www.youtube.com/watch?v=RfUhoW1kP6g">watch our CEO</a> talk at Ignite Atl) Since we were going to set-and-forget these applications, we needed a way to prevent people from going to other sites (as this is a web app just running in safari). Then I came across this <a href="http://sixrevisions.com/web-development/html5-iphone-app/">article by Alex Kessinger</a>. And we realized:</p>
<p><strong>We wanted a native iPad app. So we made a web app.</strong></p>
<p>Alex&#8217;s article gets in to a bit too much detail for us. We really only need one line:</p>
<p>&lt;meta name=&#8221;apple-mobile-web-app-capable&#8221; content=&#8221;yes&#8221; /&gt;</p>
<p>This one line of code transformed our web app (built for ipad size and features) into a native-looking app with no Safari toolbars on the top or bottom. Add it to the home screen, and you are off and running.</p>
<p>There are other features of that post that we could use, such as setting a home screen icon, but we don&#8217;t care about this, because our app should never be closed anyway while at an installation.</p>
<p>Here are screens of before:</p>
<p><a href="http://ablu.us/plg"><img class="alignnone" title="Before" src="http://ablu.us/files/plg-before.png" alt="" width="1024" height="768" /></a></p>
<p>after:</p>
<p><a href="http://ablu.us/ymg"><img class="alignnone" title="After" src="http://ablu.us/files/ymg-after.png" alt="" width="1024" height="768" /></a></p>
<p>The total time from start to finish to get this working? <strong>26 minutes. This includes me sharing the link, and our developer reading the article.</strong></p>
<p>This is why i believe that for most applications, native apps are becoming obsolete. There are HUGE incentives for more adoption of this method:</p>
<ul>
<li>no need for apple developer licence (cheap for small companies)</li>
<li>don&#8217;t have to deal with the app store</li>
</ul>
<p>There are, however, cons:</p>
<ul>
<li>hardware acceleration is spotty. games are mostly out.</li>
<li>hard to charge to download app. however, can charge for access.</li>
</ul>
<p>For the people who say that &#8220;Well, it doesn&#8217;t work on other browsers&#8221;. That&#8217;s only half true. While this method of removing top and bottom bars only works on iPhone/iPad, the app still functions 100% normally in other mobile browser. Building a mobile app gets you 90% of the way, and adding that <strong>ONE LINE</strong> gets you the rest of the way for apple browsers.</p>
<p>Here is where you can try it on your ipad (or iphone/ipod touch):<a href="http://ablu.us/files/uh_ipad" target="_blank"> http://ablu.us/files/uh_ipad</a></p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=243" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2010/06/coffin-nails-and-native-mobile-apps/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Gmail and Tips and Tricks</title>
		<link>http://andrebluehs.net/blog/2010/05/gmail-and-tips-and-tricks/</link>
		<comments>http://andrebluehs.net/blog/2010/05/gmail-and-tips-and-tricks/#comments</comments>
		<pubDate>Wed, 05 May 2010 19:44:00 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>
		<category><![CDATA[gmail]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=233</guid>
		<description><![CDATA[Did you know that you can modify your gmail address with and it will still reach you? Gmail ignores dots &#8216;.&#8217; and everything after a plus &#8216;+&#8217; sign. This comes in extremely handy. Need to create several test accounts on your new web app? No problem! Just move around a dot in your address! Need [...]]]></description>
			<content:encoded><![CDATA[<p>Did you know that you can modify your gmail address with and it will still reach you? Gmail ignores dots &#8216;.&#8217; and everything after a plus &#8216;+&#8217; sign.</p>
<p>This comes in extremely handy. Need to create several test accounts on your new web app? No problem! Just move around a dot in your address! Need to give an email address, but don&#8217;t want to ever receive email from them? Tack a &#8216;+spam&#8217; on the end of your address and filter it to automatically be marked read and go to the trash.</p>
<p>Let&#8217;s take a look at how to do this.</p>
<p><strong>The Dot</strong></p>
<p>Gmail handily ignores all dots in the name part of your email address. andre.bluehs@gmail.com is exactly the same as andrebluehs@gmail.com is exactly the same as a.n.d.r.e.b.l.u.e.h.s@gmail.com. This is useful for signing up for accounts that require a unique email address, but you already have an account for (for testing, purposes, of course). All emails will go to your account, regardless of where and how many dots are in the name.</p>
<p>This also works for signing in to google. you can use your username without the @gmail part when logging into gmail. (This is not true for third party apps using your gmail identity for log in, such as stackoverflow, you need to provide your full email address)</p>
<p><strong>The Plus</strong></p>
<p>This hack is extremely useful for &#8216;tagging&#8217; email from certain places. If a site requires that you give an email address, simply put a +spam at the end of your username, and filter it. Note: you can put whatever you want after the +, i just use &#8216;spam&#8217; here as an example. Gmail does not automatically filter things with &#8216;+spam&#8217; on the end, you must do this manually with the next step.</p>
<p>To create the appropriate filter, click on &#8216;create new filter&#8217; next to the search button:</p>
<p><a href="http://ablu.us/xum"><img class="size-full wp-image-235 alignnone" title="gmail-step1" src="http://ablu.us/files/xum-gmail-step1.png" alt="" width="302" height="75" /></a></p>
<p>Then put in the To: field, whatever your email address is, with a &#8216;+spam&#8217; for the username.</p>
<p><a href="http://ablu.us/elq"><img class="size-full wp-image-235 alignnone" title="gmail-step2" src="http://ablu.us/files/elq-gmail-step2.png" alt="" width="437" height="167" /></a></p>
<p>and finally, check &#8216;mark as read&#8217; and &#8216;delete it&#8217; in the last step.</p>
<p><a href="http://ablu.us/icz"><img class="size-full wp-image-235 alignnone" title="gmail-step3" src="http://ablu.us/files/icz-gmail-step3.png" alt="" width="474" height="214" /></a></p>
<p>and you&#8217;re done! never to be pestered by &#8216;affiliate email&#8217; again.</p>
<p>This can be repeated for any kinds of filters you want, as well as using the dot hack with this filtering hack to accomplish the same thing.</p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=233" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2010/05/gmail-and-tips-and-tricks/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Techtopics: Week 1</title>
		<link>http://andrebluehs.net/blog/2010/04/techtopics-week-1/</link>
		<comments>http://andrebluehs.net/blog/2010/04/techtopics-week-1/#comments</comments>
		<pubDate>Mon, 05 Apr 2010 22:03:14 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=231</guid>
		<description><![CDATA[My gtacm techtopics started last week. It&#8217;s all about javascript, jQuery, and AJAX. This week consisted of the introduction to the browser: DOM, DOM Inspectors, javascript: syntax, variables, jquery: syntax, selectors, basic animations. You can download this week&#8217;s zip over at the techtopic page. It includes the html and javascript from our in class demonstration [...]]]></description>
			<content:encoded><![CDATA[<p>My gtacm techtopics started <a href="http://andrebluehs.net/blog/2010/03/techtopics-and-you/">last week</a>. It&#8217;s all about javascript, jQuery, and AJAX. This week consisted of the introduction to the browser: DOM, DOM Inspectors, javascript: syntax, variables, jquery: syntax, selectors, basic animations.</p>
<p>You can download this week&#8217;s zip over at the <a href="http://andrebluehs.net/techtopic">techtopic page</a>. It includes the html and javascript from our in class demonstration as well as another example, and the powerpoint i <em>meant</em> to show.</p>
<p>Here is some auxiliary stuff that I did not get to talk about in class but I wanted to cover.</p>
<h2><strong>$(document).ready(function(){})</strong></h2>
<p>All I said in class was that &#8220;this is necessary. Put everything inside here. jQuery won&#8217;t function without this. Don&#8217;t question&#8221;. I&#8217;m sorry, this was mean. What I really meant to explain was that this IS necessary, but it is NOT the only way to accomplish this task.</p>
<p>What this line is saying is that jQuery has some things that need to be run AFTER the browser has populated the DOM with all the HTML elements. It has to wait for this so that the elements EXIST when it tries to assign all the appropriate listeners to what you have told it to do. However, you can do this exact same thing with a much simpler call:</p>
<p><code>$(function(){})</code></p>
<p>That&#8217;s right, <strong>you can replace $(document).ready(fuction(){}) with $(function(){})</strong>.</p>
<p>Wow, that was easy.</p>
<h2>Dropdown menu</h2>
<p>I had originally planned on doing a full-blown example at the end of class. This example would be a dropdown menu that would work on hover over (similar to the one found at <a href="http://www.cc.gatech.edu">http://www.cc.gatech.edu</a>). I have included it in the zip (found on <a href="http://andrebleuhs.net/techtopic">the techtopic page</a>) along with a spiffed up code that we did in class.</p>
<p>The dropdown menu included in the zip is a bare-bones implementation that you can put into any navigation. I used a function we didn&#8217;t cover in class <a href="http://api.jquery.com/children/">.children()</a>. Briefly, this function finds all the HTML elements that are contained in that element (div, span, etc), and you can refine it by putting a selector as a parameter to the function (like in my dropdown example).</p>
<p>Please study this and modify it (try using <a href="http://api.jquery.com/fadeIn/">.fadeIn()</a>/<a href="http://api.jquery.com/fadeOut/">.fadeOut()</a> instead of .slideDown()/.slideUp()) and try some experimentation with this. Also, this is a great reference to have in the future if you are building something that needs a dropdown menu: this will work great for you.</p>
<h2>More Stuff</h2>
<p>Two things I didn&#8217;t get to talk about this week are <strong>$(this)</strong> and <strong>.animate(). </strong></p>
<p>$(this) is a reference inside a callback function to whatever was selected. In the dropdown example, $(this) refers to the div with class=&#8217;hover-container&#8217; that was hovered over. It only exists inside the callback function.</p>
<p><a href="http://api.jquery.com/animate/">.animate()</a> is the big kahuna when it comes to anything animated with jQuery. Actually, in the actual source code for jQuery most specifically named animation functions (slideDown, slideUp, etc) just call .animate() with the proper parameters for that specific function.</p>
<p>.animate() is ridiculously powerful and can do a lot of things. We will be covering it more in-depth in the final week.</p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=231" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2010/04/techtopics-week-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>You and Urls and Browser Plugins</title>
		<link>http://andrebluehs.net/blog/2010/03/you-and-urls-and-browser-plugins/</link>
		<comments>http://andrebluehs.net/blog/2010/03/you-and-urls-and-browser-plugins/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 06:11:41 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=217</guid>
		<description><![CDATA[Someone recently offered to pay me to write them a url shortening backend/script to use for themselves. Not only did I decide to not charge them for it, but I decided to write a chrome extension so you can use it with as little hassle as possible. For the tl;dr; crowd: download the url shortening script [...]]]></description>
			<content:encoded><![CDATA[<p>Someone recently offered to pay me to write them a url shortening backend/script to use for themselves. Not only did I decide to not charge them for it, but I decided to write a chrome extension so you can use it with as little hassle as possible.</p>
<p>For the tl;dr; crowd: download the url <a href="http://ablu.us/files/shorten.tar.bz2">shortening script</a> and the <a href="http://ablu.us/files/custom-ext.crx">chrome extension</a> to use with your shiny new shortener. Instructions are included.</p>
<p>The following instructions are included in the README.txt of the tar file. But here they are for convenience, and for any questions: leave them in the comments. NOTE: these instructions are if you want a default installation setup. This assumes you want all the default table, user name, and password. It&#8217;s probably a good idea to change them, but that&#8217;s up to you.</p>
<p><strong>Step 0: Things You Need</strong></p>
<p>You&#8217;re going to need a url. Probably a short one? There are tons of crazy tld&#8217;s available. i chose ablu.us cause it&#8217;s moderately close, yours might work even better.</p>
<p>Apache with mod_rewrite. Or equivalent server/rewrite setup.</p>
<p>PHP.</p>
<p><strong>Step 1: The Database</strong></p>
<p>You&#8217;re going to need to create the database for your service. NOTE: the user@server$ and mysql&gt; are the prompts you should  see (clearly user@server$ will be different for you).</p>
<pre class="brush: bash;">user@server$ msyql -u root -p

mysql&gt; CREATE DATABASE `uploads`;

mysql&gt; GRANT ALL PRIVILEGES ON `uploads`.* TO 'uploads_user'@localhost IDENTIFIED BY 'uploads_pass';

mysql&gt; FLUSH PRIVILEGES;

msyql&gt; quit;</pre>
<p>This will create the database we need to install things in the next step.</p>
<p><strong>Step 2: Installation</strong></p>
<p>If you table name, username, or password you need to edit config.php accordingly. There are also a number of other things you can customize in config.php like the alphabet used to chose characters from, the length of the shortened url.</p>
<p>One thing you <strong>MUST change</strong> the $site_url in config.php. Otherwise it will insert things into your database, but not give you the correct link back.</p>
<p>Visit /install of the folder from above from your browser (http://yourdoma.in/install). If you kept the defaults from the above step, you don&#8217;t need to change any of these fields.</p>
<p>Click &#8220;Install &gt;&gt;&#8221;. And barring any database errors, you should have a working url shortening service!</p>
<p>NOTE: You may want to consider removing the /install folder after this step. It&#8217;s not necessary as running it again won&#8217;t destroy any data, but you never know.</p>
<p><strong>Chrome Extension</strong></p>
<p>The chrome extension is pretty easy to use. The first time you try to use it, it will tell you to Set Shortener. You have two choices here: If you want to use your own service you just created, enter the following:</p>
<pre>yourdomain/up.php?site=</pre>
<p>If you want to use MY service, enter:</p>
<pre>ablu.us/s/up.php?site=</pre>
<p>I&#8217;m considering doing a Firefox addon as well, but creating one of those is a bit more involved.</p>
<p>This extension will work with any url shortening api that returns just the plain text of the short url. Examples include <a href="http://fizl.us">http://fizl.us</a>, <a href="http://is.gd/api_info.php">http://is.gd</a>.</p>
<p><strong>That&#8217;s It!</strong></p>
<p>You have your own url shortening service!</p>
<p>I am running a modified version of this on my own site: <a href="http://ablu.us/s/">http://ablu.us/s/</a>. The main ablu.us is an image uploader. Feel free to use it if you so chose, but I just created it for my own personal use for some coding practice.</p>
<p>Leave any questions in the comments below, and i&#8217;ll try to get to them.</p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=217" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2010/03/you-and-urls-and-browser-plugins/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ablu.us and Chrome Extensions</title>
		<link>http://andrebluehs.net/blog/2010/02/ablu-us-and-chrome-extensions/</link>
		<comments>http://andrebluehs.net/blog/2010/02/ablu-us-and-chrome-extensions/#comments</comments>
		<pubDate>Fri, 26 Feb 2010 17:16:57 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Securiy]]></category>
		<category><![CDATA[Webby]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[extensions]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=199</guid>
		<description><![CDATA[I recently created a file hosting and url shortening service for myself: ablu.us. Now as a pet project, i&#8217;m starting to get some feature creep action going on. Case in point: I just wrote a chrome extension to use this service. NOTE: I am never going to release this to the gallery, as I don&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>I <a href="http://andrebluehs.net/blog/2010/02/image-uploader-and-url-shortener-and-you/">recently created</a> a file hosting and url shortening service for myself: <a href="http://ablu.us/s">ablu.us</a>. Now as a pet project, i&#8217;m starting to get some feature creep action going on. Case in point: I just wrote a chrome extension to use this service.</p>
<p>NOTE: <em>I am never going to release this to the <a href="https://chrome.google.com/extensions?hl=en-US">gallery</a></em>, as I don&#8217;t want THAT much attention to this thing, it&#8217;s just sweet for me to have for myself. In all actuality, I might do something else with this url eventually, so releasing this out into the world would probably be a bad idea.</p>
<p>That said, here is a link to download it if you actually want it:</p>
<p><strong>INSTALL </strong><a href="http://ablu.us/files/ablu.crx?v=0.2.1"><strong>ablu.crx</strong></a><strong>. </strong></p>
<p><strong></strong>CHANGELOG :</p>
<p>version 0.2 now includes using fizl.us.</p>
<p>version 0.2.1 fixed an initial settings bug</p>
<p>It&#8217;ll ask you if you really trust me, and to continue. And that&#8217;s it.</p>
<p>I won&#8217;t get into the nitty gritty details of HOW TO create your own chrome extension quite yet, but expect a blog post about it soon. Also, Google has a bunch of getting started tutorials that helped me so well it only took me about 2 hours to write this extension from scratch.</p>
<p>Some interesting things are as follows:</p>
<p><strong>These babies are written in javascript</strong></p>
<p>This was news to me. I knew Firefox plugins are written in XUL, which is similar to javascript. Chrome extensions are written in plain, vanilla javascript. Not only that, the part that gets displayed is literally an html page that you can do whatever you want with. If you want to include jQuery, you can (mine does not). Any other library? yup. It also means that you can use any of the HTML 5 capabilities Chrome offers: local storage, canvas, image rotation.</p>
<p>This strikes me as a bit excessive as you can load an unlimited number of scripts from anywhere. Seems to me that this could be abused.</p>
<p><strong>Chrome allows copying to clipboard</strong></p>
<p>That&#8217;s right. You can copy things to clipboard just like in IE with</p>
<pre>document.execCommand('Copy')</pre>
<p>This is exactly how similar URL Shortening extensions work. After seeing how awesome this is, I have to wonder&#8230; why doesn&#8217;t Firefox support this? I don&#8217;t see it being a security risk more than copying profanity into the clipboard. Whatever, it&#8217;s nifty that chrome has it.</p>
<p><strong>Autoupdating is scary</strong></p>
<p>Hoo dangle is it scary. What happens if the dev&#8217;s life suddenly tanks and decides he wants to have your browser randomly redirect to a porn site at random intervals? If you have a previously installed extension of his and he decides to update this new functionality, he can (there are some caveats to this, like what permissions the extension already has). This could be problematic as it would be difficult to track down exactly what&#8217;s causing this browser behavior. This has the potential to turn any previously useful and non-porn-redirecting extension into a very messy thing to be a part of.</p>
<p>If you are in the market for (another) url shortening extension, give it a try. Let me know what you think. It could probably use a much better logo, so if  you want to help drop me a line at hello () andrebluehs [] net.</p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=199" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2010/02/ablu-us-and-chrome-extensions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Image Uploader and URL Shortener and You</title>
		<link>http://andrebluehs.net/blog/2010/02/image-uploader-and-url-shortener-and-you/</link>
		<comments>http://andrebluehs.net/blog/2010/02/image-uploader-and-url-shortener-and-you/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 16:18:27 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=169</guid>
		<description><![CDATA[I recently purchased the url ablu.us with the intention of using it as a url shortener/image uploader. So i did. It was actually quite trivial, and I had the code for it done about 6 months ago, but didn&#8217;t purchase the url till recently. I had created a photo uploader for another website I was [...]]]></description>
			<content:encoded><![CDATA[<p>I recently purchased the url ablu.us with the intention of using it as a url shortener/image uploader. So i did.</p>
<p>It was actually quite trivial, and I had the code for it done about 6 months ago, but didn&#8217;t purchase the url till recently. I had created a photo uploader for another website I was working on (<a href="http://newnantheatre.org">newnantheatre.org</a>) so I just copied over the code (I wasn&#8217;t getting paid anyway, so I felt it was ok to reuse the code). There are some improvements tho that I&#8217;ll document here. However, I&#8217;m not actually going into the implementation of a url shortener or image uploader. You should take a weekend and figure this out for yourself. Or maybe I&#8217;ll do a post on it later.</p>
<h3>Resize Me</h3>
<p>The first thing I wanted was for people to not have to scroll horizontally to see huge pictures. But I didn&#8217;t want to resize them during the upload process, because I wanted people to see them the largest possible. So i wrote a quick jQuery script to detect the window size/proportions, and resize the image to that roughly. There are still some bugs in this, and currently there is no way to see the actual real size image without going to the hotlink.</p>
<h3>mod_rewrite</h3>
<p>This was a huge thing, as I had just started experimenting with it recently. I&#8217;m not a fan of big, scary regular expressions, but they&#8217;re damn useful (and necessary) when it comes to mod_rewrite. For those of you who don&#8217;t know what this is, it&#8217;s pretty urls. It takes the part after the .com, .net, .us, etc, and turns it into a meaningful request to the script that is actually being accessed. This was particularly useful for url shortening because it removes the need for a &#8216;?x=xxx&#8217; at the end of a url.</p>
<h3>api</h3>
<p>In the case of the url shortening, i created a dead simple api to use. This can be used as a bookmarklet with any browser that supports them (Firefox, Chrome (only tested in dev builds), Safari, etc). Just paste this code into the url part of a bookmark to create a bookmarklet:</p>
<pre class="brush: jscript;">javascript:void(window.location='http://ablu.us/s/up.php?site='+location.href)</pre>
<p>Or you can visit http://ablu.us/s and copy the url you want to shorten and click &#8216;Shorten&#8217;. Or you can use it in any other context of plugin, extension, etc for url shortening purposes by using the url http://ablu.us/s/up.php?site=.</p>
<p>EDIT: Chrome Extension</p>
<p>I have written a <a href="http://andrebluehs.net/blog/2010/02/ablu-us-and-chrome-extensions/">chrome extensio</a>n for this as well.<br />
I hearby release this unto the world. My friend Eric (<a href="http://twitter.com/diagonalfish">@diagonalfish</a>) runs a similar (and WAY more professional) service at <a href="http://fizl.us">http://fizl.us</a>, and both of us doing this prompted me to write this post. If you have your own personal image uploader/url shortener, let us know in the comments.</p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=169" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2010/02/image-uploader-and-url-shortener-and-you/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CakePHP and Events with AJAX and MySQL (Part 1)</title>
		<link>http://andrebluehs.net/blog/2009/12/cakephp-and-events-with-ajax-and-mysql-part-1/</link>
		<comments>http://andrebluehs.net/blog/2009/12/cakephp-and-events-with-ajax-and-mysql-part-1/#comments</comments>
		<pubDate>Tue, 15 Dec 2009 23:15:31 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[pretty things]]></category>
		<category><![CDATA[web dev]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=125</guid>
		<description><![CDATA[I recently had a customer that needed an event display system. (They&#8217;re not paying me, and non-profit, so I feel ok putting this tutorial here). They wanted to have a nice pretty calendar that displayed when certain events happened. I had also decided early on to use CakePHP and had a fairly fleshed out site [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had a customer that needed an event display system. (They&#8217;re not paying me, and non-profit, so I feel ok putting this tutorial here). They wanted to have a nice pretty calendar that displayed when certain events happened. I had also decided early on to use CakePHP and had a fairly fleshed out site at this point, so I just wanted to add in another MVC that I could use. This is actually quite easy, thought it takes some setting up, so here is how I did it.<br clear='none'><br clear='none'></p>
<p>NOTE: This tutorial assumes you have an <a href="http://book.cakephp.org/view/641/Simple-Acl-controlled-Application">Acl controlled application</a>, and know how and when to get these things to work. I will explain when things need to be done with ARO/ACO, but not how. Also, I borrowed the design heavily from <a href="http://www.stefanoverna.com/log/create-astonishing-ical-like-calendars-with-jquery">this calendar tutorial</a><br clear='none'><br clear='none'></p>
<p><strong>Step 0:</strong> Setting up the DB<br clear='none'><br clear='none'></p>
<p>This database structure provides a very simplistic event. The event has a title, body (description), date, and ID, and if it reoccurs. You may need less or more things, this will be left to your discretion, this is just to get you started.</p>
<pre>CREATE TABLE IF NOT EXISTS `events` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(55) NOT NULL,
  `body` text,
  `date` date NOT NULL,
  `recurring` text,
  PRIMARY KEY  (`id`),
  KEY `title` (`title`)
)</pre>
<p><br clear='none'><br />
<strong>Step 1:</strong> The Model<br clear='none'><br clear='none'></p>
<p>I didn&#8217;t need anything at all in my model, so I just stole the first few lines from another one and put it there. I&#8217;ll put mine here simply for completeness.</p>
<pre>class Event extends AppModel {

	var $name = 'Event';

}</pre>
<p><br clear='none'><br />
<strong>Step 2:</strong> Setting up the controller<br clear='none'><br clear='none'></p>
<p>This won&#8217;t get in to ACL/ACO/ARO at all, but a hand-waving explanation of how-to-make-it-work. Let&#8217;s stub out the controller with what we will eventually need: some setup and 4 methods.</p>
<pre>class EventsController extends AppController {

	var $name = 'Events';
	var $helpers = array('Html', 'Form', 'Ajax', 'Javascript');

	function beforeFilter() {
	    parent::beforeFilter();
	    $this-&gt;Auth-&gt;allowedActions = array('index', 'display');
	}

       	function index(){
        }

	function add($date = ""){
        }

	function delete($id){
        }

	function edit($id = null) {
        }</pre>
<p>This will be what we are mainly going to  work on. The way mine functions is that there is no way to view an individual event&#8217;s details by clicking on it. You could add this in by including a view() method and displaying the info. That will be left as an exercise to the reader.<br clear='none'><br clear='none'></p>
<p><strong>Step 3:</strong> Setting up the index() method<br clear='none'><br clear='none'></p>
<p>What we need to do here is to get all the events within a certain range (the current month) and to display them out AJAX style. CakePHP makes this trivially easy.</p>
<pre>	Configure::write('debug', 0);
	$month = $_POST['m'];
	if ($month &lt; 10) $month = "0".$month;
	$year = $_POST['y'];

	$max = cal_days_in_month(CAL_GREGORIAN, $month, $year);
	$min = "01";

	$this-&gt;set('events', $this-&gt;Event-&gt;find('all', array(
							'conditions' =&gt; array("Event.date BETWEEN '$year-$month-$min' AND '$year-$month-$max'"))));
	$this-&gt;set('month', $month);
	$this-&gt;set('year', $year);

	$this-&gt;layout = "ajax";</pre>
<p>Explanation:<br clear='none'></p>
<pre>Configure::write('debug', 0)</pre>
<p>- because we&#8217;re using AJAX, we don&#8217;t want any pesky error messages (or if still in debugging, db calls) to show up.</p>
<pre>if ($month &lt; 10) $month = "0".$month;</pre>
<p>- later we&#8217;ll see javascript gives us the month without a leading 0. this is to put it back.</p>
<pre>cal_days_in_month(CAL_GREGORIAN, $month, $year);</pre>
<p>- a handy function to give us&#8230; exactly what it says. alternative to date(&#8220;t&#8221;, mktime(&#8230;.));<br clear='none'><br clear='none'></p>
<p>Part 2 to happen shortly<br clear='none'></p>
<ul>
<li>Ajax-ing in the content</li>
<li>Javascript Date() vs PHP date()</li>
<li>Correctly padding days before first of the month</li>
</ul>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=125" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2009/12/cakephp-and-events-with-ajax-and-mysql-part-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery and waves</title>
		<link>http://andrebluehs.net/blog/2009/07/jquery-and-waves/</link>
		<comments>http://andrebluehs.net/blog/2009/07/jquery-and-waves/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 21:00:23 +0000</pubDate>
		<dc:creator>Andre</dc:creator>
				<category><![CDATA[Webby]]></category>

		<guid isPermaLink="false">http://andrebluehs.net/blog/?p=88</guid>
		<description><![CDATA[I recently watched parts of the Google I/O Keynote. In the little demos they showed at the very beginning of the video, there was a little app that followed the mouse around and created waves wherever the mouse went. I wanted to do something similar, so here is a (quite lengthy, in-depth) tutorial. This tutorial [...]]]></description>
			<content:encoded><![CDATA[<p>I recently watched parts of the <a href="http://code.google.com/events/io/">Google I/O Keynote</a>. In the little demos they showed at the very beginning of the video, there was a little app that followed the mouse around and created waves wherever the mouse went. I wanted to do something similar, so here is a (quite lengthy, in-depth) tutorial.<br clear="none"><br />
This tutorial assumes you know enough about jQuery to understand what the $(function(){}) is used for and all associated $(&#8220;&#8221;) calls do. I will explicitly tell you what can be OUTSIDE of this anonymous function. Otherwise assume it is inside the jQuery function.I like to declare all my variables outside the jQuery function so that in case my other functions need them. My preference, and is not strictly necessary.<br clear="none"><br />
You can view the demo here<br clear="none"><br />
<a href="http://andrebluehs.net/wave.html"><img class="aligncenter size-full wp-image-89" style="border:none" title="demo" src="http://andrebluehs.net/blog/wp-content/uploads/2009/07/demo.jpg" alt="demo" width="200" height="50" align="center" /></a><br clear="none"><br />
Or you can <a href="http://andrebluehs.net/files/wave.tar">download the source</a>. (tar file containing only wave.html)<br clear="none"><br clear="none"><br />
<strong>Step 1: HTML</strong><br clear="none"><br />
First we need the very basic html to set the page up.<br clear="none"></p>
<pre>&lt;html&gt;
    &lt;head&gt;
        &lt;script src="http://code.jquery.com/jquery-latest.js"&gt;&lt;/script&gt;
        &lt;script type="javascript/text"&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id="click"&gt;Click Anywhere&lt;br&gt;Press 'c' toggle color changing&lt;/div&gt;
        &lt;div id="main"&gt;
        &lt;/div&gt;
    &lt;/body&gt;
&lt;/html&gt;</pre>
<p><br clear="none"><br />
That&#8217;s it. Step 1 done. Next.<br clear="none"><br clear="none"><br />
<strong>Step 2: Basic styling</strong><br clear="none"><br />
Another easy step, adding the CSS so that the bars look like how we want. As well as the little notifications at the top.<br clear="none"></p>
<pre>
#main {
	width: 98%;
	height: 100%;
	padding: 0;
	margin: 0;
}

#click {
	position: absolute;
	left: 50%;
	z-index: -1;
}
</pre>
<p><br clear="none"><br />
<strong>Step 3: Creating the bars</strong><br clear="none"><br />
Now we need to start by making something appear on the page. <br clear="none"><br />
We need to find out how many bars we need to cover the entire page. The variable set here, &#8216;divs&#8217; needs to be instantiated OUTSIDE the jQuery function due to it being used by another function outside as well.<br clear="none"></p>
<pre>
// you can make them as wide or as skinny as you like. I personally like 12px wide.
var width = 12;
divs = ($(window).width() - (width-2)) / (width+1);
</pre>
<p><br clear="none"><br />
Then we create the html to be inserted to our #main div and insert it there.</p>
<pre>
var index = 0;
while (index &lt; divs){
	new_divs += "&lt;div id='div" + index + "'&gt;&lt;/div&gt;";
	index++;
}
$("#main").html(new_divs);
</pre>
<p><br clear="none"><br />
Now we can add some styling to them. This could probably be done a bit clearer with some classes, but the width and left offset needs to be set dynamically anyway, so I chose to do this all at once.</p>
<pre>
var left_offset = 0;
$("#main div").each(function(){
	$(this).css({
		'position': 'absolute',
		'height':'50%',
		'width': width+'px',
		'padding':'0',
		'margin': '0',
		'background-color': color,
		'bottom': '0px',
		'left': (left_offset * (width+1))
	});
	$(this).attr("rel", 0);
	left_num++;
});
</pre>
<p><br clear="none"><br />
Setting the &#8216;rel&#8217; of the divs will come in handy later when we want the behavior to function properly. Now we can work on more of the business logic.<br clear="none"><br />
<strong>Step 4: Handling a click</strong><br clear="none"><br />
We are now going to create a function waver(e) that gets called on every click. (actually, it&#8217;s a document.mousedown event&#8230; advantages of this explained later). <br clear="none"><br />
When the person clicks, we want a couple things to happen: the height of the click to be stored, the closest div to the click needs to raise up to that point, and all the other divs to the left and right need to recursively animate up to that poisition.<br clear="none"><br />
First we need to know if we&#8217;re in an IE browser or not. Simple detection of a document.all field: </p>
<pre>
var IE = document.all?true:false
</pre>
<p><br clear="none"><br />
Then the detection of mouse X and mouse Y. Typically, with the IE version, you need to offset by document.body.scrollLeft and .scrollTop. However, because our bars will never be more than the window width, and never more than the window height, we don&#8217;t need to do this. Also, this is where calling the clicking event on document.mousedown comes in handy. When the mousedown even fires, it passes a parameter Event (here used as &#8216;e&#8217;) that contains all kind of useful information. You&#8217;ll notice that the IE version doesn&#8217;t use that. That is because the event. is a built in variable that contains all the info we need.</p>
<pre>
if (IE) {
	var x = event.clientX;
	var y = event.clientY;
}
else {
	var x = e.pageX;
	var y = e.pageY;
}
</pre>
<p><br clear="none"><br />
Now that we have the location of the mouse, we can update the closest div, and all the other ones around it. We get the ID of the closes div by iterating though all of them and finding if the x-value of the mouse is inside the left and right of it. here, width+2 compensates for the extra room on either side of the bars. Also, a substr is necessary because .css(&#8216;left&#8217;) returns &#8216;XXpx&#8217;. The other substr is because our divs are names &#8216;divID&#8217;. This is a design choice, and could be done a myriad of other ways.</p>
<pre>
height = (document.height - y + 17);

$("#main div").each(function(){
	var templeft = $(this).css('left');
	templeft = parseInt(templeft.substring(0, (templeft.length - 2)));
	if ((x >= templeft) &#038;&#038; (x <= (templeft+width+2))){
		id = $(this).attr('id');
		id = parseInt(id.substring(3));
	}
});

$("#div" + id).animate({'height': height}, 400);
</pre>
<p><br clear="none"><br clear="none"><br />
<strong>Step 5: Color</strong><br clear="none"><br />
This is more a fun feature than something that is necessary, but included in the tutorial for completeness' sake. We are going to create a function (outside the anonymous jQuery function) that detects if the 'c' key is pressed on every document.onkeyup. <br clear="none"><br />
NOTE: In the file included at the top, you will notice that "document.onkeyup = logger" is located inside the jQuery function. This is not necessary, it can be outside as well.<br clear="none"></p>
<pre>
document.onkeyup = logger;
var toggle_color = false;

function logger(e){
	if (e.keyCode == 67){
		toggle_color = !(toggle_color);
	}
}
</pre>
<p><br clear="none"><br />
This allows a person to toggle the color changing function (initially turned off). Now, inside the waver function, we need to set the color based on a randomly generated number between 0 and 255.<br clear="none"><br />
NOTE: This step is a rather poorly implemented version of generating 3 random numbers. There are much better ways to do this, but I chose this way for clarity as well as ease.<br clear="none"></p>
<pre>
if (toggle_color){
	var red = Math.round((Math.random()*10000) % 255);
	var blue = Math.round((Math.random()*10000) % 255);
	var green = Math.round((Math.random()*10000) % 255);
	color = "rgb("+red+", "+blue+", "+green+")";
}
</pre>
<p><br clear="none"><br clear="none"><br />
<strong>Step 6: Recursion</strong><br clear="none"><br />
Now we have everything we need to start recursing over the rest of the bars and adjusting their height. At the very bottom of the waver function, we are going to put two setTimeout calls. One to do the left (moveleft()) side, and one to the right (moveright()). <br clear="none"><br />
It's right about this juncture in our tutorial that I should explain something about a priority system. In my initial versions of this script, waves reacted with each other in unfavorable ways. Things would go haywire and I would end up with uneven bars all the way across because if you clicked on the far left high, then quickly on the far right low, you would end up with half the bars high and half the bars low.<br clear="none"><br />
To fix this, you must give the last click the highest priority, and if another wave is encountered, the most recent click will be animated. A variable must be declared (here, count) and then every time waver is called, incremented. This new count is then passed on to the recursive functions and used as the priority number. (There is one caveat to this, that I cannot explain... if you click on a bar in the middle of being animated, it goes back to it's original position, not the newest clicked position).<br clear="none"><br />
These four lines should be the last lines inside the waver function. They use the divs immediately to the left and right of the one clicked on as their starting point.</p>
<pre>
left = id-1;
right = id+1;
setTimeout(function(){moveleft(left, height, count, color)}, 50);
setTimeout(function(){moveright(right, height, count, color)}, 50);
</pre>
<p><br clear="none"><br />
<strong>Step 7: The rest of the bars</strong><br clear="none"><br />
Now we need two separate functions (not really, it can be done in one, but for organization's sake, let's do two) that handle the left and right sides of the clicked bar. In my source, they are placed outside the jQuery function because I like to be difficult. Again, this is not strictly necessary. <br clear="none"><br />
They do the following: check the bar's current priority. If the bar's current priority (this_count) is less than the priority of the current wave (orig_count) we animate the height of the bar and change it's color (if needed). Then we decrement (or increment) the appropriate div id, and call the function again, keeping the same priority count, color, and height.</p>
<pre>
function moveleft(left_num, h_num, orig_count, this_color){
	this_count = $("#div"+left_num).attr("rel");

	// compensate for priority
	if (left_num >= 0 &#038;&#038; (this_count < orig_count)){
		$("#div"+left_num).css("background-color", this_color);
		$("#div"+left_num).attr("rel", orig_count);
		$("#div"+left_num).animate({'height': h_num}, 400);
		left_num--;
		setTimeout(function(){moveleft(left_num, h_num, orig_count, this_color)}, 40);
	}
}
</pre>
<p> <br clear="none"><br />
This is obviously on the left side. The right side is conceptually similar, but with the var name right_num. right_num must be less than divs (variable created in 3rd step) and right_num is incremented just before the recursive call.<br clear="none"><br clear="none"><br clear="none"><br />
<strong>Finished Product</strong><br clear="none"><br />
Now after creating the moveright() function, everything is done! You can check out the <a href="http://andrebluehs.net/wave.html">demo</a> or download the <a href="http://andrebluehs.net/files/wave.tar">source</a>. Enjoy.<br clear="none"><br clear="none"><br />
If you have any questions, comments, or can fix my bug (seen in step 6), leave it in the comments or email me at hello at andrebluehs dot net. </p>
 <img src="http://andrebluehs.net/blog/wp-content/plugins/wordpress-feed-statistics/feed-statistics.php?view=1&post_id=88" width="1" height="1" style="display: none;" />]]></content:encoded>
			<wfw:commentRss>http://andrebluehs.net/blog/2009/07/jquery-and-waves/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
