<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Steven Benner's Blog</title>
	
	<link>http://stevenbenner.com</link>
	<description>Random articles about programming, computing and the internet.</description>
	<lastBuildDate>Sun, 29 Aug 2010 23:22:31 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/stevenbenner" /><feedburner:info uri="stevenbenner" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Will the really real programmers please stand up?</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/AZIpxSZWCLU/</link>
		<comments>http://stevenbenner.com/2010/08/will-the-really-real-programmers-please-stand-up/#comments</comments>
		<pubDate>Thu, 12 Aug 2010 18:00:56 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[Desktop Development]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[Opinion]]></category>
		<category><![CDATA[Rants]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=1156</guid>
		<description><![CDATA[How do you define a real programmer? There are many facets that you can use to judge someone&#8217;s skill as a programmer: ability, experience, enthusiasm, dedication, etc. But I recently read an article by RethinkDB blogger Salva, and he boils all of these points down to one item, memorization. Some people agree with this idea, [...]]]></description>
			<content:encoded><![CDATA[<p>How do you define a real programmer? There are many facets that you can use to judge someone&#8217;s skill as a programmer: ability, experience, enthusiasm, dedication, etc. But I recently read <a href="http://www.rethinkdb.com/blog/2010/06/will-the-real-programmers-please-stand-up/" target="_blank">an article</a> by RethinkDB blogger Salva, and he boils all of these points down to one item, memorization. Some people agree with this idea, but I do not, at all.</p>
<p>I have heard several companies bragging about having interview scripts like this, saying that this kind of testing is the most effective way to filter out unqualified applicants. However I think that the post by Salva perfectly demonstrates just how flawed this concept is. If there is one thing you should have learned in school, it&#8217;s that standardized tests are a terrible metric for gauging skill or intelligence.</p>
<p>But more to the point, I think that this is the wrong way to define a real programmer. Allow me to present you with my definition of a real programmer.</p>
<p><span id="more-1156"></span></p>
<h3>What is a real programmer?</h3>
<p>This comes from one of my favorite quote in all of history:</p>
<blockquote><p>&#8220;Real knowledge is to know the extent of ones ignorance&#8221; &#8211; Confucius</p></blockquote>
<p>Having an encyclopedic knowledge of a college curriculum is not required to be a great programmer. Real world software development isn&#8217;t about memorizing low-level  programming techniques, or math formulas. These are great tools to have at your disposal, but are certainly not the end-all-be-all of development. It&#8217;s all about problem solving, in  the most efficient and elegant way that circumstances allow.</p>
<p>As a programmer you will be supplied with problems every day, and it&#8217;s your job to figure out how to solve these problems. To do this you need to know what you language of choice is capable of, this comes with experience. You will also need to be able to look at the possible solutions and pick the best one for the situation at hand.</p>
<p>Now here&#8217;s the fact that breaks the machine, you do not know every possible solution for every problem, <em>and you never will</em>. I don&#8217;t care how experienced you are, how much education you have had, or how many millions of lines of code you have written, because there are so many possible ways to approach the practical problems you find in software development that it is literally impossible to know everything. So you need to have a mix of creativity and resources that let you <em>learn</em> how to solve new problems.</p>
<p>Real programmers are the ones who can learn fast, and learn by doing.  These are the people who constantly strive to keep up with the  technology they love. They have ample real world experience building,  learning and growing their craft, but know that they still don&#8217;t know everything.</p>
<p>In short the defining characteristic of real programmers is that <strong>they never stop learning</strong>.</p>
<h3>My experience with real programmers</h3>
<p>I&#8217;ve worked as a peon coder, link-in-the-chain guy, I&#8217;ve worked as a manager who did the hiring and firing, I&#8217;ve worked as a one-man-shop serving milti-billion dollar corporations, and I&#8217;ve worked as a solo freelancer. This experience has taught me two things:</p>
<ol>
<li>I am not the greatest programmer in the world, despite what my website says, and</li>
<li>That you can rarely tell a good programmer from a bad one by the resume, or the interview.</li>
</ol>
<p>I&#8217;ve seen guys with terrible resumes turn out to be so far above my level that I still have them on my guru list, and I&#8217;ve seen guys with amazing resumes who couldn&#8217;t grasp even the most basic concepts.</p>
<h3>How to find a real programmer</h3>
<p>It is true that there is often a gap between what candidates say they can do and what they&#8217;re really capable of. That is why testing candidates is still a very good idea, but you need to test for higher level skills and abilities. Testing a candidate&#8217;s knowledge on obscure techniques and patterns means passing off great people for not remembering stuff that they simply do not  need to be top notch coders.</p>
<p>In my experience, it is very easy to separate the wheat from the chaff: go through the normal interview process to see if this is a person you can work with (and let&#8217;s face it, that&#8217;s all that the interview is really for). Then give them a small project, something realistic that resembles what their real work would be like, and send them home. Not a knowledge test, but a real &#8220;build something that does this&#8221; task.</p>
<p>When you get the finished result of their labor you will know if you&#8217;ve got the real deal. Is it done the way you would have expected (or better)? Is it high quality, showing some love some thought? Did they get it back to you reasonably quickly? Did they do anything cool to show off? If so, congratulations! You&#8217;ve just found a great programmer!</p>
<h3>code_by_google != bad_programmer</h3>
<p>Yes, they could have just coded by Google. But you know what? That shows  they can learn and adapt. I have plenty of respect for a person who doesn&#8217;t immediately know how to accomplish a complex task, but can quickly find out how and do it on their own without bothering the other programmers on your team.</p>
<p>Remember the key point of my real programmers definition, <em>they never stop learning</em>. Google is the greatest programming resource that has ever existed. You cannot look down on someone for using the most powerful learning tool computer science has ever known. I would go so far as to say knowing how to code by Google is the single most important skill in a programmers tool box, because if you want to grow as a programmer this is going to be the tool to use.</p>
<p>Someone who knows how to search for code examples and how to learn from the work of others will be more or less self-sufficient. They can learn and grow their skills on their own without needing someone else to do it for them. The ability to learn and grow your knowledge is the single most important skill for any developer. Without the ability to grow you will find yourself quickly deprecated.</p>
<p>I do expect people to know how to use the language and/or framework they were hired to work in, but I judge them primarily based on the work they submit. A guy who can figure out how to do things that he doesn&#8217;t know how to do, on his own, on the fly, is a real programmer.</p>
<h3>The catch-22 of impossibly high standards</h3>
<p>Of course it&#8217;s pretty absurd to require knowledge that 99% of programmers  have absolutely no need for in real life. But perhaps you don&#8217;t entirely know why. If you have very strict hiring standards then you are only going  to find people who match your specific model, and your code will suffer  because of this.</p>
<p>Computer science grows at a fantastical rate, every day  there is another small innovation that someone came up with. This small innovation will  slowly propegate out to the rest of the programming community and become part of everyone&#8217;s toolbox. This happens because  some creative programmer tried some alternative idea and found a new way to solve a problem.</p>
<p>If your team consists entirely of people with  the same background, skills and knowledge then your creativity bucket  will be quite small. This means you will not see as much innovation as a  varied team with people asking questions that wouldn&#8217;t normally be  asked, and people offering solutions that wouldn&#8217;t normally be offered.</p>
<p>Most alternative idea&#8217;s will, of course, be immediately shot down. But that one in a hundred that actually sticks will give your team an advantage. This alternative idea has given a level of innovation to your project that would not have existed if you didn&#8217;t have a radical element thinking in a unique way.</p>
<h3>Get real</h3>
<p>This was a rather long-winded rant, but let me sum it up in a few nice bullet points.</p>
<ul>
<li>People who are more interested in the buzz words and CS theory than actual experience and a history of practical application of skills are a perpetual thorn in the side of the programming industry.</li>
<li>Don&#8217;t look down on other programmers because they don&#8217;t fit your model definition of a programmer, they just might be better than you.</li>
<li>If 99 out of 100 candidates fail your interview then you are looking for something that doesn&#8217;t exist.</li>
<li>Without a varied group of developers in your team you will suffer from a lack of creativity.</li>
<li>Don&#8217;t test a CS curriculum, test the ability to create.</li>
<li>If you ever finish learning, then your career as a programmer is over. Go study law.</li>
</ul>
<h4>In short</h4>
<p>Look for someone who truly understand the concepts, who can offer creative and alternative ideas, and who shows the ability to grow as a programmer. Then you will finally find a <em>real programmer</em>.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/07/the-5-types-of-programmers/" title="The 5 types of programmers">The 5 types of programmers</a></li><li><a href="http://stevenbenner.com/2010/02/reset-cascading-style-sheets-are-bad-practice/" title="Reset cascading style sheets are bad practice">Reset cascading style sheets are bad practice</a></li><li><a href="http://stevenbenner.com/2010/02/addthis-friendly-widget-and-brilliant-viral-tracking-scheme/" title="AddThis: friendly widget and brilliant viral tracking scheme">AddThis: friendly widget and brilliant viral tracking scheme</a></li><li><a href="http://stevenbenner.com/2010/01/over-use-of-javascript-and-ajax-bad-practice-and-broken-pages/" title="Excessive JavaScript and AJAX: bad practice &#038; broken pages">Excessive JavaScript and AJAX: bad practice &#038; broken pages</a></li><li><a href="http://stevenbenner.com/2009/09/php-needs-an-ide-and-a-membership-provider/" title="PHP needs an IDE and a membership provider">PHP needs an IDE and a membership provider</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/AZIpxSZWCLU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/08/will-the-really-real-programmers-please-stand-up/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/08/will-the-really-real-programmers-please-stand-up/</feedburner:origLink></item>
		<item>
		<title>The 5 types of programmers</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/J9rl5vUpIok/</link>
		<comments>http://stevenbenner.com/2010/07/the-5-types-of-programmers/#comments</comments>
		<pubDate>Sun, 18 Jul 2010 22:11:45 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[Desktop Development]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[Opinion]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=1163</guid>
		<description><![CDATA[In my code journeys and programming adventures I&#8217;ve encountered many strange foes, and even stranger allies. I&#8217;ve identified at least five different kinds of code warriors, some make for wonderful comrades in arms, while others seem to foil my every plan. However they all have their place in the pantheon of software development. Without a [...]]]></description>
			<content:encoded><![CDATA[<p>In my code journeys and programming adventures I&#8217;ve encountered many strange foes, and even stranger allies. I&#8217;ve identified at least five different kinds of code warriors, some make for wonderful comrades in arms, while others seem to foil my every plan.</p>
<p>However they all have their place in the pantheon of software development. Without a healthy mix of these different programming styles you&#8217;ll probably find your projects either take too long to complete, are not stable enough or are too perfect for humans to look upon.</p>
<p><span id="more-1163"></span></p>
<h3>The duct tape programmer</h3>
<p><em><img class="alignright size-thumbnail wp-image-1165" title="Duct Tape" src="http://stevenbenner.com/wp-content/uploads/ducttape-150x150.png" alt="Duct Tape" width="150" height="150" />The code may not be pretty, but damnit, it works!</em></p>
<p>This guy is the foundation of your company. When something goes wrong he will fix it fast and in a way that won&#8217;t break again. Of course he doesn&#8217;t care about how it looks, ease of use, or any of those other <em>trivial</em> concerns, but he will make it happen, without a bunch of talk or time-wasting nonsense. The best way to use this person is to point at a problem and walk away.</p>
<h3>The OCD perfectionist programmer</h3>
<p><em><img class="alignright size-thumbnail wp-image-1169" title="Perfection" src="http://stevenbenner.com/wp-content/uploads/perfection-150x150.png" alt="Perfection" width="150" height="150" />You want to do what to my code?</em></p>
<p>This guy doesn&#8217;t care about your deadlines or budgets, those are insignificant when compared to the art form that is programming. When you do finally receive the finished product you will have no option but submit to the stunning glory and radiant beauty of perfectly formatted, no, perfectly beautiful code, that is so efficient that anything you would want to do to it would do nothing but defame a masterpiece. He is the only one qualified to work on his code.</p>
<h3>The anti-programming programmer</h3>
<p><em><img class="alignright size-thumbnail wp-image-1171" title="Anti-Programming" src="http://stevenbenner.com/wp-content/uploads/antiprogramming-150x150.png" alt="Anti-Programming" width="150" height="150" />I&#8217;m a programmer, damnit. I don&#8217;t write code.</em></p>
<p>His world has one simple truth; writing code is bad. If you have to write something then you&#8217;re doing it wrong. Someone else has already done the work so just use their code. He will tell you how much faster this development practice is, even though he takes as long or longer than the other programmers. But when you get the project it will only be 20 lines of actual code and will be very easy to read. It may not be very fast, efficient, or forward-compatible, but it will be done with the least effort required.</p>
<h3>The half-assed programmer</h3>
<p><em><img class="alignright size-thumbnail wp-image-1173" title="Half-assed" src="http://stevenbenner.com/wp-content/uploads/halfassed-150x150.png" alt="Half-assed" width="150" height="150" />What do you want? It works doesn&#8217;t it?</em></p>
<p>The guy who couldn&#8217;t care less about quality, that&#8217;s someone elses job. He accomplishes the tasks that he&#8217;s asked to do, quickly. You may not like his work, the other programmers hate it, but management and the clients love it. As much pain as he will cause you in the future, he is single-handedly keeping your deadlines so you can&#8217;t scoff at it (no matter how much you want to).</p>
<h3>The theoretical programmer</h3>
<p><em><img class="alignright size-thumbnail wp-image-1174" title="Theoretical" src="http://stevenbenner.com/wp-content/uploads/theoretical-150x150.png" alt="Theoretical" width="150" height="150" />Well, that&#8217;s a possibility, but in practice this might be a better alternative.</em></p>
<p>This guy is more interested the options than what should be done. He will spend 80% of his time staring blankly at his computer thinking up ways to accomplish a task, 15% of his time complaining about unreasonable deadlines, 4% of his time refining the options, and 1% of his time writing code. When you receive the final work it will always be accompanied by the phrase &#8220;if I had more time I could have done this the right way&#8221;.</p>
<h3>Where do you fit?</h3>
<p>Personally, I&#8217;d have to classify myself as the perfectionist. So, which type of programmer are you? Or perhaps you know another programming archetype that is missing from my list? Post a comment below and I&#8217;ll add it to a new updated list.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/08/will-the-really-real-programmers-please-stand-up/" title="Will the really real programmers please stand up?">Will the really real programmers please stand up?</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/J9rl5vUpIok" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/07/the-5-types-of-programmers/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/07/the-5-types-of-programmers/</feedburner:origLink></item>
		<item>
		<title>Setting up an Ubuntu LAMP server. Part 1: Initial setup</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/vhstLYVl1tA/</link>
		<comments>http://stevenbenner.com/2010/06/setting-up-an-ubuntu-lamp-server-part-1-initial-setup/#comments</comments>
		<pubDate>Sun, 27 Jun 2010 23:23:43 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[System Administration]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=1074</guid>
		<description><![CDATA[This will be the first part in a new series of articles about setting up your own Ubuntu LAMP (Linux, Apache, MySQL, PHP) server. If you are already proficient with Linux and/or Ubuntu then you can skip this first part because it is just about the basic post-install set up and configuration. For those of [...]]]></description>
			<content:encoded><![CDATA[<p>This will be the first part in a new series of articles about setting up your own <em>Ubuntu</em> LAMP <em>(Linux, Apache, MySQL, PHP)</em> server. If you are already proficient with Linux and/or Ubuntu then you can  skip this first part because it is just about the basic post-install set up and configuration. For those of us who haven&#8217;t set up an Ubuntu system a  hundred times over, here is the quick and dirty base Ubuntu set up guide.</p>
<p>Once you have your Ubuntu operating system installed there are still a  few things you need to do to get it ready for prime time. A fresh  install of Ubuntu will have a root account (you chose the password  during installation) and the SSH server running. At the moment my recommendation for version is <em>10.04 LTS</em>.</p>
<p>Without further ado, lets get this show on the road. Start by connecting to your system via SSH and logging in to the root  account. (For Windows users I recommend <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/" target="_blank">PuTTY</a> as an SSH client)</p>
<p><span id="more-1074"></span></p>
<h3>Create your account</h3>
<p>The first thing to do is to create your account. Logging in as root  is bad and you should never do it after you create a personal  account. Create a new account with the <em>adduser</em> command.</p>
<pre>adduser steve</pre>
<p>Of course use whatever name you like for your account. It will ask  you for various pieces of account information, all of which is optional except for the  password. Be sure to use a strong password, this is the single most important piece of security for the entire system. If you choose a dictionary word for your password then it is almost inevitable that sooner or later, someone will find it and destroy your server.</p>
<h3>Give your new account sudo access</h3>
<p>The reason you never have to log in as root is because your account  will have super-user access via the <em>sudo</em> command. Sudo will let  you execute other commands at nearly the same level as the root account.  Open the sudo configuration file with the <em>visudo</em> command.</p>
<pre>visudo</pre>
<p>This will open a text file, at the bottom of that file add the  following line (replacing &#8220;steve&#8221; with your account name).</p>
<pre>steve ALL=(ALL) ALL</pre>
<p>Now save the file and exit the text editor. Chances are this file was  opened in <em>nano</em>, so you exit by hitting CTRL+X on your keyboard.  It will ask you if you want to save the changes, say yes by typing &#8220;y&#8221;  and hitting the enter key.</p>
<h3>Configure the SSH server</h3>
<p>We need to add your new account to the SSH server so you can log in  under that name. We will also do a couple minor tweaks to make the server  much more secure. Nothing too advanced, just enough to get your SSH of the easy attack list. Open the SSH server configuration file in <em>nano</em>.</p>
<pre>nano /etc/ssh/sshd_config</pre>
<p>Here are the thing we need to do in this file:</p>
<ul>
<li><strong>Change the port to something other than 22.</strong> If you leave SSH running  on port 22 it will constantly be under attack by botnets and hackers that try to  find SSH logins for every IP on the internet. This little trick will save you from millions of automated hack attempts.</li>
<li><strong>Disable root login.</strong> We do not want anyone (even you) to be able to  SSH in as root. It is a huge security risk because root can do anything to the system.</li>
<li><strong>Disable X11Forwarding.</strong> X11Forwarding is another security risk for  numerous reasons. You simply don&#8217;t need it so disable it.</li>
<li><strong>Add your account to the list of users that can SSH in.</strong> The SSH server will only allow the log in of accounts that are added to the configuration file.</li>
</ul>
<p>First and foremost, lets make sure your new account can log in via SSH. Add the  following line to the file (I usually just add this at the bottom).</p>
<pre>AllowUser steve</pre>
<p>Again, replace &#8220;steve&#8221; with your account name.</p>
<p>Now for the security stuff. Find the following config statements and  update them accordingly:</p>
<pre>Port 12345
PermitRootLogin no
X11Forwarding no</pre>
<p><strong>Remember your port  number!</strong> You will need it to SSH in to  your server again.</p>
<p>Use any port number you like, I would  recommend 4 or 5 digits because port sniffers start at 1 and scan up from there. So a large number will take much longer to find. This will thwart most lazy port sniffers. Do not use any of the standard service ports (e.g. 23, 24, 25, 80, etc). For a list of standard port numbers check out this <a href="http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers" target="_blank">Wikipedia list</a>. Basically any port number greater than 1024 should be okay to use. For better security use something 10000 or above.</p>
<p>Now  reload the SSH server by entering the following command:</p>
<pre>/etc/init.d/ssh reload</pre>
<p>Now  you can logout and log back in using your new account.</p>
<pre>logout</pre>
<p>SSH back in to your server and log in with your new account. You should  now be logged in as yourself in your home directory. For now on when you  need to execute something that requires <em>super-user</em> permissions  you will do so via the <em>sudo</em> command.</p>
<h3>Update all of the installed software</h3>
<p>The next step is to get all of the software on your system up to date. This is one of the best things about running Ubuntu, updates are incredibly easy using the <em>aptitude</em> tool. Frankly, this is even easier than Windows Update, and far more reliable.</p>
<p>Update the packages list with the following command:</p>
<pre>sudo aptitude update</pre>
<p>This grabs all of the latest package information from the Ubuntu servers for everything you have installed.</p>
<p>Note that when you use sudo it will ask for your password. This is just the password for your account login. It will save your sudo session for a few minutes, so if you need to execute several sudo commands it will only ask for your password the first time you run it.</p>
<p>Now you can upgrade everything that needs updates with the <em>safe-upgrade</em> command.</p>
<pre>sudo aptitude safe-upgrade</pre>
<p>It will list all of the packages (programs) that it wants to update and ask if you want to proceed. Enter &#8220;y&#8221; for yes and hit enter. The aptitude program will automatically download and upgrade everything that needs to be updated.</p>
<p>You should do this as often as possible, once or twice a week is more than enough if you&#8217;re anal about keeping everything updated with the latest fixes and security updates.</p>
<h3>Install the base software</h3>
<p>There are a couple software packages that you will almost certainly need at some point, no matter what you do with your server. So let&#8217;s go ahead and install those now.</p>
<p>Lets start with <em>wget</em>, the tool you will use to download files from the web to your server. It&#8217;s just a simple file download tool that you will be using.</p>
<pre>sudo aptitude install wget</pre>
<p>The next item on the list is the <a href="http://packages.ubuntu.com/dapper/build-essential" target="_blank">build-essential</a> package. It includes all of the software you need to build Debian/Ubuntu software packages on your machine. Most of the software you will be using will be downloaded pre-built and auto installed by the aptitude software, but if you need to build Apache modules or any other non-standard pieces of software you will require all of the suff in the <em>build-essential</em> package.</p>
<pre>sudo aptitude install build-essential</pre>
<p>This will download and automatically install everything in this package.</p>
<h3>Useful commands you should know</h3>
<p>Here are some of the most useful commands you should know for managing your system.</p>
<dl>
<dt>sudo !!</dt>
<dd>This is one of the best little tricks to know. When you try to run a command but get a permissions error because you needed to sudo it, then you can run &#8220;sudo !!&#8221; which will execute the last command under sudo. This will save you lots of typing. </dd>
<dt>free -m</dt>
<dd>The free command will display current memory usage information. This will be an invaluable tool for optimizing as well as diagnosing problems. The -m flag tells it to display the numbers in megabytes, instead of just bytes.</dd>
<dt>df -h and du -h</dt>
<dd>The df and du commands report disk usage information. The df (disk free) command reports how much disk space is used and available on each partition. The du (disk usage) command reports how much space all of the files and folders in the current directory are taking. Both commands should be run with the -h flag which tells it we want human-readable (xxxKB, MB, GB) numbers. </dd>
<dt>top</dt>
<dd>Use the top command to find out information about the processes that are currently running. This app shows a constantly refreshed list of the processes with memory utilization, CPU usage, time running and such. It&#8217;s a very useful tool for finding out which processes are taking the most CPU and memory. There are a lot of feature in this one so read the <a href="http://manpages.ubuntu.com/manpages/hardy/man1/top.1.html" target="_blank">man page</a>. Press &#8220;q&#8221; to exit the app. </dd>
<dt>man</dt>
<dd>Of course real men don&#8217;t read instructions, so I don&#8217;t know why I&#8217;m listing this here. Perhaps it&#8217;s all of the times other real men have told me to RTFM. Anyway, man is the manual tool. For example entering &#8220;man top&#8221; will display the manual for the top application.</dd>
</dl>
<h3>Conclusion</h3>
<p>That&#8217;s it. You now have an Ubuntu server running the latest software with the basic best practices for accounts and SSH security. You can go anywhere from here, set up a LAMP server, a mail server, a game server, whatever. Look forward to the next part of my Ubuntu server series where I&#8217;ll discuss installing and configuring the web server.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2009/09/firewall-script-tool-for-iptables/" title="Firewall script tool for iptables">Firewall script tool for iptables</a></li><li><a href="http://stevenbenner.com/2010/05/how-to-circumvent-internet-filtering-and-enhance-privacy/" title="How to circumvent internet filtering and enhance privacy">How to circumvent internet filtering and enhance privacy</a></li><li><a href="http://stevenbenner.com/2010/02/poor-mans-source-code-repository-and-versioning-with-dropbox/" title="Poor man&#8217;s code repository and versioning with Dropbox">Poor man&#8217;s code repository and versioning with Dropbox</a></li><li><a href="http://stevenbenner.com/2010/04/building-a-home-network-attached-storage-server-part-3-wrapping-up-and-product-reviews-2/" title="Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews">Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews</a></li><li><a href="http://stevenbenner.com/2010/01/building-a-home-network-attached-storage-server-part-2-raid-setup/" title="Building a home Network Attached Storage server. Part 2: RAID Setup">Building a home Network Attached Storage server. Part 2: RAID Setup</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/vhstLYVl1tA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/06/setting-up-an-ubuntu-lamp-server-part-1-initial-setup/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/06/setting-up-an-ubuntu-lamp-server-part-1-initial-setup/</feedburner:origLink></item>
		<item>
		<title>Ultimate web site optimization trick: data URIs</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/b_9fVQw-lL8/</link>
		<comments>http://stevenbenner.com/2010/05/ultimate-web-site-optimization-trick-data-uris/#comments</comments>
		<pubDate>Tue, 18 May 2010 04:15:46 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Internet Explorer]]></category>
		<category><![CDATA[Tips and Tricks]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=1012</guid>
		<description><![CDATA[Are you an obsessive compulsive web site optimization nut? Are you willing to sacrifice maintainability for one less HTTP request? Have people ever asked you to seek a mental health professional because of your OCD? If so, then you are in good company. Us optimization nuts may not be very well received when we start [...]]]></description>
			<content:encoded><![CDATA[<p>Are you an obsessive compulsive web site optimization nut? Are you willing to sacrifice maintainability for one less HTTP request? Have people ever asked you to seek a mental health professional because of your OCD? If so, then you are in good company. Us optimization nuts may not be very well received when we start building a project, but we become very appreciated when someone asks how to make it scale. Depending on who you ask, web optimization is either the most useless talent ever or the single most important skill on a web developers resume.</p>
<p>Allow me to broaden your optimization toolbox with the ultimate HTTP connection killer, the <a href="http://en.wikipedia.org/wiki/Data_URI_scheme" target="_blank">data URI scheme</a>. Most web developers have never heard of data URIs but they can dramatically reduce the number of HTTP connections required to download your web site.</p>
<p>This article will explain what data URIs are, how to use them, and how to properly implement them.</p>
<p><span id="more-1012"></span></p>
<h3>What exactly are data URIs?</h3>
<p>The data URI scheme (<em>aka data: URLs</em>) is a method to include data in-line in a URI.</p>
<p>I don&#8217;t suppose that makes much sense, let me elaborate; Simply put, data URIs allow you to include a file (or multiple files) inside of another file. The most obvious and practical use is to embed background images inside a CSS file. You do this by base64 encoding a file and embedding the contents, with some basic meta data, into a URI.</p>
<p>It is important to note that while the data URI scheme was created in 1998 it never reached the status of Standard Protocol. It is still on the list of &#8220;Proposed Standards&#8221;. However, all of the most popular <em>modern</em> browsers have implemented the data URI scheme.</p>
<p>Due to the IE problem (explained further down) data URIs can only be fully supported in CSS files.</p>
<h3>Data URI scheme</h3>
<p>The format is pretty straight forward, the official data URL scheme as defined in the RFC is:</p>
<blockquote><p>data:[&lt;MIME-type&gt;][;charset="&lt;encoding&gt;"][;base64],&lt;data&gt;</p></blockquote>
<p>Okay, let me break that down for you and provide some real world examples.</p>
<p>Like any URI it starts with a protocol identifier, data. Just like http, ftp, mailto or gopher this tells the browser how to use the following information.</p>
<p>The first piece the browser needs to know is the MIME type of the data you are including (e/g image/png or text/html). Over normal HTTP transfers the server identifies the MIME type from the file extension and sends it back with the file in the response headers. Since we are not transferring the data through an HTTP connection there is no automatic MIME-type data, and we have to manually identify the type of data we are giving to the browser.</p>
<p>Next is the charset identifier, which is exactly what it sounds like. If you are passing any kind of text document (e.g. plain text, html, xml) then you should tell the browser what kind of character encoding your are using for that text (e.g. UTF-8 or US-ASCII).</p>
<p>And the last piece of possible meta data is the base64 keyword. This tells the browser that the following data is base64 encoded.</p>
<p>All of this meta data is optional depending on the data you are embedding. If you are passing an image you won&#8217;t include the charset information, likewise if you&#8217;re passing a text document then your probably wont be encoding it and will omit the base64 keyword.</p>
<h3>Useful examples</h3>
<p>Here are a few examples of data URIs at work.</p>
<h4>CSS background</h4>
<p>In the real world, CSS backgrounds are about the only place where data URIs can be used (see: IE problem below). In my opinion this is the single best use for this technique anyway. We can embed all of our icons and backgrounds inside of the very style sheet that defines the rules that use those images.</p>
<pre class="brush: css;">
#logo {
	background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcw[*about 2000 more characters*]AAAABJRU5ErkJggg==');
}
</pre>
<h4>JavaScript</h4>
<p>If you don&#8217;t have to worry about old versions of IE then you can use data URIs anywhere, even in JavaScript for any kind of URL destination. If you need to open a new window with a basic layout but don&#8217;t want to host an otherwise useless file then embed the layout in a data URI.</p>
<pre class="brush: jscript;">
window.open('data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Cht' +
	'ml%20lang%3D%22en%22%3E%0D%0A%3Chead%3E%3Ctitle%3EEmbedded%20Window%3C%' +
	'2Ftitle%3E%3C%2Fhead%3E%0D%0A%3Cbody%3E%3Ch1%3EDATA%3C%2Fh1%3E%3C%2Fbod' +
	'y%3E%0A%3C%2Fhtml%3E%0A%0D%0A', '_blank', 'height=300,width=400');
</pre>
<h4>Embedded images</h4>
<p>If you don&#8217;t need to support IE 6/7 then you can embed an image inside of the image tag itself with a data URI. This probably isn&#8217;t very useful, but it is a good demonstration of the technique.</p>
<pre class="brush: xml;">
&lt;a href=&quot;http://www.linkedin.com/in/stevenbenner&quot; target=&quot;_blank&quot;&gt;
	&lt;img src=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcw[*about 2000 more characters*]AAAABJRU5ErkJggg==&quot; /&gt;
&lt;/a&gt;
</pre>
<h4>Embedded links</h4>
<p>Even Internet Explorer 8 won&#8217;t let you embed data in links because of security considerations, but it is another good demonstration of the technique.</p>
<pre class="brush: xml;">
&lt;a href=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcw[*about 2000 more characters*]AAAABJRU5ErkJggg==&quot;&gt;
	Link to Twitter logo
&lt;/a&gt;
</pre>
<h3>The Internet Explorer problem</h3>
<p>As usual, Microsoft <em>Internet Explorer</em> screws up everything, data URLs are not supported in any version prior to Internet Explorer version 8. As much as I may want to add this to the hate list, I honestly cannot  fault Microsoft for not supporting this until version 8. As I said earlier the data URI scheme is still on the list of <em>Proposed</em> Standards. So the fault rests entirely on the shoulders of the <a href="http://www.w3.org/" target="_blank"><em>W3C</em></a> for not looking at this <a href="http://tools.ietf.org/html/rfc2397" target="_blank">RFC</a> for the last 10 years.</p>
<p>In addition to the problem of old versions, Internet Explorer 8 has some additional restrictions.</p>
<ul>
<li>data must be smaller than 32KB</li>
<li>data URIs cannot be used for JavaScript</li>
<li>only object, img, input and link HTML tags can use data URIs</li>
</ul>
<p>However, you can use data URIs in any CSS url statements, so there is still plenty of use for data URIs in the latest version of Internet Explorer.</p>
<h4>Graceful degradation options for older versions of IE</h4>
<p>There are two options for graceful degradation:</p>
<ul>
<li>Make regular images available and use regular url links as a fall-back.</li>
<li>Or, use the MHTML technique.</li>
</ul>
<p>Personally, I&#8217;m not a fan of the MHTML technique because it will greatly increases the size of your CSS file (doubling the number of embedded images). In my opinion, this is just too high of a price to pay. It is far simpler to make the images available on your server and just link to them with regular CSS url statements. At least it is for me, if for some reason you would rather take the file size hit instead of publishing the image files then there is a good MHTML how-to article <a href="http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-and-under/" target="_blank">here</a>.</p>
<p>For the this article I will use CSS url fall-backs for graceful degradation.</p>
<h3>IE 6-7 specific CSS degradation</h3>
<p>There are two ways to implement your CSS fall-back statements to target IE 6 and 7:</p>
<ul>
<li>Use Internet Explorer revision targeting with conditional comments</li>
<li>Or, use the asterisk CSS hack.</li>
</ul>
<p>Both of these are good choices, with simple pros and cons. IE revision targeting with <em>conditional comments</em> will require an additional CSS file specifically for older versions of IE, but in return it allows you to craft a style sheet that validates. Whereas the asterisk hack saves you the trouble of having an extra file but creates an invalid CSS statement.</p>
<p>Personally, I am anal about writing valid CSS and often have a separate CSS file for IE anyway (for IE PNG fix behavior statement) so I use IE revision targeting statements.</p>
<p>I&#8217;ll leave the choice up to you, so here are examples of both techniques:</p>
<h4>Internet Explorer revision targeting</h4>
<p>I&#8217;m sure you seen this technique countless times before, but here it is again. Internet Explorer supports version targeting statements in HTML comment blocks. These <em>conditional comments</em> are quite smooth because they produce valid markup and guarantee, for all time, that your code will only be parsed by the versions of Internet Explorer that you target.</p>
<p>The following snippet demonstrates how you include a CSS file that will only be seen and downloaded by Internet Explorer versions 7 and older:</p>
<pre class="brush: xml;">
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;base.css&quot; /&gt;
&lt;!--[if lte IE 7]&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;old_ie.css&quot; /&gt;
&lt;![endif]--&gt;
</pre>
<p>In this example base.css is seen by all browsers but old_ie.css is only seen by IE 7 and older. Place this code in your HTML head block and the statements you place in old_ie.css will override the rules from base.css in old versions of Internet Explorer. For example:</p>
<p><strong>base.css</strong></p>
<pre class="brush: css;">
#logo {
	background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcw[*about 2000 more characters*]AAAABJRU5ErkJggg==');
}
</pre>
<p><strong>old_ie.css</strong></p>
<pre class="brush: css;">
#logo {
	background-image: url(/images/logo.gif);
}
</pre>
<p>Now when a modern browser visits the page they will properly download and use the embedded background image but when an old version of IE visits the site it will use the rule from old_ie.css.</p>
<h4>The asterisk hack</h4>
<p>A less well known technique for targeting old versions of IE is the so called <em>Asterisk Hack</em>. This is a nifty little trick that someone, somewhere figured out. If you prepend an asterisk to a CSS rule then that rule will only be processed by Internet Explorer versions 6 and 7.</p>
<p>Here is an example of this hack:</p>
<pre class="brush: css;">
#wrapper {

	/* Processed by all browsers */
	background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAYAAABxcw[*about 2000 more characters*]AAAABJRU5ErkJggg==');

	/* Only seen by IE 6 &amp; 7 */
	*background-image: url('/images/graphic.png');

}
</pre>
<p>Simple! Unfortunately this is invalid CSS, which irks me, but it is a clean and efficient hack. Only IE 6 and 7 will see the second background statement, and since it comes after the default data URI statement, it will override it with a link to the image.</p>
<h3>How to get encoded files for data URIs</h3>
<p>I built a little tool that allows me to quickly encode files on my computer. Here is the download if you would like to use it. This isn&#8217;t my finest work, it is just a basic tool to do the job in 20 lines of code, don&#8217;t expect a lot of bells and whistles.</p>
<p><strong>Steve&#8217;s  Encode64 Utility</strong></p>
<p><img class="aligncenter size-full wp-image-1035" title="Steve's Encode64 Utility" src="http://stevenbenner.com/wp-content/uploads/encode64_screenshot.png" alt="Steve's Encode64 Utility Screen Shot" width="379" height="255" /></p>
<p style="-moz-border-radius:4px;-webkit-border-radius:4px;background-color:#F0F8FF;border:1px solid #CCC;font-size:20px;margin:15px auto;padding:10px;text-align:center;width:300px;;"><strong><a href="http://stevenbenner.com/wp-content/uploads/Steves-Encode64-Utility.exe" onclick="_gaq.push(['_trackEvent', 'Downloads', 'Encode64 Tool']);">Click here to download</a></strong></p>
<ul>
<li>This tool is for almost any version of Microsoft Windows (sorry Mac users).</li>
<li>There is no file size check so be careful. If you try to process a big file it will hang your computer for some time, or lock up entirely. Files less than 32KB (IE8 limit) will be fine.</li>
<li>You can get the source code here: <a href="http://stevenbenner.com/wp-content/uploads/Steves-Encode64-Utility-Src.zip">Steve&#8217;s Encode64 Utility Source Code</a> (VS 2008 C#)</li>
</ul>
<h3>How to generate base64 strings in code</h3>
<p>If you need to dynamically generate base64 strings then you&#8217;re in luck, this task is quite simple. Here are a couple code examples to get you on your way:</p>
<h4>PHP</h4>
<pre class="brush: php;">
$encoded_string = base64_encode(file_get_contents('logo.png'));
</pre>
<h4>C#</h4>
<pre class="brush: csharp;">
private string Base64Encode(string fileToEncode)
{
	byte[] EncodeBuffer;

	using (FileStream fs = new FileStream(fileToEncode, FileMode.Open))
	{
		int Length = Convert.ToInt32(fs.Length);
		EncodeBuffer = new byte[Length];

		fs.Read(EncodeBuffer, 0, Length);
	}

	return Convert.ToBase64String(EncodeBuffer);
}
</pre>
<h3>Conclution</h3>
<p>I think data URIs are an awesome, and powerful optimization trick. You can save many extra HTTP connections, and the associated latency and overhead for all of your users who are running a modern browser. This will make your site much snappier, especially if you use a lot of icons.</p>
<p>Using these techniques you can have a fully compatible web site that will gracefully degrade to traditional external resources if you can accept the following restrictions:</p>
<ul>
<li>Only images</li>
<li>Only in CSS files</li>
<li>Less than 32KB each</li>
</ul>
<p>I can see this working well for background images and for icons. With data URIs you can embed all of your presentation resources into the same CSS file that uses them. This file will grow much larger than normal but it will cache well and save in overall bandwidth.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/01/graceful-degradation-theory-ie-and-css3/" title="Graceful degradation theory, IE  and CSS3">Graceful degradation theory, IE  and CSS3</a></li><li><a href="http://stevenbenner.com/2010/03/javascript-regex-trick-parse-a-query-string-into-an-object/" title="JavaScript regex trick: Parse a query string into an object">JavaScript regex trick: Parse a query string into an object</a></li><li><a href="http://stevenbenner.com/2010/02/optional-parameters-and-named-parameters-in-c-4-0/" title="Optional parameters and named parameters in C# 4.0">Optional parameters and named parameters in C# 4.0</a></li><li><a href="http://stevenbenner.com/2010/02/reset-cascading-style-sheets-are-bad-practice/" title="Reset cascading style sheets are bad practice">Reset cascading style sheets are bad practice</a></li><li><a href="http://stevenbenner.com/2010/02/exslt-set-distinct-in-msxml-for-internet-explorer/" title="EXSLT set:distinct in MSXML for Internet Explorer">EXSLT set:distinct in MSXML for Internet Explorer</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/b_9fVQw-lL8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/05/ultimate-web-site-optimization-trick-data-uris/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/05/ultimate-web-site-optimization-trick-data-uris/</feedburner:origLink></item>
		<item>
		<title>How to circumvent internet filtering and enhance privacy</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/PucKeVBnxkw/</link>
		<comments>http://stevenbenner.com/2010/05/how-to-circumvent-internet-filtering-and-enhance-privacy/#comments</comments>
		<pubDate>Sun, 09 May 2010 23:30:36 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[System Administration]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[Privacy]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=894</guid>
		<description><![CDATA[The whole world knows about the so called &#8220;Great Wall&#8221; that protects the Chinese people from dangerous western ideals, the serious concerns about internet privacy in the United Kingdom, now we have Australia making a real push towards internet filtering legislation as well as all of North America and most of Europe signing on with [...]]]></description>
			<content:encoded><![CDATA[<p>The whole world knows about the so called <em>&#8220;Great Wall&#8221;</em> that protects the Chinese people from dangerous western ideals, the serious concerns about internet privacy in the United Kingdom, now we have Australia making a real push towards <a href="http://www.guardian.co.uk/world/2010/mar/29/google-yahoo-australia-internet-filter" target="_blank">internet filtering legislation</a> as well as all of North America and most of Europe signing on with the <em><a href="http://en.wikipedia.org/wiki/Anti-Counterfeiting_Trade_Agreement" target="_blank">ACTA</a></em> legislation. Of course we still have <a href="http://online.wsj.com/article/SB10001424052748704140104575056972514372994.html" target="_blank">Iran blocking news, social media and even gmail</a>. But even countries that we wouldn&#8217;t expect to support censorship are signing bad legislation, such as <a href="http://www.theinquirer.net/inquirer/news/1591582/france-falls-internet-censorship" target="_blank">France</a>, <a href="http://www.neowin.net/news/main/09/09/14/new-zealand-to-get-country-wide-filtered-internet" target="_blank">New Zealand</a>, and <a href="http://www.irishtimes.com/newspaper/frontpage/2009/0820/1224252952116.html" target="_blank">Ireland</a>.</p>
<p>For at least a decade the very idea of filtering information was something first world countries laughed at as a tactic only dictatorships and xenophobes would seriously consider, now it seems the stage is changing and governments and ISPs are becoming more open to the idea of national censorship.</p>
<p>This is a terrible, and dangerous trend. I for one hope the aussies block the forced internet filtering law and I hope the whole world sees ACTA as the scam that it is. There is a quote that I often like to cite when the topic of internet filtering comes up, <em>&#8220;The internet sees censorship as damage, and automatically routes around it&#8221;</em>. Unfortunately, when censorship happens at the ISP level, that automatic routing is broken, so you have to do some manual routing to circumvent the broken tubes.</p>
<p>I think it is important to know how to get around censorship schemes and I believe that the greater the number of people with this knowledge the better off the internet, and the world will be. So here&#8217;s how to circumvent internet censorship, the <em>VPN</em>.</p>
<p><span id="more-894"></span></p>
<p>In several countries it is illegal to even attempt to circumvent government authorized internet censorship. You can and will go to jail if you get caught bypassing official filters in such countries. As much as I want to promote internet free speech I do not want any of my readers to go to jail. Please read up on your countries laws regarding net filtering and, of  course, I do not advise you to attempt anything illegal.</p>
<p><em>Disclaimer: This site is not intended for distribution to, or use by, any person or entity in any jurisdiction or country where such distribution or use would be contrary to local law or regulation.</em></p>
<h3>VPN tunnel through a country that supports internet freedom</h3>
<p>The idea is simple, do your internet surfing through a <em>VPN</em> tunnel that ends in a country that has a good record of supporting a free internet and protecting privacy (such as Sweden).</p>
<p><img class="aligncenter size-full wp-image-929" title="OpenVPN" src="http://stevenbenner.com/wp-content/uploads/openvpn_logo.png" alt="OpenVPN Logo" width="300" height="74" /></p>
<h3>What is a VPN?</h3>
<p>A VPN (or <em>Virtual Private Network</em>) is a way to securely extend a private network across the internet to another location. The client computer (in this case your computer) makes an encrypted connection to a server which acts as a normal network connection. This technique is commonly used at tech companies to allow employees to connect to their work network from anywhere in the world so they can access files on the corporate network or even open a remote desktop connection to their office computer.</p>
<p>This system is very secure. Since VPN users don&#8217;t want any nefarious types or compromised systems along the way to be able to sniff out what files you are reading or what information you are sending, hence &#8220;Private&#8221;. When a VPN connection is open it is just like having an Ethernet cable connected to the other machine, just slower since it is going over public networks.</p>
<h3>Sounds great! So how do I get a VPN?</h3>
<p><img class="alignright size-full wp-image-931" title="PRQ" src="http://stevenbenner.com/wp-content/uploads/prq_logo.jpg" alt="PRQ Logo" width="110" height="95" />It&#8217;s not free, but it is cheap. There are actually several companies offering this service already, such as <a href="http://prq.se/?intl=1" target="_blank">PRQ</a> which offers an <a href="http://prq.se/?p=tunnel&amp;intl=1" target="_blank">OpenVPN tunnel service</a> for as little as $10 per month. There, your VPN server is all ready to go! I mention PRQ only because they are famous for protecting their customers, for more VPN providers check out the <a href="http://en.cship.org/wiki/Virtual_Private_Network" target="_blank">VPN list</a> on the Internet censorship wiki.</p>
<p>You can also set up a VPN server of your very own anywhere you like, but you will need to find a host to house the system and keep it online. I highly recommend a VPS (<em>Virtual Private Server</em>) system for this because it is cheap, fast and you don&#8217;t need much computing power to run a VPN. Of course a dedicated server is fine too, but a lot more expensive. No matter what you will need a system that gives you root access so you can install and configure the VPN server.</p>
<h3>Alright, I have my VPN server! Now, how do I use it?</h3>
<p>Making a VPN connection is easy with <a href="http://openvpn.net/" target="_blank">OpenVPN</a>, and even easier with a Windows Server VPN. For this article I&#8217;ll assume you are running OpenVPN because a Windows Server VPN would be prohibitively expensive for a home user who just wants to browse the internet in peace. I will also assume you are using the Microsoft Windows operating system (because that&#8217;s what I use). If you are a Mac or Linux user you&#8217;ll find similar guides with a quick Google search.</p>
<p>The first thing you need to do is to download a copy of the <a href="http://openvpn.se/" target="_blank">OpenVPN GUI</a> for Windows. This is a free application and the Installation Package includes everything you need, even the base OpenVPN software.</p>
<p>If you are getting your VPN access from a company that provides OpenVPN services then you should receive all of your configuration information from them. If you&#8217;re setting up your own VPN server then you will need to read the OpenVPN documentation for your particular system.</p>
<p>The guide for setting up your VPN will be the topic of another article, I&#8217;ll write up a nice how-to with screen shots and such eventually. For now, if you&#8217;re looking fo a good walk-through on setting up the OpenVPN GUI check out <a href="http://www.itsatechworld.com/2006/01/29/how-to-configure-openvpn/" target="_blank">How to configure OpenVPN</a>.</p>
<h3>The other option</h3>
<p>I believe that a VPN system like what I have described is the best solution because it is fast and nearly impossible to eavesdrop on, however it isn&#8217;t the only solution.</p>
<p>The simplest option is the good old fashioned <em><a href="http://en.wikipedia.org/wiki/Proxy_server" target="_blank">proxy server</a></em>. A proxy server, as it&#8217;s name implies is a server that bounces internet traffic from your system, through the proxy server, to the destination. Setting up your computer to work with a proxy server is easy, every web browser has a configuration section for proxies. They are free and readily available, however they have some <a href="http://www.sesawe.net/Risks-of-using-web-proxies.html" target="_blank">serious drawbacks</a>. Proxies are notoriously slow, often have injected ads, offer little to no security and do not encrypt the connection.</p>
<p>There are more advanced proxy systems such as <em><a href="http://www.torproject.org/" target="_blank">Tor</a></em> and <em><a href="http://info.picidae.net/" target="_blank">picidae</a></em> that aim to remedy some of those problems. However in general proxies are designed to enhance privacy, not circumvent filtering.</p>
<p>In completely cut off countries there is the <em><a href="http://en.wikipedia.org/wiki/Sneakernet" target="_blank">sneakernet</a></em> phenomena. A sneakernet is literally a network of people who pass media around by hand, such a CDs and flash drives. This is the last line of defense for free speech because it doesn&#8217;t use any computer network to transmit data. However I understand that the latency is quite poor.</p>
<h3>Why ACTA is bad for the internet and you</h3>
<p>Since I&#8217;m American and the only thing really threatening American  internet is the upcoming ACTA treaty. So I feel that I must add a note here about ACTA.</p>
<p>ACTA  (<em>The Anti-Counterfeiting Trade Agreement</em>) is an international  agreement being pushed through many nations. There are some parts of  ACTA that are good, basically the anti-counterfeiting portions, however  most of ACTA is about <em>intellectual property</em>. In fact ACTA, if it  is accepted, will put forth some of the most sweeping international  copyright policies ever made.</p>
<p>Primarily backed by media giant and  organizations such as the infamous RIAA and MPAA, ACTA will force  countries to enact legislation very similar to the American DMCA (<em>Digital  Media Communications Act</em>) except with even more stringent rules and  harsher penalties.</p>
<h4>Quick ACTA facts:</h4>
<ul>
<li>ACTA will require ISPs to permanently sever users internet  connections if they get 3 copyright infringement complaints.</li>
<li>Complaints need not have any evidence nor see a court of any kind,  all it takes is a <em>complaint</em>.</li>
<li>Several sections of the ACTA draft show that rightsholders can  obtain an injunction just by showing that infringement is &#8220;imminent,&#8221;  even if it hasn&#8217;t happened yet. (source: <a href="http://arstechnica.com/tech-policy/news/2010/04/acta-is-here.ars" target="_blank">Ars  Technica</a>)</li>
<li>ACTA will allow border agents to search and seize any equipment  that they believe may contain copies of copyrighted material. (e.g.  laptops, iPods, hard drives, etc.)</li>
<li>ACTA would ban &#8220;the unauthorized circumvention of an effective  technological measure.&#8221; No-CD cracks will become illegal.</li>
</ul>
<p>Basically, ACTA is a very bad thing. It sacrifices a great deal of  your privacy and threatens to get you permanently disconnected from the  internet without any evidence or legal action.</p>
<p>Stop ACTA, support  the EFF and countless human rights organizations.</p>
<h3>Support free speech, privacy and net neutrality</h3>
<p>Now that you have the power to get around censorship I want you to help prevent censorship. Take action, join free speech groups, donate to influential organizations, write your government representatives and vote for candidates who support free speech. It is important to know how to get information even when the powers that be try to stop you, but it is even more important to work with those powers to make the world a better place.</p>
<h4>Support the watchdog organizations</h4>
<p>Here are some influential organizations that are fighting for internet freedom:</p>
<ul>
<li><a href="http://www.eff.org/" target="_blank">Electronic Frontier Foundation (EFF)</a></li>
<li><a href="http://www.cdt.org/" target="_blank">Center for Democracy &amp; Technology (CDT)</a></li>
<li><a href="http://www.aclu.org/free-speech/internet-censorship" target="_blank">American Civil Liberties Union (ACLU)</a></li>
<li><a href="http://www.efa.org.au/" target="_blank">Electronic Frontiers Australia (EFA)</a></li>
<li><a href="http://www.laquadrature.net/en" target="_blank">La Quadrature du Net</a></li>
</ul>
<h4>Learn more</h4>
<p>These are a few informational sites on topics of freedom of speech and internet censorship:</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Internet_censorship" target="_blank">Internet censorship article on Wikipedia</a></li>
<li><a href="http://www.indexoncensorship.org/" target="_blank">Index on Censorship</a> (UK)</li>
<li><a href="http://www.digitaldueprocess.org/" target="_blank">Digital Due Process</a> (US)</li>
<li><a href="http://www.edri.org/" target="_blank">European Digital Rights</a> (EU)</li>
<li><a href="http://www.rsf.org/index.php?page=rubrique&amp;id_rubrique=2" target="_blank">Reporters Without Borders</a></li>
<li><a href="http://opennet.net/" target="_blank">OpenNet Initiative</a></li>
<li><a href="http://www.google.com/governmentrequests/" target="_blank">Google government requests tool</a></li>
</ul>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/06/setting-up-an-ubuntu-lamp-server-part-1-initial-setup/" title="Setting up an Ubuntu LAMP server. Part 1: Initial setup">Setting up an Ubuntu LAMP server. Part 1: Initial setup</a></li><li><a href="http://stevenbenner.com/2009/09/firewall-script-tool-for-iptables/" title="Firewall script tool for iptables">Firewall script tool for iptables</a></li><li><a href="http://stevenbenner.com/2010/04/calculate-page-size-and-view-port-position-in-javascript/" title="Calculate page size and view port position in JavaScript">Calculate page size and view port position in JavaScript</a></li><li><a href="http://stevenbenner.com/2010/04/building-a-home-network-attached-storage-server-part-3-wrapping-up-and-product-reviews-2/" title="Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews">Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews</a></li><li><a href="http://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/" title="Custom link click tracking using Omniture">Custom link click tracking using Omniture</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/PucKeVBnxkw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/05/how-to-circumvent-internet-filtering-and-enhance-privacy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/05/how-to-circumvent-internet-filtering-and-enhance-privacy/</feedburner:origLink></item>
		<item>
		<title>Calculate page size and view port position in JavaScript</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/XnVwea4MW8w/</link>
		<comments>http://stevenbenner.com/2010/04/calculate-page-size-and-view-port-position-in-javascript/#comments</comments>
		<pubDate>Sun, 25 Apr 2010 23:45:50 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=939</guid>
		<description><![CDATA[Have you ever had to try to figure out the dimensions of a page or browser in JavaScript? This task seems very simple at first glace, every browser has simple properties that will give you this information. Unfortunately, not every browser agrees what object these properties belong to, or even what the names are. I&#8217;ve [...]]]></description>
			<content:encoded><![CDATA[<p>Have you ever had to try to figure out the <em>dimensions of a page</em> or browser in <em>JavaScript</em>? This task seems very simple at first glace, every browser has simple properties that will give you this information. Unfortunately, not every browser agrees what object these properties belong to, or even what the names are.</p>
<p>I&#8217;ve built a couple nifty tools in JavaScript that need to know the exact dimensions of the <em>document</em> as well as the exact dimensions of the <em>browser view port</em>. Since these scripts are uses on very high traffic sites with a very wide audience I have had to make sure they work in a multitude of browsers.</p>
<p>In this article I will show you how I support all modern (and not so modern) browsers with one function and I will explain a little about exactly what techniques I&#8217;m using to accomplish this.</p>
<p><span id="more-939"></span></p>
<h3>When do you need to know page and view port dimensions?</h3>
<p>Basically, whenever you are going to dynamically place absolute positioned elements on the document. I have built several JavaScript features that needed to know this information.</p>
<p>One good example is a Lightbox/Thickbox style image viewer. You need to place a box in the exact middle of the <em>view port</em> and have the ability to shrink it down to fit within the view port constraints.</p>
<p>An even better example was a pop up image thumbnail feature. When you put your mouse over certain links a thumbnail pops up to show you a picture or the logo of the link you&#8217;re about to click on. This image follows the mouse around, hanging just below and to the left of the cursor. Since I could never say for sure where the links will be on the page I have to know when the flip the image to the other side of the mouse to keep it from extending past the view port.</p>
<h3>Dimensions needed for placement</h3>
<p><img class="alignright size-full wp-image-952" title="View Port Example" src="http://stevenbenner.com/wp-content/uploads/view_port_example.png" alt="View Port Example" width="210" height="260" />To do any kind of dynamic absolute placement you need to know what your constraints will be. These bounds are generally going to be the browser view port, which is literally the browsers current view of your document, but it is a little more complex than that.</p>
<p>Think of the web browser as a window and the document as a paper underneath it. When you scroll down a page you are simply moving the document up in the view port. This means that if you want to position something based on the view port you will need to consider where the view port is on the page.</p>
<p>Horizontal bounds are simple (assuming you aren&#8217;t dealing with a very small browser), your constraints will be <em>X=0 to X=View Port Width</em>. Vertical bounds are a little more complicated. To compute vertical bounds on a page you have to factor in the current scrolled position of the view port in the document. So your vertical constraints will be <em>Y=View Port Offset to Y=View Port Offset + View Port Height</em>. Simple.</p>
<p>However those formulas are missing one critical piece that most browsers do not give you, scroll bar width. Most browsers consider the scroll bars to be inside of the view port so they are not factored in to the view port dimensions that the browsers will give you. If you create an element and push it in to a scroll bar it will increase the size of the document as the browser renders it, this leads to the &#8220;infinite scrolling phenomena&#8221;. Unfortunately the width of the scroll bars depends on the operating system.</p>
<p>It is basically impossible to compute the size of the scroll bars in such situations, so I picked an arbitrary number that seems to have appropriate padding on every systems that I&#8217;ve tested, 17 pixels.</p>
<p>Taking the 17 pixel padding into consideration our bounding formula now looks like this:</p>
<ul>
<li>X = 0 to X = View Port Width &#8211; 17</li>
<li>Y = View Port Offset to Y = View Port Offset + View Port Width &#8211; 17</li>
</ul>
<h3>The browser properties</h3>
<p>Basically every browser except <em>Internet Explorer</em> agrees where the dimensions information is located. Can&#8217;t say that I&#8217;m particularly surprised, Microsoft has made it their mission to complicate these things.</p>
<p>For a nice visual list of available properties and browser support check out the <a href="http://www.quirksmode.org/dom/w3c_cssom.html" target="_blank">W3C DOM CSS Object Model Compatibility</a> article on QuirksMode. It&#8217;s a very nice list of many useful properties and methods as well as a chart of what browsers support them and how effective their support is.</p>
<h4>View port dimensions</h4>
<p>I found three possible objects that contain the view port size information. In most browsers the <code>self</code> object has the properties of <code>innerWidth</code> and <code>innerHeight</code>. However Internet Explorer has two other objects, IE6 keeps this information in the <code>document.documentElement</code> object and all new IEs use the <code>document.body</code> object. These objects have a <code>clientWidth and <code>clientHeight</code> property.</code></p>
<h4>Document dimensions</h4>
<p>Once again, I found three different ways to get the document measurements. Most browsers agree that this information is stored in the <code>document.body</code> object. However Firefox&#8217;s best measurement of total document height seems to be <code>window.innerHeight + window.scrollMaxY</code>. The rest of the browsers respond to the <code>scrollWidth/scrollHeight</code> or <code>offsetWidth/offsetHeight</code>.</p>
<h4>Scroll offset</h4>
<p>Last but not least is the scroll offset, and again three different possible locations for this information. The general consensus is that scroll offset can be found in the <code>pageYOffset</code> and <code>pageXOffset</code> properties of the <code>self</code> or <code>window</code> objects. However IE uses <code>scrollTop</code> and <code>scrollLeft</code> properties on the <code>document.body</code> or <code>document.documentElement</code> objects.</p>
<h3>Short documents</h3>
<p>One scenario that we need to be able to handle is that of a small document. If the document is smaller than the view port, such as a short page that doesn&#8217;t extend further down past the browser view, then we don&#8217;t want to use the document as our bounding constraints, we want to use the view port.</p>
<p>We do this by adding a simple test to see if if the view port height is greater than the document height and another test to see if the view port width is greater than the document width. In each case if it is greater then use the view port dimension instead of the document dimension.</p>
<h3>The code</h3>
<p>Here is the JavaScript code that I came up with. It is fairly short and simple and uses object detection, because you should always check for objects, never check for browsers.</p>
<p>This code is based on the work posted on QuirksMode some 6 years ago. The article has long since disappeared so I can&#8217;t give a better credit.</p>
<pre class="brush: jscript;">
// Page Size and View Port Dimension Tools
// http://stevenbenner.com/2010/04/calculate-page-size-and-view-port-position-in-javascript/
if (!sb_windowTools) { var sb_windowTools = new Object(); };

sb_windowTools = {
	scrollBarPadding: 17, // padding to assume for scroll bars

	// EXAMPLE METHODS

	// center an element in the viewport
	centerElementOnScreen: function(element) {
		var pageDimensions = this.updateDimensions();
		element.style.top = ((this.pageDimensions.verticalOffset() + this.pageDimensions.windowHeight() / 2) - (this.scrollBarPadding + element.offsetHeight / 2)) + 'px';
		element.style.left = ((this.pageDimensions.windowWidth() / 2) - (this.scrollBarPadding + element.offsetWidth / 2)) + 'px';
		element.style.position = 'absolute';
	},

	// INFORMATION GETTERS

	// load the page size, view port position and vertical scroll offset
	updateDimensions: function() {
		this.updatePageSize();
		this.updateWindowSize();
		this.updateScrollOffset();
	},

	// load page size information
	updatePageSize: function() {
		// document dimensions
		var viewportWidth, viewportHeight;
		if (window.innerHeight &amp;&amp; window.scrollMaxY) {
			viewportWidth = document.body.scrollWidth;
			viewportHeight = window.innerHeight + window.scrollMaxY;
		} else if (document.body.scrollHeight &gt; document.body.offsetHeight) {
			// all but explorer mac
			viewportWidth = document.body.scrollWidth;
			viewportHeight = document.body.scrollHeight;
		} else {
			// explorer mac...would also work in explorer 6 strict, mozilla and safari
			viewportWidth = document.body.offsetWidth;
			viewportHeight = document.body.offsetHeight;
		};
		this.pageSize = {
			viewportWidth: viewportWidth,
			viewportHeight: viewportHeight
		};
	},

	// load window size information
	updateWindowSize: function() {
		// view port dimensions
		var windowWidth, windowHeight;
		if (self.innerHeight) {
			// all except explorer
			windowWidth = self.innerWidth;
			windowHeight = self.innerHeight;
		} else if (document.documentElement &amp;&amp; document.documentElement.clientHeight) {
			// explorer 6 strict mode
			windowWidth = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} else if (document.body) {
			// other explorers
			windowWidth = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		};
		this.windowSize = {
			windowWidth: windowWidth,
			windowHeight: windowHeight
		};
	},

	// load scroll offset information
	updateScrollOffset: function() {
		// viewport vertical scroll offset
		var horizontalOffset, verticalOffset;
		if (self.pageYOffset) {
			horizontalOffset = self.pageXOffset;
			verticalOffset = self.pageYOffset;
		} else if (document.documentElement &amp;&amp; document.documentElement.scrollTop) {
			// Explorer 6 Strict
			horizontalOffset = document.documentElement.scrollLeft;
			verticalOffset = document.documentElement.scrollTop;
		} else if (document.body) {
			// all other Explorers
			horizontalOffset = document.body.scrollLeft;
			verticalOffset = document.body.scrollTop;
		};
		this.scrollOffset = {
			horizontalOffset: horizontalOffset,
			verticalOffset: verticalOffset
		};
	},

	// INFORMATION CONTAINERS

	// raw data containers
	pageSize: {},
	windowSize: {},
	scrollOffset: {},

	// combined dimensions object with bounding logic
	pageDimensions: {
		pageWidth: function() {
			return sb_windowTools.pageSize.viewportWidth &gt; sb_windowTools.windowSize.windowWidth ?
				sb_windowTools.pageSize.viewportWidth :
				sb_windowTools.windowSize.windowWidth;
		},
		pageHeight: function() {
			return sb_windowTools.pageSize.viewportHeight &gt; sb_windowTools.windowSize.windowHeight ?
				sb_windowTools.pageSize.viewportHeight :
				sb_windowTools.windowSize.windowHeight;
		},
		windowWidth: function() {
			return sb_windowTools.windowSize.windowWidth;
		},
		windowHeight: function() {
			return sb_windowTools.windowSize.windowHeight;
		},
		horizontalOffset: function() {
			return sb_windowTools.scrollOffset.horizontalOffset;
		},
		verticalOffset: function() {
			return sb_windowTools.scrollOffset.verticalOffset;
		}
	}
};
</pre>
<p>As you can see this object has several functions that go through all of our object checks and dimension calculations then provides an object with 5 simple properties that have every dimension we need for any kind of absolute placement.</p>
<p>Perhaps this code is more complex that is actually necessary, but I like to make my scripts very portable. I can drop this in anywhere and be able to get exactly what I need out of it.</p>
<h3>Using the code</h3>
<p>I have created a simple <code>centerElementOnScreen</code> function to demonstrate how all this works.</p>
<ul>
<li>The first the you need to do before using any of the dimensions data is to get that data (or update it). You can load all of the dimensions data at once by calling the <code>updateDimensions</code> method.</li>
<li>Once you have called that method all of the information is loaded and made available through the <code>pageDimensions</code> methods. You call these methods directly to retrieve the information. So if you want to know how far the viewer has scrolled down the page you call <code>sb_windowTools.pageDimensions.verticalOffset()</code>. I know it&#8217;s kind of long, but I like descriptive names. Do not forget the parentheses, since I need to perform some logic on the <code>pageHeight</code> and <code>pageWidth</code> properties I had to make all of them functions.</li>
</ul>
<p>Here is an example that will center a test element on the screen when the window loads:</p>
<pre class="brush: jscript;">
window.onload = function() {
	// center the test element on the screen
	sb_windowTools.centerElementOnScreen(document.getElementById('test'));
}
</pre>
<h3>Handling browser scrolling and resizing</h3>
<p>Ideally, you will only need to compute these dimensions once, when you build whatever element you are trying to build. However you may need to handle the browser scrolling or resizing after you have done whatever work you needed to do.</p>
<p>The simplest solution is to call the function again and update your interface whenever the <code>window.onscroll</code> or <code>window.onresize</code> events are fired. I&#8217;ve tried to make this function as efficient as possible for a stand-alone function but it will have to recalculate information that surely hasn&#8217;t changed. Of course there is a more efficient way, but it requires some additional code.</p>
<p>What we can do is add just the needed code to the window events. Here is an example that will update just the scroll offset and reposition the element whenever the user scrolls the page.</p>
<pre class="brush: jscript;">
window.onscroll = function() {
	// update the scroll information
	sb_windowTools.updateScrollOffset();
	// update the vertical position of the element
	var element = document.getElementById('test');
	element.style.top = ((sb_windowTools.pageDimensions.verticalOffset() + sb_windowTools.pageDimensions.windowHeight() / 2) - (sb_windowTools.scrollBarPadding + element.offsetHeight / 2)) + 'px';
};
</pre>
<p>Now, when the user scrolls it will only update the needed information. This is really unnecessary, simply calling the centerElementOnScreen method in the onscroll event is perfectly acceptable. The execution time is so minimal that you really shouldn&#8217;t even bother. But if you are incredibly obsessive about optimizing your JavaScript code then this is the way to do it.</p>
<h3>Working example</h3>
<p>Here is a working example with this code.</p>
<p><script type="text/javascript" src="/misc/sb_windowTools.js"></script></p>
<div style="height:270px;padding:10px;border:1px solid #EEE;">
<div id="test" style="padding:100px;background-color:#CCC;border:1px solid #000;width:300px;">
<input type="button" onclick="centerMe()" value="Center Me!" />
<input type="button" onclick="cancelMe()" value="Cancel" />
<p><small>Click &#8220;Center Me!&#8221; and scroll. Click &#8220;Cancel&#8221; to reset.</small></p>
</div>
</div>
<p>Please note that this is not the best way to keep an element in a fixed position on the page. It&#8217;s just a good example of the code.</p>
<h3>Conclution</h3>
<p>There, I&#8217;ve successfully made a simple task complex and difficult to understand. I hope at least some of this information will be useful to someone out there! If you have any questions or comments please leave them in the form below and I will respond asap.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/" title="Custom link click tracking using Omniture">Custom link click tracking using Omniture</a></li><li><a href="http://stevenbenner.com/2010/01/external-templates-for-the-dataview-control-microsoft-ajax-library-4-0/" title="External templates for the DataView control: Microsoft Ajax Library 4.0">External templates for the DataView control: Microsoft Ajax Library 4.0</a></li><li><a href="http://stevenbenner.com/2010/05/how-to-circumvent-internet-filtering-and-enhance-privacy/" title="How to circumvent internet filtering and enhance privacy">How to circumvent internet filtering and enhance privacy</a></li><li><a href="http://stevenbenner.com/2010/03/microsoft-ajax-4-0-dataview-templates-vs-jtemplates-and-pure/" title="MS Ajax 4.0 DataView Templates vs. jTemplates and PURE">MS Ajax 4.0 DataView Templates vs. jTemplates and PURE</a></li><li><a href="http://stevenbenner.com/2010/03/javascript-regex-trick-parse-a-query-string-into-an-object/" title="JavaScript regex trick: Parse a query string into an object">JavaScript regex trick: Parse a query string into an object</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/XnVwea4MW8w" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/04/calculate-page-size-and-view-port-position-in-javascript/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/04/calculate-page-size-and-view-port-position-in-javascript/</feedburner:origLink></item>
		<item>
		<title>Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/ooPTHwd0i1c/</link>
		<comments>http://stevenbenner.com/2010/04/building-a-home-network-attached-storage-server-part-3-wrapping-up-and-product-reviews-2/#comments</comments>
		<pubDate>Tue, 06 Apr 2010 23:05:15 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[System Administration]]></category>
		<category><![CDATA[NAS]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[Reviews]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=865</guid>
		<description><![CDATA[It&#8217;s taken me a quarter of a year to write this series, partially because I wanted to make sure I got it right, partially because I&#8217;m a lazy writer. But now the wait is over, here is the third and final installment of the NAS series about my home NAS server build. 80MB/sec across the [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s taken me a quarter of a year to write this series, partially because I wanted to make sure I got it right, partially because I&#8217;m a lazy writer. But now the wait is over, here is the third and final installment of the <em><a href="http://stevenbenner.com/tag/nas/">NAS series</a></em> about my home NAS server build.</p>
<div style="text-align: center;">
<p><img class="aligncenter size-full wp-image-869" title="80MB/s Network Throughput!" src="http://stevenbenner.com/wp-content/uploads/nas_transfer.png" alt="80MB/s Network Throughput!" width="416" height="244" /></p>
<p><strong>80MB/sec across the network!</strong></p>
</div>
<p>In this article I will discuss the final garnishing that you need to do to get you NAS to fire and forget server status. I will also give you my reviews of the various pieces of hardware that I used and offer a general wrap up statement on the project.</p>
<p><span id="more-865"></span></p>
<h3>Surprises</h3>
<p>During the course of building and using my NAS I discovered a couple of interesting things that I was not expecting to see.</p>
<ul>
<li><strong>Drives spin down:</strong> I was quite surprised to see that the drives on the ICH10R will power down. This was not something that I was expecting, but was happy to see. Just setting the regular power controls in Windows will allow your system to turn off the hard drives when they haven&#8217;t been used for a while. Doing this will not only save your electric bill, but will also increase the life span of the hard drives.</li>
<li><strong>Full network saturation:</strong> To be honest I was expecting 40-50MB/sec, and I would have been able to live with that. However I often see network transfers moving at 70-80MB/sec! Fully saturating my gigabit network. Of course a lot of this speed is due to the SMB2 performance, so don&#8217;t expect to see these speeds on XP or Linux machines.</li>
<li><strong>Low power consumption:</strong> The whole system only consumes 68 watts when the hard drives are powered down and the server is just sitting idle. I can probably reduce this even further with an undervolt and more aggressive power saving, but it isn&#8217;t worth the trouble. I sleep quite well knowing my NAS is costing me less than that light I left on in the kitchen.</li>
</ul>
<h3>Final tinkering and optimizations</h3>
<p>Before we walk away and call the server done we should take care of a couple small items that will help keep the server up, running and problem free for a very long time.</p>
<ul>
<li>Add a UPS with a decent battery backup time</li>
<li>Disable the swap file</li>
<li>Set up RAID notifications</li>
</ul>
<h3>Add a battery backup</h3>
<p><img class="alignright size-thumbnail wp-image-899" title="APC Back-UPS XS 1300" src="http://stevenbenner.com/wp-content/uploads/APC_Back-UPS_XS_1300-150x150.jpg" alt="APC Back-UPS XS 1300" width="150" height="150" />Running the file server off of a <a href="http://en.wikipedia.org/wiki/Uninterruptible_power_supply" target="_blank">UPS</a> <em>(Uninterruptible Power Supply)</em> is perhaps the single most important thing you can do. I would say this is not only necessary, but absolutely required for this kind of RAID server. If you run your server simply plugged into the wall then you are placing your data at great risk. A risk that can be all but removed by spending $50-$250 on a UPS.</p>
<p>A good battery backup can make the difference between a fully functional system and an instantly corrupted RAID array. If the power on the system were to fail during a parity write then the RAID array could be corrupted, i.e. <em>catastrophic data loss</em>.</p>
<p>This bears repeating:<br />
<strong>Do not run a NAS with a RAID setup like this one without a good UPS!</strong></p>
<p>Really, the choice of UPS isn&#8217;t all that important so long as it has a data connection that Windows Vista has native support for. Any somewhat modern UPS with a USB connection should do fine. You should be able to just plug the UPS USB cable in and Windows will recognize it as a battery. For the record I chose the APC Back-UPS XS 1300 (second generation) because it was a quality UPS that was available locally.</p>
<p>The bigger the battery the better, but you&#8217;re not going for length of run time, you just want a nice thick buffer of power for when there is a brown out or extended black out. I would recommend 1000VA or better, but you could probably get buy even with a 500VA so long as you&#8217;re not running a monitor and a bunch of peripherals. Go as big as your budget allows.</p>
<h4>Configure the system to automatically power down</h4>
<p>You want the server to automatically turn itself off (safely) if you are experiencing an extended black out or brown out. To do this we will configure Windows to shut down when a low battery status is detected. Here is a brief walk-through for Windows Vista:</p>
<ol>
<li>Open up the <em>Power Options</em> from the Windows Control Panel.</li>
<li>Click the <em>&#8220;Change plan settings&#8221;</em> link under your current power plan.</li>
<li>Pick an appropriate time for sleep.</li>
<li>Click the the <em>&#8220;Change advanced power settings link&#8221;</em>.</li>
<li>Under the Battery controls (at the bottom of the list), set both <em>Low battery action</em> and <em>Critical battery action</em> to <em>&#8220;Shut Down&#8221;</em>. Also verify that the Low battery level and Critical battery level setting are appropriate for your UPS.</li>
</ol>
<p>The default battery level settings should be fine for 1000VA or better, but if you&#8217;re running an underpowered battery jack these values up to 70-80% because a small battery will run out much faster and you want to leave ample time for the system to shut down.</p>
<p>That&#8217;s it for settings. Your server should automatically shut down when there is a power failure without any assistance, even if you&#8217;re far away from home. When you are at home you will surely hear your UPS screaming at you if there is a power failure. If you&#8217;re there then go ahead and shut the server down, don&#8217;t take any risks with your precious data (and quiet that loud alarm).</p>
<p>A note on power actions: I believe that hibernate and sleep should also protect your RAID array, but I don&#8217;t know enough about the inner workings of these functions to say for sure. All you are trying to do is to get the drives to finish any current I/O activity and power down safely.</p>
<p>The only time a power failure can damage your system is if it happens during a parity <strong>write</strong>. So if you&#8217;re in the middle of a wind storm and see your lights flickering, do yourself a favor and do not save a bunch of files to the NAS.</p>
<h4>Test it</h4>
<p>Once you&#8217;ve got your UPS charged and running you should do a little test to verify that it is all correctly configured and working properly. Make sure that no one will be writing to the NAS for a while (or just unplug the ethernet), also make sure all of the drives are fully powered up and spinning. Then unplug the UPS from the wall.</p>
<ul>
<li>Verify that it instantly switches to battery power and the system stays up an running.</li>
<li>Verify that the system automatically powers down according to your settings.</li>
</ul>
<h3>Disable the swap file</h3>
<p>We want our hard drives to last as long as possible, the way we do that is by keeping disk I/O to a minimum and powering down the drives when they&#8217;re not needed.</p>
<p>The first thing we can do to lower I/O is to disable the <em>swap file</em> (a.k.a. the <em>paging file</em>). In case you&#8217;re not familiar with this, the swap file is basically on-disk RAM for the operating system. If you have it enabled then Windows is always pinging the drives for the extra memory.</p>
<p>If you&#8217;re running Windows Vista then <strong>you should have at least 4GB of RAM before you disable the swap file</strong>. Without the benefit of virtual memory the system will crash if you max out your RAM, which could put your data at risk. So skip this step if you&#8217;re only running 1 or 2GB of RAM.</p>
<p>Disabling the swap file will not only greatly decrease disk I/O, but it will also improve the performance of your system because it forces Windows to use RAM for its temporary storage, which is about ten-bazillion times faster than hard drives.</p>
<p><strong>To disable the swap file in Windows Vista:</strong></p>
<ol>
<li>Open &#8220;System&#8221; in the control panel</li>
<li>Open &#8220;Advanced system settings&#8221;, on the left menu in the System screen.</li>
<li>Under the &#8220;Advanced&#8221; tab, click the &#8220;Settings&#8221; button in the &#8220;Performance&#8221; section.</li>
<li>Click the &#8220;Advanced&#8221; tab.</li>
<li>Click the &#8220;Change&#8221; button in the &#8220;Virtual memory&#8221; section.</li>
<li>Select the &#8220;No paging file&#8221; option and click &#8220;Set&#8221;.</li>
</ol>
<div id="attachment_875" class="wp-caption aligncenter" style="width: 310px"><a href="http://stevenbenner.com/wp-content/uploads/disable_swap_vista.png"><img class="size-medium wp-image-875" title="Disable the swap file on Windows Vista" src="http://stevenbenner.com/wp-content/uploads/disable_swap_vista-300x141.png" alt="Disable the swap file on Windows Vista" width="300" height="141" /></a><p class="wp-caption-text">Disable the swap file on Windows Vista</p></div>
<p>The screen shot above shows the windows and configuration settings that you want to disable your swap file in the Microsoft Windows Vista operating system.</p>
<h3>Setup RAID notifications</h3>
<p>Something that is woefully missing from the Intel Matrix RAID controller software is notifications. Sure, it will pop a little message on the desktop, but I don&#8217;t spend much time looking at the desktop on a machine that doesn&#8217;t even have a monitor.</p>
<p>No, what you need to have is email notifications, throw on some SMS and maybe a small fog horn for good measure. However for this article I will only cover email notifications and skip the instructions for setting up SMS or a fog horn.</p>
<p>Now as I said, the Intel Matrix software doesn&#8217;t have any way to send notifications, however, it does post events to the <em>Windows event log</em>, and the Windows event log <em>does</em> have the ability to attach actions to log events. So let&#8217;s use this feature which comes with Windows.</p>
<h4>RAID event log information</h4>
<p>Whenever a RAID event occurs a message will be sent to the Windows event log. These events are logged on the <em>Application</em> log as a message from the <em>IAANTmon</em> source. The events that we&#8217;re looking for (RAID events) are given the <em>Warning</em> level. This is all of the information we need to attach an action.</p>
<p>The big thing we are looking for is when a RAID array goes from &#8220;Normal&#8221; status to &#8220;Degraded&#8221; status. This means that a hard drive has failed and the array is in it&#8217;s emergency status, working hard to keep the data available but on the verge of total data loss.</p>
<p>A Degraded notification is logged with an event ID of 7202 (on my system, may be standard but I can&#8217;t say that for sure). Here is an example of the first couple lines of such a notification:</p>
<blockquote><p>Status for volume &#8216;Data&#8217; changed from &#8216;Normal&#8217; to &#8216;Degraded&#8217;.<br />
System Report:-<br />
Intel RAID Controller: Intel(R) ICH8R/ICH9R/ICH10R SATA RAID Controller<br />
Number of Serial ATA ports: 6</p></blockquote>
<p>When you see this message you should be very concerned and do everything possible to replace the failed hard drive. The information that follows this message will give you the details of all of the drives so you can identify which SATA port has the failed drive.</p>
<h4>Attaching actions to Windows events</h4>
<p>Creating a task that will be run whenever this event happens is really easy, it&#8217;s built right in to the event system. It even has a &#8220;Send email&#8221; feature, unfortunately it is really dumb and requires a simple SMTP server which doesn&#8217;t require a log in. If you have such a SMTP server (possibly from your ISP) then you can use this feature and call it done.</p>
<p>If you do not have access to an SMTP server that doesn&#8217;t require authentication then you will need to do find another way to send messages. In my case I created a little program that uses my gmail account. I&#8217;ll post more on this and provide a download in another article.</p>
<h3>Hardware reviews</h3>
<p>I have now logged 3,000 hours of uptime on this machine and feel qualified to review the hardware that I picked. Overall, I am exceedingly happy with my investment, but I still wish I had been able to spend the money on a hardware RAID controller. Not for performance mind you, this system still completely saturates my gigabit network, but for peace of mind, rebuilds take a long time with the ICH10R.</p>
<h4>Antec 1200 computer case</h4>
<p>This case has been wonderful for the NAS server. I love how the hard drives and internals stay cool and clean. Taking the filters out for cleaning is a bit annoying but I can live with that. Overall I&#8217;ve been very satisfied with this case.</p>
<h4>Seagate Barracuda LP ST32000542AS 2TB Hard Drives</h4>
<p>The price that I paid and the vast storage capacity has made these drives a great choice. I purchased six of them and all of them are still working fine to this day. It seems that the reports of DOAs have been greatly exaggerated. Just goes to show, people are 50 times more likely to leave a negative review when something goes wrong then they are to leave a positive review when everything works fine.</p>
<h4>Western Digital Caviar Black hard drives</h4>
<p>Fine drives, they work well, were reasonable priced and are still chugging along. But everyone already knows that WD makes good hard drives.</p>
<h4>Gigabyte EP45-UD3R motherboard</h4>
<p>The first one I got was DOA, it would try to boot, fail and reboot over and over. Apparently this was a common occurrence with these boards. The second one I got worked fine, however two of the CPU pins were slightly bent. I don&#8217;t know if it was something that happened at the factory or during shipping, but they were easily repaired with a pair of needle-nose pliers. Other than those issues the board has been working great.</p>
<h4>APC Back-UPS XS 1300 (second generation)</h4>
<p>APC has a long history of making good UPS systems and supporting them for a very long time. You can probably still purchase replacement batteries for a UPS that you bought from APC 10 years ago.</p>
<p>I chose APC because I have had good luck with them personally, and I chose the second generation Back-UPS XS 1300 because it was a high quality system that I&#8217;ve used before and was available locally. These can be quite heavy so I didn&#8217;t want to pay shipping and I didn&#8217;t want to have to do a mail RMA if I had the bad luck of getting a dead one.</p>
<p>The XS 1300, as it&#8217;s name implies, is a 1300VA UPS, which is more than enough juice for my low-power NAS. I can get well over 30 minutes of battery run time on this unit and it has a nice LCD screen that reports load (wattage and percent) as well as estimated run time. It has AVR <em>(Automatic Voltage Regulation)</em> which is a feature I would recommend. AVR keeps the voltage constant during a brown out which will help protect sensitive (and expensive) computer components. It also looks good, a tall and slim black unit that looks great sitting next to the Antec 1200.</p>
<p>This is a quality unit and I am happy to give it a big thumbs up.</p>
<h3>Conclusion</h3>
<p>This has been a great project and I have received an absolutely amazing file server for my time, effort and money. I leave this machine on 24 hours a day, 365 days a year and use it for all of my file storage. It&#8217;s wicked fast, has massive storage capacity and doesn&#8217;t even seem to hurt my electric bill.</p>
<p>Since I started this project the Intel i5/i7 processors and motherboards have come down in price a lot, but have yet to completely replace the old Core 2 series. If anything the Core 2 series is an even better deal today and I would still recommend building your home NAS on that platform.</p>
<p>I hope that this series of articles will be of some help to anyone out there looking to set up their own file storage solution. Please, if you have any questions leave them in the comment field below and I will answer them to the best of my ability.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/01/building-a-home-network-attached-storage-server-part-2-raid-setup/" title="Building a home Network Attached Storage server. Part 2: RAID Setup">Building a home Network Attached Storage server. Part 2: RAID Setup</a></li><li><a href="http://stevenbenner.com/2009/12/building-a-home-network-attached-storage-server-part-1-hardware/" title="Building a home Network Attached Storage server. Part 1: Hardware">Building a home Network Attached Storage server. Part 1: Hardware</a></li><li><a href="http://stevenbenner.com/2010/06/setting-up-an-ubuntu-lamp-server-part-1-initial-setup/" title="Setting up an Ubuntu LAMP server. Part 1: Initial setup">Setting up an Ubuntu LAMP server. Part 1: Initial setup</a></li><li><a href="http://stevenbenner.com/2010/05/how-to-circumvent-internet-filtering-and-enhance-privacy/" title="How to circumvent internet filtering and enhance privacy">How to circumvent internet filtering and enhance privacy</a></li><li><a href="http://stevenbenner.com/2010/03/microsoft-ajax-4-0-dataview-templates-vs-jtemplates-and-pure/" title="MS Ajax 4.0 DataView Templates vs. jTemplates and PURE">MS Ajax 4.0 DataView Templates vs. jTemplates and PURE</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/ooPTHwd0i1c" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/04/building-a-home-network-attached-storage-server-part-3-wrapping-up-and-product-reviews-2/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/04/building-a-home-network-attached-storage-server-part-3-wrapping-up-and-product-reviews-2/</feedburner:origLink></item>
		<item>
		<title>MS Ajax 4.0 DataView Templates vs. jTemplates and PURE</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/le5vcAuXuGY/</link>
		<comments>http://stevenbenner.com/2010/03/microsoft-ajax-4-0-dataview-templates-vs-jtemplates-and-pure/#comments</comments>
		<pubDate>Sat, 27 Mar 2010 03:00:18 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Reviews]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=319</guid>
		<description><![CDATA[There are many occasions where you&#8217;ll want a JavaScript template system to handle the rendering of data. If you&#8217;re building any kind of JavaScript widget or user interface that will consume JSON data, then build presentation with that data, you really should consider using a JavaScript template engine. It can save a lot of future [...]]]></description>
			<content:encoded><![CDATA[<p>There are many occasions where you&#8217;ll want a JavaScript template system to handle the rendering of data. If you&#8217;re building any kind of JavaScript widget or user interface that will consume JSON data, then build presentation with that data, you really should consider using a JavaScript template engine. It can save a lot of future headaches and make updates a much easier task.</p>
<p>Up until now there has really been only two good option for doing maintainable presentation templates in JavaScript, <em><a href="http://jtemplates.tpython.com/" target="_blank">jTemplates</a></em>, and <em><a href="http://beebole.com/pure/" target="_blank">PURE</a></em>. These two template engines allow you to build layout with data.</p>
<ul>
<li><strong>jTemplates</strong> is a plugin for jQuery, and of course jQuery is by far my favorite JavaScript library, the only one I&#8217;ll ever recommend. jTemplates is an abstraction for data and presentation. It even has its own pseudo-language syntax for the template engine.</li>
<li><strong>PURE</strong> (<em>Pure Unobtrusive Rendering Engine</em>) is a template engine that works with several JavaScript libraries, including jQuery. This system is designed from the ground up to use valid, unobtrusive techniques to build out it&#8217;s templates. PURE templates are normal in-page markup that the JavaScript replaces data in to. A very elegant system indeed.</li>
</ul>
<p>Now there is a new option coming out along with the new Microsoft .NET Framework release.</p>
<ul>
<li>Version 4.0 of the Microsoft Ajax Library (<em>aka ASP.NET AJAX</em>) includes a new client side template system using the <strong>DataView control</strong> which is actually quite good. Frankly, it feels like it was inspired by taking the best of jTemplates and PURE but it is enhanced for ASP.NET developers.</li>
</ul>
<p>I&#8217;ve been playing around with this new system and have been pleasantly surprised. Templates are relatively easy to construct, implementation is straight forward, and it works quite well. There are some pros, and some cons when compared to the other template systems.</p>
<p><span id="more-319"></span></p>
<p>Of course there are actually many other <a href="http://plugins.jquery.com/search/node/templates+type%3Aproject_project" target="_blank">template  plugins</a> for jQuery, however PURE and  jTemplates have the best documentation,  best syntax and the most recent  updates. Almost all of the other template  systems have been abandoned  or deprecated by their creators.</p>
<h3>Comparison table</h3>
<p>Basically all of these template systems have similar features and capabilities. They are all great choices for JavaScript templating, they only really differ in template structure and syntax.</p>
<table border="1" cellspacing="0" cellpadding="10">
<tbody>
<tr>
<td></td>
<th>jTemplates</th>
<th>PURE</th>
<th>MS Ajax DataView</th>
</tr>
<tr>
<td>JSON Data</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>In-page templates</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>External Templates</td>
<td>Yes (as js files)</td>
<td>No (but can be done)</td>
<td>No (but can be done)</td>
</tr>
<tr>
<td>Valid XHTML</td>
<td>Yes (hacky)</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Fast</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>File Size</td>
<td>10kB</td>
<td>9kB</td>
<td>50kB</td>
</tr>
</tbody>
</table>
<p>The Microsoft Ajax Library has some nifty features for power users but that is something  that I will save for another article. Suffice it to say that any  template you care to make can be done in any of these systems.</p>
<h3>Comparison details</h3>
<h4>Supported data</h4>
<p>PURE, jTemplates and the DataView control can only understand and parse JSON data.</p>
<h4>In-page templates</h4>
<p>When I say &#8220;in-page templates&#8221; I am talking about markup in the HTML document that gets evaluated as the template. It may be far more convenient for you to build your template as part of the page that will be using it (or it may not). All of these systems support in-page templates, but they have different ways of accomplishing this technique.</p>
<ul>
<li>jTemplates grabs the template by using the jQuery .html() method to read the contents of a node. Because jTemplates templates are usually not valid markup the common practice is to store them in a <code>&lt;script type="text/html"&gt;</code> tag, which is ignored by browsers. This is actually a fairly dirty work-around when you think about it.</li>
<li>PURE on the other hand actually uses markup conventions to build templates, so the template itself is simply DOM element on the page. When PURE loads it reads the DOM for the target you specified and attempts to insert data according to their conventions.</li>
<li>The Microsoft Ajax DataView control also uses templates that do validate as XHTML. So DataView templates are just regular in-page elements that are hidden with CSS. DataView templates can be a little uglier and a bit more intimidating to look at, but they are valid markup.</li>
</ul>
<p>Of course since the template for PURE and the DataView control are part of the document this also means that crawlers and bad screen readers will see the template as a part of the document.</p>
<h4>External templates</h4>
<p>External templates are separate files that contain the raw template that the script will use. jTemplates will let you store your templates as a JavaScript string. This means that you can simply add another JavaScript file reference to the document and have a separate, cacheable template.</p>
<p>PURE and the DataView control on the other hand must be passed a DOM reference to the template element in the document, so if you want to keep your template file external you will need to use a workaround solution, namely AJAX. This workaround will always be somewhat hacky but does work.</p>
<p>I&#8217;ve already posted an example of this in the <a href="http://stevenbenner.com/2010/01/external-templates-for-the-dataview-control-microsoft-ajax-library-4-0/">external templates for the DataView control</a> article.</p>
<h4>Valid XHTML</h4>
<p>All of these template engines will read and produce valid markup. As I said, jTemplates has to keep it&#8217;s templates inside a script tag, but this is a valid work around. PURE and DataView templates use standard XML structure and are valid in XHTML.</p>
<h4>Speed</h4>
<p>Template engines are by definition are slower than direct rendering. Both jTemplates and DataView hold true to this, PURE seems to be a bit faster, but not by much. However they are more then snappy enough for the real world. It is just a fact of life that you have to sacrifice some amount of performance to gain greatly enhanced maintainability.</p>
<p>Personally I find the trade off to almost always be worth it. If you want single-digit millisecond page rendering you shouldn&#8217;t be using external data on a web page anyway.</p>
<h4>File size</h4>
<p><em>MicrosoftAjaxTemplates.js</em> is notably larger than<em> jquery-templates.js</em> or <em>pure.js</em>, five times larger actually. I can understand this because the DataView control does a lot more work, but this is something that you may want to factor into a decision on a high traffic web site.</p>
<p>Both the DataView control and jTemplates are dependent on their parent libraries, MicrosoftAjax and jQuery, respectively. PURE also requires a base library, but it only requires a CSS selector engine so it supports several libraries, including dojo, DOMAssistant, jQuery, Mootools, prototype.js, Sizzle and Sly.</p>
<p>Another point to factor in is the fact that MicrosoftAjax can be served from Microsoft&#8217;s CDN. So you don&#8217;t have to worry about the bandwidth costs for these libraries. jQuery is also available from Microsoft&#8217;s or Google&#8217;s CDN, however the PURE and jTemplates files you will have to host yourself.</p>
<h3>Conclusion</h3>
<p>The Microsoft Ajax Library 4.0 has a capable template engine that will be very useful to fans of the Microsoft Ajax Library. This is an excellent new tool in the arsenal of ASP.NET web developers, but it won&#8217;t usurp jTemplates&#8217; or PURE&#8217;s position as the predominant JavaScript template engines. If you&#8217;re building a web app on the Microsoft Ajax Library then you should probably be using the DataView control for your templates. Don&#8217;t import all of jQuery on top of Microsoft Ajax just for PURE or jTemplates.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/01/external-templates-for-the-dataview-control-microsoft-ajax-library-4-0/" title="External templates for the DataView control: Microsoft Ajax Library 4.0">External templates for the DataView control: Microsoft Ajax Library 4.0</a></li><li><a href="http://stevenbenner.com/2010/01/over-use-of-javascript-and-ajax-bad-practice-and-broken-pages/" title="Excessive JavaScript and AJAX: bad practice &#038; broken pages">Excessive JavaScript and AJAX: bad practice &#038; broken pages</a></li><li><a href="http://stevenbenner.com/2010/04/calculate-page-size-and-view-port-position-in-javascript/" title="Calculate page size and view port position in JavaScript">Calculate page size and view port position in JavaScript</a></li><li><a href="http://stevenbenner.com/2010/04/building-a-home-network-attached-storage-server-part-3-wrapping-up-and-product-reviews-2/" title="Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews">Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews</a></li><li><a href="http://stevenbenner.com/2010/03/comparison-of-popular-social-bookmarking-widgets/" title="Comparison of popular social bookmarking widgets">Comparison of popular social bookmarking widgets</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/le5vcAuXuGY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/03/microsoft-ajax-4-0-dataview-templates-vs-jtemplates-and-pure/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/03/microsoft-ajax-4-0-dataview-templates-vs-jtemplates-and-pure/</feedburner:origLink></item>
		<item>
		<title>Comparison of popular social bookmarking widgets</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/CTHBRx2JKAY/</link>
		<comments>http://stevenbenner.com/2010/03/comparison-of-popular-social-bookmarking-widgets/#comments</comments>
		<pubDate>Sat, 13 Mar 2010 23:47:06 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[Web Design]]></category>
		<category><![CDATA[Analytics]]></category>
		<category><![CDATA[Reviews]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=680</guid>
		<description><![CDATA[Social bookmarking widgets are absolutely everywhere, you see them on blogs, news, online retailers and even on government web sites. They have become one of the defining features of Web 2.0 design. There are three major players in the social bookmarking widget world, AddThis, ShareThis and AddToAny. These three services together are seen by almost [...]]]></description>
			<content:encoded><![CDATA[<p>Social bookmarking widgets are absolutely everywhere, you see them on blogs, news, online retailers and even on government web sites. They have become one of the defining features of Web 2.0 design.</p>
<p>There are three major players in the social bookmarking widget world, <a href="http://www.addthis.com/" target="_blank">AddThis</a>, <a href="http://sharethis.com/" target="_blank">ShareThis</a> and <a href="http://www.addtoany.com/" target="_blank">AddToAny</a>. These three services together are seen by almost everyone on the internet every single day.</p>
<p>This begs the question, which one is best? As usual it depends on what your needs are and how you will be using it. However I&#8217;ll try to take away the dartboard selection process that you&#8217;re using now by providing some reviews and a performance comparison for these different services.</p>
<p><span id="more-680"></span></p>
<h3>Performance chart</h3>
<p>I started off by creating pages that contain absolutely nothing but the widget. With these completely empty pages I can get a much more accurate reflection on load time. I used Mozilla Firefox with the Firebug, YSlow, PageSpeed and Tamper Data add-on&#8217;s to to determine the number of connections, load time and weight for each widget. These are some of my <a href="http://stevenbenner.com/2010/01/must-have-firefox-add-ons-for-web-developers-designers/">recommended Firefox add-on&#8217;s for all web guys</a>.</p>
<p>All of these tests are using the default button with the default settings for each service. I ran all of these tests from two locations in northern California. One set was run from my home 6Mbps ADSL connection and another set is run from my dual 30Mbps DS3 work connection. The results were basically the same from both locations. Both computers were Intel Qxxxx quad core systems with ample power.</p>
<p>These tests are not necessarily what you would expect average users to see but they offer a good comparison between the performance factors for each service.</p>
<p>The following table contains a breakdown of major points that I tested:</p>
<table border="1">
<tbody>
<tr>
<th></th>
<th>AddThis</th>
<th>ShareThis</th>
<th>AddToAny</th>
<th>Tell-a-Friend</th>
</tr>
<tr>
<td>DNS Requests</td>
<td>2</td>
<td>6</td>
<td>3</td>
<td>2</td>
</tr>
<tr>
<td>Initial HTTP Requests</td>
<td>5</td>
<td>18</td>
<td>6</td>
<td>3</td>
</tr>
<tr>
<td>Initial Weight</td>
<td>20.7KB</td>
<td>82.6KB</td>
<td>20.9KB</td>
<td>12.5KB</td>
</tr>
<tr>
<td>Initial Load Time (avg)</td>
<td>460ms</td>
<td>1.69s</td>
<td>525ms</td>
<td>477ms</td>
</tr>
<tr>
<td>Total HTTP Requests</td>
<td>14</td>
<td>21</td>
<td>8</td>
<td>6</td>
</tr>
<tr>
<td>Total Weight</td>
<td>71.3KB</td>
<td>82.8KB</td>
<td>47.8KB</td>
<td>22.8KB</td>
</tr>
<tr>
<td>Total Load Time (avg)</td>
<td>1.52s</td>
<td>2.34s</td>
<td>538ms</td>
<td>886ms</td>
</tr>
<tr>
<td>Window Load Time (avg)</td>
<td>490ms</td>
<td>460ms</td>
<td>485ms</td>
<td>480ms</td>
</tr>
<tr>
<td>Cookies</td>
<td>10</td>
<td>5</td>
<td>0</td>
<td>0</td>
</tr>
</tbody>
</table>
<p>Let me first take a moment to explain exactly what these values are. The <em>initial</em> items refer to the resources required to just display the widget button on the page, the <em>total</em> requests include the content loaded when you put your  mouse cursor on the widget</p>
<ul>
<li><strong>DNS Requests</strong><br />
The number of specific domains and sub domains that must be resolved to load the widget. Lower is better, but in the real world most users will already have these <acronym title="Domain Name System">DNS</acronym> entries cached in their browsers, especially AddThis.</li>
<li><strong>HTTP Requests</strong><br />
The number of connections and independent client/server interactions that have to be made to load and render the widget. The <em>initial</em> requests will delay loading the rest of your page.</li>
<li><strong>Weight</strong><br />
The download size of all of the scripts, <acronym title="Cascading Style Sheets">CSS</acronym> and images needed to display the content you see on the page. Again, in the real world you can expect much of this to be cached.</li>
<li><strong>Load Time</strong><br />
The average time it takes to connect and download all of the content for the widget.</li>
<li><strong>Window Load Time</strong><br />
The average time it takes before the <code>window.onload</code> event fires on my test pages with no cache. This is not execution time, it is just a point of reference that includes execution time.</li>
<li><strong>Cookies</strong><br />
The number of cookies that the widget and/or its servers will set and force the browser to send back every time it makes an HTTP request. These add to the weight of the widget, but are not counted towards the weight in this test.</li>
</ul>
<p>All of these services fail the YSlow and PageSpeed recommendations in  some way or another. They have to sacrifice some cache performance for the ability to update the code on their end without requiring every publisher in the world to update the code on their site.</p>
<h3>AddThis</h3>
<div id="attachment_696" class="wp-caption alignright" style="width: 162px"><img class="size-full wp-image-696 " title="AddThis Widget" src="http://stevenbenner.com/wp-content/uploads/addthis_widget.png" alt="AddThis Widget" width="152" height="129" /><p class="wp-caption-text">AddThis Widget</p></div>
<p>By far the most popular social bookmarking widget on the internet today, AddThis set&#8217;s the standard that all of the other bookmarking widgets try to live up to. They have the largest user base, a massive collection of supported service, lots of display flexibility and have localized their widget into more than 50 languages. They also support SSL.</p>
<p>I have <a href="http://stevenbenner.com/2010/02/addthis-friendly-widget-and-brilliant-viral-tracking-scheme/">voiced some concerns</a> about AddThis using some black-hat user tracking and profiling tactics. However, since they are so popular this service must get some love in this review.</p>
<h4>Try it</h4>
<p><a class="addthis_button" href="http://addthis.com/bookmark.php?v=250&amp;username=stevenbenner"><img style="border: 0;" src="http://s7.addthis.com/static/btn/v2/lg-share-en.gif" alt="Bookmark and Share" width="125" height="16" /></a><script src="http://s7.addthis.com/js/250/addthis_widget.js#username=stevenbenner" type="text/javascript"></script></p>
<h4>Customization</h4>
<p>The AddThis widget is also the most flexible in terms of layout and design. There are several basic templates and styles to choose from and the JavaScript that runs the widget supports numerous customizations. With a little CSS trickery you can completely control the look of your widget.</p>
<h4>Performance</h4>
<p>This widget scored very well in the performance testing for initial load, with low initial download and only 4 initial HTTP requests. However it has the second largest total download weight when your mouse crosses it with a total of 71KB of script, CSS and images.</p>
<h4>Thoughts</h4>
<p>I do like the look and feel of AddThis and love the extra levels of customization you get with this product. They have good documentation and have good performance. They&#8217;ve recently added clickback analytics which is a very nice feature indeed. Saves you the time of building bit.ly links and integrates click tracking back in to the analytics system.</p>
<p>If you care about your visitors privacy, do not use AddThis, the    primary reason this widget exists it so mine data. They use black-hat    tracking tactics and their privacy policy says that they can   resell  the data to anyone who wants it.</p>
<h3>ShareThis</h3>
<div id="attachment_699" class="wp-caption alignright" style="width: 226px"><img class="size-full wp-image-699 " title="ShareThis Widget" src="http://stevenbenner.com/wp-content/uploads/sharethis_widget.png" alt="ShareThis Widget" width="216" height="157" /><p class="wp-caption-text">ShareThis Widget</p></div>
<p>The second most popular social bookmarking widget out there is ShareThis. The ShareThis widget is not as flexible as the AddThis widget and can be very difficult to customize beyond their widget creator. But it has other strong points.  The ShareThis widget is supported by a service that users can sign up for. People logged into a ShareThis account will see their favorite social sites at the top of the widget. This is a pretty nice feature, for people who decide to sign up.</p>
<h4>Try it</h4>
<p><script src="http://w.sharethis.com/button/sharethis.js#publisher=3e603d49-b061-4b55-8019-a4e380b7da6b&amp;type=website&amp;post_services=email%2Cfacebook%2Ctwitter%2Cdigg%2Cwindows_live%2Cdelicious%2Cstumbleupon%2Creddit%2Cgoogle_bmarks%2Ctechnorati%2Clinkedin%2Cybuzz%2Cmyspace%2Cbebo%2Cblogger%2Cyahoo_bmarks%2Cmixx%2Cfriendfeed%2Cpropeller%2Cwordpress%2Cnewsvine%2Cxanga%2Cslashdot" type="text/javascript"></script></p>
<h4>Customization</h4>
<p>You can customize a couple levels display with a few different display options. You can of course choose and order the social site links that display in the hover view. However since the popup menu is displayed via an <code>iframe</code> there is absolutely no way to change the look and feel other than the very basic controls you get in the widget creator.</p>
<h4>Performance</h4>
<p>Unfortunately ShareThis has done quite poorly in the performance test. It has the largest initial download weight (by far) and the most initial HTTP requests. The initial weight is a whopping 83KB! Most of the initial weight comes from one large sprite and the MooTools JavaScript library. The sprite is fine, a JavaScript library is not. Granted both of these items will cache well, but you should not build a simple little widget on top of a JavaScript library, it&#8217;s a huge dependency that is 95% wasted on this product.</p>
<p>It takes a total of 18 HTTP requests to get the widget if the user does not have it in browser cache. Since it is so popular many people will have it cached but these requests will delay the loading of your page.</p>
<p>What really hurt was the initial load time. I should point out that the load time is skewed by the Google ad services and analytics calls that the script calls. It will render and display before the 1.69 second average initial load time, but since I tested load time not render time I have to factor in the tracking and ad code.</p>
<p>ShareThis sends two logging GET request as well as an AJAX POST request when it opens the sharing dialog, the ShareThis server requests tend to take around 220ms each which hurt the total load time even more. The Google analytics request only takes 45ms, which is they kind of speed they should be shooting for if they want this extra logging.</p>
<p>In my opinion even through the loading time doesn&#8217;t really reflect the render time, they still deserve a bad score because of this unnecessary logging and fat weight. Do you really need two separate logs every time someone happens to mouse over the button? Do you really need any?</p>
<h4>Thoughts</h4>
<p>Aside from their overzealous logging, ShareThis was actually quite a good contender. The widget has a good look and good functionality. However, the bad load time and the lack of customizations hurt.</p>
<h3>AddToAny</h3>
<div id="attachment_700" class="wp-caption alignright" style="width: 207px"><img class="size-full wp-image-700 " title="AddToAny Widget" src="http://stevenbenner.com/wp-content/uploads/addtoany_widget.png" alt="AddToAny Widget" width="197" height="199" /><p class="wp-caption-text">AddToAny Widget</p></div>
<p>The AddToAny brings a couple nifty features to the table that are quite nice, it tracks how many shares a particular page has for each service and shows them in the Share / Save list.</p>
<p>They also have a &#8220;personalization&#8221; feature that displays services that individual visitors actually use up top. This is rather dubious because if they are doing what I think they are doing, then they are exploiting a security hole in the browser. Regardless, it is conceptually a brilliant feature that can be very useful.</p>
<h4>Try it</h4>
<p><a class="a2a_dd" href="http://www.addtoany.com/share_save"><img src="http://static.addtoany.com/buttons/share_save_171_16.png" border="0" alt="Share/Bookmark" width="171" height="16" /></a><script src="http://static.addtoany.com/menu/page.js" type="text/javascript"></script></p>
<h4>Customization</h4>
<p>The button itself is an image tag, so it&#8217;s easy to change that to anything you like. The widget creator lets you change the colors, but that&#8217;s about it. However, since the popup menu is not in a <code>iframe</code> you can control it&#8217;s look with CSS. The markup is well structured and has good ID and class identifiers. Their API offers some additional customization elements as well.</p>
<h4>Performance</h4>
<p>AddToAny did very well in the performance testing. It has the second highest initial weight, but that is due to one huge sprite with every logo imaginable. Its initial load time was right in the middle, not super-fast, not slow. However it had the fastest average total load time.  The performance is fast and it is consistent. I had to give it 3 DNS requests because that&#8217;s how many it takes to load it, however one of those DNS requests is google-analytics.com, which basically every person on the internet will already have cached before they ever reach your site.  Overall, the performance was very good.</p>
<h4>Thoughts</h4>
<p>If you ask me the popup share view is way too big. The bookmark tab is not very useful, in IE it gives you an &#8220;Add to Favorites&#8221; button, but in every other browser it simply says &#8220;Press Ctrl+D or ⌘+D to bookmark this page&#8221;, there isn&#8217;t much point. The search form? Why is it there? Who will search for their social site? If you press that little down-arrow under the social links it will display every social site that has been enabled. With the default settings this is a massive list which can mess with your site layout if you don&#8217;t expect it.</p>
<h3>Tell-a-Friend</h3>
<div id="attachment_701" class="wp-caption alignright" style="width: 138px"><img class="size-full wp-image-701 " title="Tell-a-Friend Widget" src="http://stevenbenner.com/wp-content/uploads/tell-a-friend_widget.png" alt="Tell-a-Friend Widget" width="128" height="106" /><p class="wp-caption-text">Tell-a-Friend Widget</p></div>
<p>The Tell-a-Friend widget from SocialTwist is the least known bookmarking widget in this review, but those who use it tend to like it so I felt they deserved a place in this article. Of all the widgets I&#8217;ve been giving serious consideration to, this is the only one with a pay version. There is of course a free version but the pay version does have several features that power users will want.  It would seem that the primary focus of this widget is branding control for the client. You don&#8217;t get these advanced features in the free version so I cannot give you more details, but they have acquired some rather large clients (Disney, Barnes &amp; Noble, Kraft Foods, etc.) who use the more advanced branding features.</p>
<h4>Try it</h4>
<p><script src="http://cdn.socialtwist.com/2010030436403/script.js" type="text/javascript"></script><a class="st-taf" style="border: 0; padding: 0; margin: 0;" onclick="return false;" href="http://tellafriend.socialtwist.com:80"><img style="border: 0; padding: 0; margin: 0;" onclick="STTAFFUNC.cw(this, {id:'2010030436403', link: window.location, title: document.title });" onmouseover="STTAFFUNC.showHoverMap(this, '2010030436403', window.location, document.title)" onmouseout="STTAFFUNC.hideHoverMap(this)" src="http://images.socialtwist.com/2010030436403/button.png" alt="SocialTwist Tell-a-Friend" /></a></p>
<h4>Customization</h4>
<p>The widget customization is pretty much limited to what you can build in the widget creator. The button itself is an <code>img</code> tag so you can replace that quite easily with any image you want. This widget doesn&#8217;t use an <code>iframe</code> for the hover windows so you can do a little bit with CSS, however the social media icons are inline styles so they cannot be changed.</p>
<p>They offer more customization in the pay version, but I wasn&#8217;t willing to shell out any cash for this test so I can&#8217;t comment on the quality or any additional flexibility.</p>
<p>They chose to go a different direction for the bookmarking and sharing functionality. When you click on an icon to share the widget opens a kind of large modal over the page, instead of simply clicking an image and getting redirected to the share form on the social site. This feature is part of their branding focus which allows paying clients to customize the sharing screen. But it absolutely dominates the window once it opens.</p>
<h4>Performance</h4>
<p>Surprisingly, the Tell-a-Friend widget was the one of the best in the performance comparison. It had the lowest initial download weight and the second fastest average initial load time. The images are served from a different sub domain than the JavaScript, they could literally cut their DNS lookups in half by putting the images and JS on the same CDN sub domain. But I can&#8217;t really complain about 2 DNS lookups. For some reason their script seems to have the longest execution time, average <code>window.onload</code> fires around 100ms later than AddThis or ShareThis.</p>
<p>The performance tests did not take into consideration the modal window. Since you have to take an action to open the window I didn&#8217;t feel it needed to be rated for performance. However I do want to note that it is quite large, with lots of scripts, CSS and images.</p>
<h4>Thoughts</h4>
<p>Tell-a-Friend is the only TRUSTe certified widget. They seem to have the best privacy practices.</p>
<blockquote><p>&#8220;We take user privacy very serious. We do not profile users, store any  personal information. Even the address book feature, others &#8220;import&#8221; we  only let users view.&#8221;</p></blockquote>
<p>Indeed, they don&#8217;t do any cookie based tracking and their low weight and fast  response suggest that they are not doing any profiling on the back end. When it comes to user privacy they seem to be the winners.</p>
<p>I understand why they have a pay system in place, it&#8217;s the  only business model that works for them. Unfortunately their free  implementation feels like they&#8217;ve had to do extra work to reduce the  usefulness of their product. The basic vs detailed analytics, not  offering button customization and not offering modal customization. It  all just feels too forced, especially when their competition is offering  all of those features and more for free.</p>
<p>Personally, I refuse to enter my Twitter login information anywhere other than Twitter or my Twitter desktop application. However if you want to share to twitter via the Tell-a-Friend form you will have to enter your login information. It asks you to login to share for several other services as well. I can see this stopping many users dead in their tracks.</p>
<h3>Features chart</h3>
<p>All of the services that I&#8217;ve tested include support for just about every social media site ever made. Since I haven&#8217;t used every social media site ever made I can&#8217;t comment on the ones they are missing, however I will say that they all support every social media site you care about.</p>
<p>Every service also has it&#8217;s own integrated analytics system. All of these analytics systems are pretty good with pretty much the same metrics (shares, locations, services used, etc) and are easy to navigate. All except for Tell-a-Friend can support Google analytics integration and none except for ShareThis can support Omniture integration.</p>
<table border="1">
<tbody>
<tr>
<th></th>
<th>AddThis</th>
<th>ShareThis</th>
<th>AddToAny</th>
<th>Tell-a-Friend</th>
</tr>
<tr>
<td>Every service you care about</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
</tr>
<tr>
<td>Language Localization</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /> (55)</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /> (5)</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /> (51)</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /> (18)</td>
</tr>
<tr>
<td>Analytics</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
</tr>
<tr>
<td>Google Analytics</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/x.gif" alt="-" width="24" height="24" /></td>
</tr>
<tr>
<td>Omniture Reporting</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/x.gif" alt="-" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/x.gif" alt="-" width="24" height="24" />(?)</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/x.gif" alt="-" width="24" height="24" /></td>
</tr>
<tr>
<td>SSL Support</td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/x.gif" alt="-" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/check.gif" alt="X" width="24" height="24" /></td>
<td><img src="http://stevenbenner.com/wp-content/uploads/x.gif" alt="-" width="24" height="24" /></td>
</tr>
</tbody>
</table>
<p>To be honest I never found any value in having the ability to share SSL pages, but it has been a deciding factor for some people. Really, why is a page that you want to share with everyone behind a secure connection?</p>
<h3>Other mentions</h3>
<ul>
<li><a href="http://www.onlywire.com/" target="_blank">OnlyWire</a> is another social bookmarking widget service that wants to play with the big boys. Unfortunately their free version has ads and I don&#8217;t think anyone will accept ads on their site just for a bookmarking widget. It also blew the performance tests with 42 initial HTTP requests and an initial weight of over 100KB.</li>
<li><a href="http://sociallist.org/" target="_blank">SocialList</a> is a super-simple bookmarking widget. It doesn&#8217;t have analytics or any creation interface and near as I can tell it hasn&#8217;t been updated in over a year. But it is small and simple, so it does deserve a mention.</li>
</ul>
<h3>Winners</h3>
<p>Of course each widget is better in different areas, so I can really only compare them with the performance tests and features.</p>
<h4>Best performance &#8211; AddToAny</h4>
<p>This was a difficult choice. For initial load performance Tell-a-Friend won for having the lightest weight, least HTTP requests and fastest response. But they failed the overall widget performance test because their form system is just so big and heavy. AddToAny wins the best overall performance despite having the heaviest total weight of all the widgets. Most of that weight is from a 26.8KB sprite with every logo ever made. But it caches well and does not seem to hurt performance.</p>
<h4>Most customizable &#8211; AddThis</h4>
<p>AddThis wins the customization contest hands-down. In some ways AddToAny is as good or better, but because of their &#8220;Toolbox&#8221; and list layouts you really can get a very good level of control over how the widget renders. They have more standard buttons and layouts than any of the other services and have more customization features.</p>
<h4>Best end-user privacy &#8211; Tell-a-Friend</h4>
<p>Of course, Tell-a-Friend with their decent privacy policy and lack of profiling wins the privacy competition. Most web masters don&#8217;t concern themselves with matters like this, but internet privacy is something that I consider very important. As much as I want to know who you are, I don&#8217;t presume the right to know everything about you just because you viewed my site. At the same time servers and dark-fiber aint cheap, and I need to make money off of you somehow.</p>
<h3>Do you really need a bookmarking widget?</h3>
<p>Before you go implementing one right this moment; please take a moment to ask yourself, do you really need a social bookmarking <em>widget</em>?</p>
<p>There are literally hundreds of social media sites out there, most of them you have never heard of and neither have any of your visitors. If your site serves a particular niche then it should be easy to identify the social media sites that you need to target. Just put up a couple icons and links. This will make your site load faster and make it easier to follow your presence on those social media sites. If someone wants to link your site on a social media service then not having a widget will not stop them.</p>
<p>If you do need to have an easy link for more than just a few social media sites then perhaps you do need one of these widgets.</p>
<h3>Final thoughts</h3>
<p>Any of these widgets are good choices, depending on your needs. Personally, I think AddThis has the best look and feel, but I hate their <a href="http://stevenbenner.com/2010/02/addthis-friendly-widget-and-brilliant-viral-tracking-scheme/" target="_blank">aggressive tracking strategy</a>. I like the look and functionality of ShareThis but it&#8217;s too fat and users have to register to get the most out of it. AddToAny has good performance and is intuitive, but it is just so big. And  Tell-a-Friend has nice branding tools but their big modal window is a bit cumbersome and it feels neutered unless you pay.</p>
<p>If I had to pick a widget right now it would probably be AddToAny, with a lot of thought given to AddThis.</p>
<p><strong>Update (3/16):</strong> Re-tested all widgets and updated all of the performance numbers. Updated AddToAny customization information. Added thoughts on Tell-a-Friend privacy.</p>
<p><strong>Update (3/19):</strong> Update language localization information. Added SSL support.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/04/building-a-home-network-attached-storage-server-part-3-wrapping-up-and-product-reviews-2/" title="Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews">Building a home Network Attached Storage server. Part 3: Wrapping up and product reviews</a></li><li><a href="http://stevenbenner.com/2010/03/microsoft-ajax-4-0-dataview-templates-vs-jtemplates-and-pure/" title="MS Ajax 4.0 DataView Templates vs. jTemplates and PURE">MS Ajax 4.0 DataView Templates vs. jTemplates and PURE</a></li><li><a href="http://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/" title="Custom link click tracking using Omniture">Custom link click tracking using Omniture</a></li><li><a href="http://stevenbenner.com/2010/02/addthis-friendly-widget-and-brilliant-viral-tracking-scheme/" title="AddThis: friendly widget and brilliant viral tracking scheme">AddThis: friendly widget and brilliant viral tracking scheme</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/CTHBRx2JKAY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/03/comparison-of-popular-social-bookmarking-widgets/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/03/comparison-of-popular-social-bookmarking-widgets/</feedburner:origLink></item>
		<item>
		<title>Custom link click tracking using Omniture</title>
		<link>http://feedproxy.google.com/~r/stevenbenner/~3/DClZT54xC_Y/</link>
		<comments>http://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/#comments</comments>
		<pubDate>Sun, 07 Mar 2010 20:55:28 +0000</pubDate>
		<dc:creator>Steven Benner</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[Analytics]]></category>
		<category><![CDATA[How-To]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://stevenbenner.com/?p=633</guid>
		<description><![CDATA[Omniture is the de facto standard tracking and analytics system that most online retailers use. It has a suite of reporting metrics and allows for custom reporting variables. It is primarily designed for online stores to track usage, conversions and sales. Admittedly, I am not that big a fan of Omniture. Their SiteCatalyst reporting application [...]]]></description>
			<content:encoded><![CDATA[<p>Omniture is the de facto standard tracking and analytics  system that most online retailers use. It has a suite of reporting metrics and allows for custom reporting variables. It is primarily designed for online stores to track usage, conversions and sales.</p>
<p>Admittedly, I am not that big a fan of Omniture. Their <em>SiteCatalyst</em> reporting application is slower than Google Analytics and not as flexible as it could be. My biggest gripe is their tracking JavaScript code, it&#8217;s just plain terrible. It&#8217;s slow, obfuscated (really? why do this?), bloated, impossible to debug and not built using modern practices. Oh, and they&#8217;re also insanely expensive if you want to get at all fancy.</p>
<p>This article is simply a straight how-to for building a custom link tracking JavaScript in the hopes that I can save some other JavaScript developer out there the headaches and tears usually associated with learning advanced Omniture implementations.</p>
<p><span id="more-633"></span></p>
<h3>Omniture basics</h3>
<p>The Omniture JavaScript does all of its work through the <code>s</code> object. This object has many different methods and properties that are all ingeniously designed to confuse and torment would-be implementers.</p>
<p>For custom tracking metrics you will be using <em>prop codes</em>, which are called <em>&#8220;Custom Insights&#8221;</em> in the SiteCatalyst reporting software. You associate a specific prop code to a specific piece of data. So you may associate <code>s.prop1</code> to be the category of a product and <code>s.prop2</code> to be the name of the product.</p>
<p>The way we track is by loading up prop variables with strings then telling the Omniture code to run. This is done by calling <code>s.t()</code> for a pageview track or <code>s.tl()</code> for a link click track.</p>
<h3>How to track a click action with custom prop data</h3>
<p>The code to track an action is actually quite simple, loading prop values and calling the tracking method. Here is what a tracking script looks like. This example is the basic custom click tracking code that you would expect to see in an <em>onclick</em> event attached to an anchor tag.</p>
<pre class="brush: jscript;">
s.linkTrackVars = 'prop1,prop2';
s.linkTrackEvents = 'None';
s.prop1 = 'Televisions';
s.prop2 = 'Samsung - LN40B530 - 40&quot; LCD TV - 1080p (FullHD)';
s.tl(this, 'o');
</pre>
<p>Now, this is fairly self-explanatory, but I&#8217;ll break this down line for line so you know exactly what&#8217;s happening and what these statement mean.</p>
<ul>
<li><strong>s.linkTrackVars</strong> declares which prop codes you will be using in this event. This property is a comma-separated list of each prop code that you want tracked.</li>
<li><strong>s.linkTrackEvents</strong> are the &#8220;events&#8221; that an action may represent. Usually you will use events like &#8220;purchase&#8221; or &#8220;save&#8221; but you can put just about anything you want in here. It&#8217;s just a different way of looking at the same information. I typically set it to none and use custom insights for reports.</li>
<li><strong>s.prop1</strong> and <strong>s.prop2</strong> are the custom metrics that will be attached to this click event. In this case prop1 is the product category and prop2 is the product name.</li>
<li><strong>s.tl(this, &#8216;o&#8217;)</strong> calls the tracking action. The <code>tl()</code> method (a.k.a. the <em>trackLinks method</em>) requires two parameters, this anchor object to be tracked (should always be this) and the type of link we are tracking. There are three types of basic links that is method understands: o is the &#8220;Other/General&#8221; link type, d is a &#8220;File Download&#8221; link, and e which is an &#8220;Exit Link&#8221;. Anything that isn&#8217;t a download or exit link should be given the o tag.</li>
</ul>
<p>There you have it, not incredibly complex, but not obviously simple either. There are a bunch of other properties that you can attach to a trackLinks event, but this is what 99% of them look like.</p>
<h3>Building a script to attach link tracking events automatically</h3>
<p>There are two standard ways to attach these custom link tracking events to the anchors that you want to track: <em>the stupid method</em>, and <em>the smart method</em>.</p>
<ul>
<li><strong>The stupid method</strong> is the most common. This involves adding an onclick tag to every anchor and filling it with the appropriate code.
<ul>
<li>Pros: Stupid, simple, always works</li>
<li>Cons: Stupid, unmaintainable, ugly, increases markup, adds non-semantic markup</li>
</ul>
</li>
<li><strong>The smart method</strong> is to build a single script that will attach the code to every link you want to track automatically on page load.
<ul>
<li>Pros: Smart, not-stupid, simple, maintainable</li>
<li>Cons: Links don&#8217;t get the tracking events attached until the whole page has finished loading.</li>
</ul>
</li>
</ul>
<p>For this rest of this article I will assume that you want to use <em>the smart method</em>. Basically, we will build a script that will look at every single anchor tag in the <acronym title="Document Object Model">DOM</acronym> and attach the click tracking events to any links matching whatever criteria you choose. This script will automatically run when the page loads.</p>
<h3>Defining the criteria and search pattern</h3>
<p>First off, you must be able to identify the links that you want to track. I have used two different systems for this: Searching href tags and adding rel tags.</p>
<p>Searching href tags is generally the best option. This means matching the link href and attaching the event based on that data. However I have found situations where I didn&#8217;t want to track every link that had the string &#8220;products.aspx&#8221; in it, so I chose to add rel tags to those links that I did want to track. This way I could just search for links that had rel=&#8221;product&#8221; in them.</p>
<p>The technique is still basically the same, we will gather all of the anchors in the document into an array, then iterate through the array, attaching the appropriate events and data to those anchor which meet our criteria for each type of page.</p>
<h3>The basic script structure</h3>
<p>No matter what you choose as your search criteria and trackable data you will need a script that has at least the following features:</p>
<ul>
<li>Iterates through all anchors on a page</li>
<li>Allows you to attach tracking events <em>without overriding</em> any existing events</li>
<li>Automatically runs as soon as the page loads</li>
<li>Highly <em>optimized</em> for minimum execution time</li>
<li>Cannot throw an error that will break the page</li>
<li><em>Unobtrusive design</em> that doesn&#8217;t have any dependencies (other than Omniture)</li>
<li>Namespaced in <em>object literal notation</em> so that it can never have any confilcts</li>
</ul>
<h4>The basic JavaScript object that I use for this</h4>
<pre class="brush: jscript;">
// Omniture click tracking
// http://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/

if(!sb_trackLinks) { var sb_trackLinks = new Object(); };

sb_trackLinks = {
	// initialize the click-tracking system and attach events
	init: function() {
		// verify that we are running in a modern browser, if not give up silently
		if (!document.getElementsByTagName) return false;

		// iterate through all of the links in the document
		var anchors = document.getElementsByTagName('a');
		for (var i=anchors.length-1; i&gt;=0; i--) {

			// SEARCH STATEMENTS AND TRACKING VARIABLE BUILDING GOES HERE

		};
	},

	// tracking function
	track: function(obj, propCodes, linkType = 'o') {
		s = s_gi(s_account);
		// reset tracking properties
		s.linkTrackVars = '';
		s.linkTrackEvents = 'None';
		// iterate through each property in propCodes
		for (code in propCodes) {
			s.linkTrackVars += code + ','; // build the CSV list
			s[code] = propCodes[code];     // attach the prop code
		}
		// track it
		s.tl(obj, linkType);
	},

	// non-breaking/non-overriding/cross-browser event attachment
	addEvent: function(obj, evType, fn, useCapture) {
		if (obj.addEventListener) {
			obj.addEventListener(evType, fn, useCapture);
			return true;
		} else if (obj.attachEvent) {
			var r = obj.attachEvent('on' + evType, fn);
			return r;
		} else {
			obj['on' + evType] = fn;
		};
	}
};

// auto-init on load
sb_trackLinks.addEvent(window, 'load', sb_trackLinks.init);
</pre>
<p>This is the basic outline of my JavaScript click tracking object. It has three methods and automatically runs itself in the window.onload event.</p>
<p>I've put a lot of thought into optimizing this script. For instance, notice that I reverse iterate through the anchors array. This technique has been proven to be faster because the array length is only calculated once when the for statement begins. If you do the <code>while i&lt;array.length</code> style then the length is calculated once for every iteration in the loop.</p>
<p>When you build any kind of tracking or analytics JavaScript your highest priority should be to shave every nanosecond from it's operations. It is acceptable to sacrifice readability for performance here. After all, as a web developer your primary job should be to improve <em>user experience</em>, tracking scripts are a necessity but do not benefit users in any way, so you do your best to reduce their impact on user experience.</p>
<h3>The sb_trackLinks methods</h3>
<p>There are three methods in the sb_trackLinks object:</p>
<dl>
<dt>init()</dt>
<dd>The init() method is where the startup, initialization and event attachment will occur. We still need to build the search criteria, tracking variables and event attachment code before this will do anything.</dd>
<dt>track(obj, propCodes, linkType)</dt>
<dd>The track() method is the code that will be attached to the onclick events for the anchors that we want to track. It requires two parameters and will accept a third.</p>
<ul>
<li><strong>obj:</strong> The first parameter is the anchor object that we will be tracking, in the context of our for statement in the <code>init()</code> method this will always be <code>anchors[i]</code>.</li>
<li><strong>propCodes:</strong> The second parameter is an object with the prop codes that we want to use. This should be a standard JavaScript <em>object</em> with the conventions as the <code>s.propXX</code> codes. So if you want to track prop1 you should set <code>propCodes['prop1']</code> to the value that you want and pass it through to the <code>track()</code> method.</li>
<li><strong>linkType:</strong> The third parameter is the link type that will be passed to s.tl() and is optional. It will default to <code>'o'</code>. As I said earlier, it will accept o, d and e as valid values.</li>
</ul>
<p>You may have to modify this function if your Omniture code has been customized.</p>
</dd>
<dt>addEvent(obj, evType, fn, useCapture)</dt>
<dd>The addEvent() method is the standard addEvent that you see everywhere on the internet.</dd>
</dl>
<h3>Crafting the search and tracking variables</h3>
<p>Now that we have a basic structure that we can use, all that we need to build is the anchor search statements, tracking variable and event attachments.</p>
<p>This needs to be custom built for your specific needs. The application, URL structure and tracking needs are all critical parts of this equation. If you are using <a href="http://stevenbenner.com/2010/02/friendly-urls-in-an-asp-net-app/">friendly URLs</a> and need to parse data from them then you will may have to deconstruct the URI to grab the specific data that you want to track in prop codes. If you need data from somewhere else on the page you will have to figure out out to grab it. Etcetera.</p>
<p>I'll provide you with an example based on our previous examples. In this case we are tracking clicks in an online store application. We are using standard query string parameters in our URLs and do not need any other special data.</p>
<p>This is what our <code>for</code> statement will look like for this site:</p>
<pre class="brush: jscript;">
for (var i=anchors.length-1; i&gt;=0; i--) {
	// parse the query string into an object
	var queryString = {};
	anchors[i].href.replace(
		new RegExp(&quot;([^?=&amp;]+)(=([^&amp;]*))?&quot;, &quot;g&quot;),
		function($0, $1, $2, $3) { queryString[$1] = $3; }
	);

	// conditionals to match link href targets
	if (anchors[i].href == 'http://your.domain/' || anchors[i].href.indexOf('home.aspx') &gt; -1) {
		// home page
		sb_trackLinks.addEvent(anchors[i], 'click', (function() {
				return function() {
					try {
						sb_trackLinks.track(
							this,
							{
								'prop1': 'home'
							}
						);
					} catch(e) {}
					return true;
				};
			})()
		);
	} else if (anchors[i].href.indexOf('category.aspx') &gt; -1) {
		// category pages
		sb_trackLinks.addEvent(anchors[i], 'click', (function(catId) {
				return function() {
					try {
						sb_trackLinks.track(
							this,
							{
								'prop1': 'category',
								'prop2': catId
							}
						);
					} catch(e) {}
					return true;
				};
			})(queryString['category_id'])
		);
	} else if (anchors[i].href.indexOf('product.aspx') &gt; -1) {
		// product pages
		sb_trackLinks.addEvent(anchors[i], 'click', (function(catId, productId, productName) {
				return function() {
					try {
						sb_trackLinks.track(
							this,
							{
								'prop1': 'product',
								'prop2': catId,
								'prop3': productId,
								'prop4': productName
							}
						);
					} catch(e) {}
					return true;
				};
			})(queryString['category_id'], queryString['product_id'], queryString['product_name'])
		);
	};
};
</pre>
<p>This is a bit technical, and uses some advanced JavaScript techniques. However, it should be fairly understandable if you know your way around JavaScript.</p>
<p>Since we need to grab query string values for most of our tracked pages I start off by <a title="JavaScript regex trick: Parse a query string into an object" href="http://stevenbenner.com/2010/03/javascript-regex-trick-parse-a-query-string-into-an-object/">parsing the query string into an object</a>.</p>
<p>I then proceed down a list of <code>if</code> statements that try to match a specific page. Inside each <code>if</code> statement I attach an onclick event to the current anchor. That event passes our current data down through the JavaScript scope via a <code>(function(v){})(data)</code> statement. Inside that event I call the <code>track()</code> method with the data and return true so that the browser follows the link once the code as been executed.</p>
<p>This can get very long indeed if you have a lot of different  types of pages in your web application that need click tracking. As  always, order your if statements by commonality, putting the most  commonly matched statements first. The script engine will have to fail  every if check to get to the last statement. This minor optimization may  save you a millisecond or two.</p>
<p>It is very important that you <strong>wrap all of the code inside the event in a <code>try{}catch(e){}</code></strong> just in case something somewhere breaks. If you do not do this then the links on your site will stop working if anything goes wrong. There is nothing quite as asinine as links not working because the tracking script is broken.</p>
<h3>Putting it all together</h3>
<p>Now that we've built the search and attach code all we have to do is insert it into our base script. The result looks like this:</p>
<pre class="brush: jscript;">
// Omniture click tracking
// http://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/

if(!sb_trackLinks) { var sb_trackLinks = new Object(); };

sb_trackLinks = {
	// initialize the click-tracking system and attach events
	init: function() {
		// verify that we are running in a modern browser, if not give up silently
		if (!document.getElementsByTagName) return false;

		// iterate through all of the links in the document
		var anchors = document.getElementsByTagName('a');
		for (var i=anchors.length-1; i&gt;=0; i--) {
			// parse the query string into an object
			var queryString = {};
			anchors[i].href.replace(
				new RegExp(&quot;([^?=&amp;]+)(=([^&amp;]*))?&quot;, &quot;g&quot;),
				function($0, $1, $2, $3) { queryString[$1] = $3; }
			);

			// conditionals to match link href targets
			if (anchors[i].href == 'http://your.domain/' || anchors[i].href.indexOf('home.aspx') &gt; -1) {
				// home page
				sb_trackLinks.addEvent(anchors[i], 'click', (function() {
						return function() {
							try {
								sb_trackLinks.track(
									this,
									{
										'prop1': 'home'
									}
								);
							} catch(e) {}
							return true;
						};
					})()
				);
			} else if (anchors[i].href.indexOf('category.aspx') &gt; -1) {
				// category pages
				sb_trackLinks.addEvent(anchors[i], 'click', (function(catId) {
						return function() {
							try {
								sb_trackLinks.track(
									this,
									{
										'prop1': 'category',
										'prop2': catId
									}
								);
							} catch(e) {}
							return true;
						};
					})(queryString['category_id'])
				);
			} else if (anchors[i].href.indexOf('product.aspx') &gt; -1) {
				// product pages
				sb_trackLinks.addEvent(anchors[i], 'click', (function(catId, productId, productName) {
						return function() {
							try {
								sb_trackLinks.track(
									this,
									{
										'prop1': 'product',
										'prop2': catId,
										'prop3': productId,
										'prop4': productName
									}
								);
							} catch(e) {}
							return true;
						};
					})(queryString['category_id'], queryString['product_id'], queryString['product_name'])
				);
			};
		};
	},

	// tracking function
	track: function(obj, propCodes, linkType = 'o') {
		s = s_gi(s_account);
		// reset tracking properties
		s.linkTrackVars = '';
		s.linkTrackEvents = 'None';
		// iterate through each property in propCodes
		for (code in propCodes) {
			s.linkTrackVars += code + ','; // build the CSV list
			s[code] = propCodes[code];     // attach the prop code
		}
		// track it
		s.tl(obj, linkType);
	},

	// non-breaking/non-overriding/cross-browser event attachment
	addEvent: function(obj, evType, fn, useCapture) {
		if (obj.addEventListener) {
			obj.addEventListener(evType, fn, useCapture);
			return true;
		} else if (obj.attachEvent) {
			var r = obj.attachEvent('on' + evType, fn);
			return r;
		} else {
			obj['on' + evType] = fn;
		};
	}
};

// auto-init on load
sb_trackLinks.addEvent(window, 'load', sb_trackLinks.init);
</pre>
<p>Save this code in the s_code.js (or whatever you named it) script for your Omniture-enabled web site and it will start sending click data back to your Omniture account.</p>
<h3>Gotchas</h3>
<p>There are some possible problems that may crop up using this system.</p>
<ul>
<li><strong>Page never stops loading</strong><br />
I have seen pages that simply never stop loading because of poor use of AJAX, slow servers, or broken dependencies. The click tracking events will not be attached to links until the loading icon in the browser stops.</li>
<li><strong>Links added after page load</strong><br />
If there are any links that are created after the page load event is fired, such as AJAX widgets or dynamic links, then they will not exist when this script is run and therefor will not have any tracking events attached to them.</li>
<li><strong>Omniture conflicts</strong><br />
There have been several occasions where I've seen people try to use two (or more) instances of Omniture on the same page. If you have to deal with this then you need to modify the <code>track()</code> method to use your account name.</li>
<li><strong>500ms link delay</strong><br />
The <code>s.tl()</code> function has a half-second delay built into it. This is because the tracking data is sent by requesting a 1 pixel web beacon from the Omniture servers and the script needs to give the browser enough time to send the request. Unfortunately, this also adds an annoying delay to every link that you attach tracking code to.</li>
</ul>
<h3>Advanced tracking (more than just links)</h3>
<p>Omniture link tracking can be used for more than just links. You can use the <code>s.tl()</code> method to send any data at any time for any reason. Do you want to track when someone puts their mouse over an image, or how long they waited before scrolling, or even what text they selected? You can do all of that, and more if you <em>really</em> want to.</p>
<p>If you ask me those fine-grain <em>viewer eyeball focus</em> type of reports are not only invasive but genuinely useless. However, I've been asked to do even worse invasive and pointless tracking before. If you need to you can send any data you want on any DOM event, or even with timers.</p>
<p>The only thing that you must overcome is that the object you pass to <code>s.tl()</code> must be an anchor, or more specifically, <em>something</em> with an href attribute. There is nothing about this in the documentation, but it simply doesn't seem to work unless it has an anchor with an href passed to it. You can get around this by creating an element or simply passing <code>true</code>.</p>
<p>This is, however, the subject of a different article.</p>
<h3>Conclusion</h3>
<p>As you can see it's not <em>that</em> difficult to implement custom link tracking with Omniture and you can get some very powerful reports without that much work. I still cannot give my glowing stamp of approval for Omniture as a reporting solution, for various reasons, but if you are using it then hopefully this tutorial will save you some time and headaches.</p>
<p>If you have any questions or comments please leave them in the form below and I will do my best to answer them. You can also take a look at the <a href="http://blogs.omniture.com/2009/03/12/custom-link-tracking-capturing-user-actions/" target="_blank">Custom Link Tracking</a> post in the Omniture blog. It's a year old, but the author is still responding to questions.</p>
<h3  class="related_post_title">Related articles</h3><ul class="related_post"><li><a href="http://stevenbenner.com/2010/04/calculate-page-size-and-view-port-position-in-javascript/" title="Calculate page size and view port position in JavaScript">Calculate page size and view port position in JavaScript</a></li><li><a href="http://stevenbenner.com/2010/02/addthis-friendly-widget-and-brilliant-viral-tracking-scheme/" title="AddThis: friendly widget and brilliant viral tracking scheme">AddThis: friendly widget and brilliant viral tracking scheme</a></li><li><a href="http://stevenbenner.com/2010/01/external-templates-for-the-dataview-control-microsoft-ajax-library-4-0/" title="External templates for the DataView control: Microsoft Ajax Library 4.0">External templates for the DataView control: Microsoft Ajax Library 4.0</a></li><li><a href="http://stevenbenner.com/2010/05/how-to-circumvent-internet-filtering-and-enhance-privacy/" title="How to circumvent internet filtering and enhance privacy">How to circumvent internet filtering and enhance privacy</a></li><li><a href="http://stevenbenner.com/2010/03/microsoft-ajax-4-0-dataview-templates-vs-jtemplates-and-pure/" title="MS Ajax 4.0 DataView Templates vs. jTemplates and PURE">MS Ajax 4.0 DataView Templates vs. jTemplates and PURE</a></li></ul><img src="http://feeds.feedburner.com/~r/stevenbenner/~4/DClZT54xC_Y" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		<feedburner:origLink>http://stevenbenner.com/2010/03/custom-link-click-tracking-using-omniture/</feedburner:origLink></item>
	</channel>
</rss>
