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

<channel>
	<title>Chase B. Gale</title>
	<atom:link href="http://blog.chasegale.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.chasegale.com</link>
	<description>Conquering Mountains With Technology</description>
	<lastBuildDate>Thu, 16 Feb 2012 01:25:42 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Introducing: Rocketcharts, Open-source HTML5 Financial/Statistical Charts</title>
		<link>http://blog.chasegale.com/introducing-rocketcharts-open-source-html5-financialstatistical-charts/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=introducing-rocketcharts-open-source-html5-financialstatistical-charts</link>
		<comments>http://blog.chasegale.com/introducing-rocketcharts-open-source-html5-financialstatistical-charts/#comments</comments>
		<pubDate>Fri, 21 Oct 2011 15:01:00 +0000</pubDate>
		<dc:creator>chasegale</dc:creator>
				<category><![CDATA[Rocketcharts]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://blog.chasegale.com/?p=57</guid>
		<description><![CDATA[// Utilizing HTML5&#8242;s canvas, I present to you my newest open-source project: Rocketcharts. I am following the &#8220;release early, release often&#8221; mantra for this project, so I&#8217;d keep it out of production code for the moment. Speaking of code, how did I generate the above interactive chart? &#160; &#60;script type="text/javascript" src="http://www.rocketcharts.com/rocketcharts.js">&#60;/script> &#60;script type="text/javascript" src="http://www.rocketcharts.com/js/jquery-1.5.1.min.js">&#60;/script> &#60;script [...]]]></description>
			<content:encoded><![CDATA[<p><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js" type="text/javascript"></script><br />
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script><br />
<script type="text/javascript" src="http://www.rocketcharts.com/rocketcharts.js"></script></p>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/themes/base/jquery-ui.css" type="text/css" media="all" />
<link rel="stylesheet" href="http://www.rocketcharts.com/style/dev.css" type="text/css" media="all" />
<script type="text/javascript">// <![CDATA[
 $(document).ready(function () {    
var style = new Object();
style.UpColor = {r: 0, g: 255, b: 0};
style.DownColor = {r: 255, g: 0, b: 0};
var settings = new Object();
settings.customUI = true;
settings.backgroundColor = "#000000";
var googData = [{date: "11/9/2010",open: 17.22,high: 17.6,low: 16.86,close: 16.97,volume: 56218900},
{date: "11/10/2010",open: 17,high: 17.01,low: 16.75,close: 16.94,volume: 17012600},
{date: "11/11/2010",open: 16.63,high: 16.86,low: 16.52,close: 16.8,volume: 15310600},
{date: "11/12/2010",open: 16.65,high: 16.75,low: 16.4,close: 16.55,volume: 17703400},
{date: "11/15/2010",open: 16.56,high: 16.89,low: 16.33,close: 16.6,volume: 18934600},
{date: "11/16/2010",open: 16.45,high: 16.49,low: 16.1,close: 16.24,volume: 23484100},
{date: "11/17/2010",open: 16.21,high: 16.33,low: 16.11,close: 16.15,volume: 10305800},
{date: "11/18/2010",open: 16.4,high: 17.17,low: 16.29,close: 16.99,volume: 46500100},
{date: "11/19/2010",open: 16.97,high: 16.97,low: 16.52,close: 16.57,volume: 24036200},
{date: "11/22/2010",open: 16.43,high: 16.65,low: 16.25,close: 16.56,volume: 14316900},
{date: "11/23/2010",open: 16.34,high: 16.43,low: 16.04,close: 16.19,volume: 22437900},
{date: "11/24/2010",open: 16.31,high: 16.48,low: 16.15,close: 16.41,volume: 11561700},
{date: "11/26/2010",open: 16.25,high: 16.4,low: 16.22,close: 16.22,volume: 4953900},
{date: "11/29/2010",open: 16.1,high: 16.45,low: 15.95,close: 16.38,volume: 14653000},
{date: "11/30/2010",open: 16.2,high: 16.34,low: 15.77,close: 15.82,volume: 24981100},
{date: "12/1/2010",open: 16,high: 16.4,low: 16,close: 16.15,volume: 17435900},
{date: "12/2/2010",open: 16.2,high: 16.41,low: 16.12,close: 16.33,volume: 13167300},
{date: "12/3/2010",open: 16.27,high: 16.37,low: 16.2,close: 16.35,volume: 9228000},
{date: "12/6/2010",open: 16.47,high: 16.6,low: 16.3,close: 16.33,volume: 12063800},
{date: "12/7/2010",open: 16.5,high: 17.07,low: 16.5,close: 16.94,volume: 29056400},
{date: "12/8/2010",open: 17.01,high: 17.22,low: 16.96,close: 17.02,volume: 21773300},
{date: "12/9/2010",open: 17.12,high: 17.19,low: 16.8,close: 16.95,volume: 8673300},
{date: "12/10/2010",open: 16.97,high: 17.05,low: 16.91,close: 17.01,volume: 8985300},
{date: "12/13/2010",open: 16.9,high: 16.99,low: 16.69,close: 16.7,volume: 12755400},
{date: "12/14/2010",open: 16.77,high: 16.84,low: 16.57,close: 16.63,volume: 11429500},
{date: "12/15/2010",open: 16.55,high: 16.73,low: 16.42,close: 16.45,volume: 10944200},
{date: "12/16/2010",open: 16.45,high: 16.7,low: 16.44,close: 16.51,volume: 12940500},
{date: "12/17/2010",open: 16.51,high: 16.66,low: 16.32,close: 16.38,volume: 24896100},
{date: "12/20/2010",open: 16.38,high: 16.42,low: 16.15,close: 16.28,volume: 17566400},
{date: "12/21/2010",open: 16.31,high: 16.68,low: 16.2,close: 16.6,volume: 11394700},
{date: "12/22/2010",open: 16.67,high: 16.78,low: 16.56,close: 16.63,volume: 6767500},
{date: "12/23/2010",open: 16.56,high: 16.73,low: 16.45,close: 16.72,volume: 8889200},
{date: "12/27/2010",open: 16.62,high: 16.63,low: 16.4,close: 16.48,volume: 7492300},
{date: "12/28/2010",open: 16.47,high: 16.54,low: 16.33,close: 16.43,volume: 8389100},
{date: "12/29/2010",open: 16.5,high: 16.77,low: 16.43,close: 16.61,volume: 7668600},
{date: "12/30/2010",open: 16.6,high: 16.77,low: 16.52,close: 16.76,volume: 8318900},
{date: "12/31/2010",open: 16.74,high: 16.76,low: 16.47,close: 16.63,volume: 7754500},
{date: "1/3/2011",open: 16.81,high: 16.94,low: 16.67,close: 16.75,volume: 17684000},
{date: "1/4/2011",open: 16.71,high: 16.83,low: 16.57,close: 16.59,volume: 11092800},
{date: "1/5/2011",open: 16.55,high: 16.91,low: 16.34,close: 16.91,volume: 23447700},
{date: "1/6/2011",open: 16.9,high: 17.34,low: 16.77,close: 17.06,volume: 30656800},
{date: "1/7/2011",open: 17.03,high: 17.17,low: 16.65,close: 16.9,volume: 19869500},
{date: "1/10/2011",open: 16.78,high: 16.8,low: 16.5,close: 16.6,volume: 16176700},
{date: "1/11/2011",open: 16.7,high: 16.73,low: 16.53,close: 16.58,volume: 14615700},
{date: "1/12/2011",open: 16.71,high: 16.81,low: 16.59,close: 16.65,volume: 15066200},
{date: "1/13/2011",open: 16.64,high: 16.92,low: 16.57,close: 16.75,volume: 15961000},
{date: "1/14/2011",open: 16.67,high: 16.83,low: 16.6,close: 16.81,volume: 13593500},
{date: "1/18/2011",open: 16.62,high: 16.68,low: 16.42,close: 16.5,volume: 21392500},
{date: "1/19/2011",open: 16.49,high: 16.55,low: 16.23,close: 16.31,volume: 17130000},
{date: "1/20/2011",open: 16.29,high: 16.33,low: 16.09,close: 16.23,volume: 14622700},
{date: "1/21/2011",open: 16.27,high: 16.31,low: 15.93,close: 15.97,volume: 23366200},
{date: "1/24/2011",open: 16,high: 16.24,low: 15.76,close: 16.09,volume: 23375300},
{date: "1/25/2011",open: 16.17,high: 16.19,low: 15.85,close: 16.02,volume: 26673100},
{date: "1/26/2011",open: 15.93,high: 16.05,low: 15.41,close: 15.57,volume: 49690800},
{date: "1/27/2011",open: 15.58,high: 16.36,low: 15.58,close: 16.2,volume: 39067000},
{date: "1/28/2011",open: 16.15,high: 16.21,low: 15.68,close: 15.83,volume: 24734000},
{date: "1/31/2011",open: 15.82,high: 16.2,low: 15.79,close: 16.12,volume: 22911400},
{date: "2/1/2011",open: 16.33,high: 16.46,low: 16.23,close: 16.38,volume: 26938900},
{date: "2/2/2011",open: 16.25,high: 16.66,low: 16.25,close: 16.57,volume: 21106800},
{date: "2/3/2011",open: 16.48,high: 16.91,low: 16.4,close: 16.69,volume: 33314600},
{date: "2/4/2011",open: 16.74,high: 16.91,low: 16.45,close: 16.79,volume: 19127900},
{date: "2/7/2011",open: 16.81,high: 17,low: 16.77,close: 16.8,volume: 16046500},
{date: "2/8/2011",open: 16.83,high: 16.85,low: 16.48,close: 16.6,volume: 17932000},
{date: "2/9/2011",open: 16.54,high: 16.7,low: 16.35,close: 16.43,volume: 17778700},
{date: "2/10/2011",open: 16.39,high: 16.72,low: 16.35,close: 16.62,volume: 15430500},
{date: "2/11/2011",open: 16.58,high: 16.87,low: 16.54,close: 16.85,volume: 15386300},
{date: "2/14/2011",open: 16.84,high: 16.93,low: 16.72,close: 16.89,volume: 14503000},
{date: "2/15/2011",open: 16.8,high: 17.39,low: 16.78,close: 17.2,volume: 31395200},
{date: "2/16/2011",open: 17.23,high: 17.82,low: 17.21,close: 17.76,volume: 41824100},
{date: "2/17/2011",open: 17.75,high: 17.82,low: 17.5,close: 17.77,volume: 23566600},
{date: "2/18/2011",open: 17.69,high: 17.84,low: 17.57,close: 17.66,volume: 13729900},
{date: "2/22/2011",open: 17.08,high: 17.39,low: 16.87,close: 16.91,volume: 34759500},
{date: "2/23/2011",open: 17.03,high: 17.1,low: 16.35,close: 16.58,volume: 35225100},
{date: "2/24/2011",open: 16.66,high: 16.73,low: 16.04,close: 16.37,volume: 31570400},
{date: "2/25/2011",open: 16.39,high: 16.77,low: 16.38,close: 16.5,volume: 16939600},
{date: "2/28/2011",open: 16.37,high: 16.6,low: 16.28,close: 16.4,volume: 20210300},
{date: "3/1/2011",open: 16.46,high: 16.49,low: 16.08,close: 16.1,volume: 16702800},
{date: "3/2/2011",open: 16.65,high: 16.85,low: 16.6,close: 16.63,volume: 24521100},
{date: "3/3/2011",open: 16.85,high: 17.05,low: 16.76,close: 16.86,volume: 35202100},
{date: "3/4/2011",open: 16.75,high: 17.2,low: 16.72,close: 17.08,volume: 20274200},
{date: "3/7/2011",open: 17.07,high: 17.15,low: 16.49,close: 16.7,volume: 18770800},
{date: "3/8/2011",open: 16.74,high: 17.02,low: 16.72,close: 16.94,volume: 12717200},
{date: "3/9/2011",open: 16.89,high: 17.7,low: 16.85,close: 17.65,volume: 33798000},
{date: "3/10/2011",open: 17.3,high: 17.39,low: 16.93,close: 17.06,volume: 25659700},
{date: "3/11/2011",open: 17,high: 17.54,low: 17,close: 17.42,volume: 19454900},
{date: "3/14/2011",open: 17.24,high: 17.44,low: 17.09,close: 17.31,volume: 21615500},
{date: "3/15/2011",open: 16.66,high: 16.68,low: 16.04,close: 16.33,volume: 51489300},
{date: "3/16/2011",open: 16.33,high: 16.48,low: 15.85,close: 15.91,volume: 38378500},
{date: "3/17/2011",open: 16.16,high: 16.42,low: 15.81,close: 15.86,volume: 37548800},
{date: "3/18/2011",open: 16.1,high: 16.19,low: 16.01,close: 16.03,volume: 26660400},
{date: "3/21/2011",open: 16.18,high: 16.5,low: 16.16,close: 16.29,volume: 20613700},
{date: "3/22/2011",open: 16.29,high: 16.48,low: 16.16,close: 16.36,volume: 30692400},
{date: "3/23/2011",open: 16.3,high: 16.34,low: 15.98,close: 16.13,volume: 30842500},
{date: "3/24/2011",open: 16.19,high: 16.91,low: 16.17,close: 16.83,volume: 20120300},
{date: "3/25/2011",open: 16.94,high: 17.05,low: 16.7,close: 16.96,volume: 21047200},
{date: "3/28/2011",open: 17.01,high: 17.06,low: 16.58,close: 16.58,volume: 16066700},
{date: "3/29/2011",open: 16.6,high: 16.78,low: 16.53,close: 16.75,volume: 10037900},
{date: "3/30/2011",open: 16.83,high: 16.92,low: 16.68,close: 16.74,volume: 12944600},
{date: "3/31/2011",open: 16.71,high: 16.88,low: 16.65,close: 16.68,volume: 15131500},
{date: "4/1/2011",open: 16.83,high: 16.98,low: 16.72,close: 16.84,volume: 12487400},
{date: "4/4/2011",open: 16.9,high: 17.05,low: 16.81,close: 16.87,volume: 9560800},
{date: "4/5/2011",open: 16.81,high: 17.29,low: 16.79,close: 17.11,volume: 18464500},
{date: "4/6/2011",open: 17.17,high: 17.2,low: 16.94,close: 17.05,volume: 13298700},
{date: "4/7/2011",open: 16.91,high: 17.1,low: 16.79,close: 17,volume: 12778700},
{date: "4/8/2011",open: 17.08,high: 17.11,low: 16.77,close: 16.77,volume: 13114200},
{date: "4/11/2011",open: 16.91,high: 16.96,low: 16.37,close: 16.59,volume: 34841900},
{date: "4/12/2011",open: 16.55,high: 16.64,low: 16.29,close: 16.36,volume: 19783600},
{date: "4/13/2011",open: 16.43,high: 16.69,low: 16.43,close: 16.64,volume: 16700400},
{date: "4/14/2011",open: 16.55,high: 16.82,low: 16.43,close: 16.69,volume: 16595500},
{date: "4/15/2011",open: 16.64,high: 16.78,low: 16.54,close: 16.62,volume: 14756500},
{date: "4/18/2011",open: 16.35,high: 16.44,low: 16.06,close: 16.35,volume: 21935700},
{date: "4/19/2011",open: 16.21,high: 16.36,low: 16.08,close: 16.12,volume: 31547400},
{date: "4/20/2011",open: 16.7,high: 17.23,low: 16.59,close: 16.87,volume: 34310400},
{date: "4/21/2011",open: 16.93,high: 16.94,low: 16.74,close: 16.85,volume: 13985200},
{date: "4/25/2011",open: 17.01,high: 17.31,low: 16.9,close: 17.11,volume: 17771500},
{date: "4/26/2011",open: 17.11,high: 17.37,low: 17.02,close: 17.28,volume: 20062300},
{date: "4/27/2011",open: 17.3,high: 17.43,low: 17.18,close: 17.26,volume: 16659400},
{date: "4/28/2011",open: 17.22,high: 17.53,low: 17.17,close: 17.51,volume: 14414700},
{date: "4/29/2011",open: 17.46,high: 17.77,low: 17.36,close: 17.7,volume: 30800000},
{date: "5/2/2011",open: 17.79,high: 18.35,low: 17.57,close: 18.14,volume: 44030600},
{date: "5/3/2011",open: 18.23,high: 18.64,low: 17.88,close: 17.92,volume: 32622700},
{date: "5/4/2011",open: 17.99,high: 18.38,low: 17.96,close: 18.2,volume: 23584900},
{date: "5/5/2011",open: 18.12,high: 18.56,low: 18.05,close: 18.43,volume: 30788400},
{date: "5/6/2011",open: 18.59,high: 18.8,low: 18.38,close: 18.65,volume: 29690800},
{date: "5/9/2011",open: 18.6,high: 18.84,low: 18.54,close: 18.56,volume: 15595600},
{date: "5/10/2011",open: 18.67,high: 18.7,low: 18.42,close: 18.55,volume: 18507100},
{date: "5/11/2011",open: 18.45,high: 18.61,low: 16.74,close: 17.2,volume: 131183400},
{date: "5/12/2011",open: 17.12,high: 17.81,low: 16.93,close: 17.17,volume: 52300000},
{date: "5/13/2011",open: 16.14,high: 16.84,low: 15.96,close: 16.55,volume: 120036300},
{date: "5/16/2011",open: 16.68,high: 16.69,low: 15.63,close: 15.81,volume: 62082200},
{date: "5/17/2011",open: 15.88,high: 16.07,low: 15.73,close: 16,volume: 31205200},
{date: "5/18/2011",open: 16.07,high: 16.16,low: 15.75,close: 15.96,volume: 25649600},
{date: "5/19/2011",open: 16.05,high: 16.49,low: 16.04,close: 16.35,volume: 40356400},
{date: "5/20/2011",open: 16.32,high: 16.44,low: 16.15,close: 16.3,volume: 23492200},
{date: "5/23/2011",open: 16.05,high: 16.17,low: 16,close: 16.06,volume: 19250500},
{date: "5/24/2011",open: 16.11,high: 16.41,low: 16.05,close: 16.14,volume: 23150600},
{date: "5/25/2011",open: 16.19,high: 16.99,low: 16.1,close: 16.15,volume: 34166900},
{date: "5/26/2011",open: 16.18,high: 16.22,low: 15.88,close: 15.98,volume: 23999500},
{date: "5/27/2011",open: 16.03,high: 16.19,low: 15.95,close: 16.02,volume: 20091200},
{date: "5/31/2011",open: 16.17,high: 16.59,low: 16.12,close: 16.55,volume: 30248700},
{date: "6/1/2011",open: 16.34,high: 16.43,low: 15.79,close: 15.85,volume: 40282000},
{date: "6/2/2011",open: 16,high: 16.11,low: 15.87,close: 16.02,volume: 21005000},
{date: "6/3/2011",open: 15.82,high: 16,low: 15.63,close: 15.68,volume: 22230500},
{date: "6/6/2011",open: 15.65,high: 15.85,low: 15.41,close: 15.45,volume: 18200400},
{date: "6/7/2011",open: 15.54,high: 15.65,low: 15.32,close: 15.45,volume: 16516100},
{date: "6/8/2011",open: 15.37,high: 15.38,low: 15.08,close: 15.1,volume: 21986600},
{date: "6/9/2011",open: 15.18,high: 15.33,low: 14.94,close: 15.22,volume: 18681900},
{date: "6/10/2011",open: 15.27,high: 15.73,low: 15.11,close: 15.2,volume: 19452400},
{date: "6/13/2011",open: 15.2,high: 15.34,low: 15.1,close: 15.16,volume: 14581200},
{date: "6/14/2011",open: 15.26,high: 15.56,low: 15.19,close: 15.2,volume: 21994400},
{date: "6/15/2011",open: 15.01,high: 15.05,low: 14.5,close: 14.81,volume: 41286100},
{date: "6/16/2011",open: 15.01,high: 15.09,low: 14.65,close: 14.78,volume: 24446700},
{date: "6/17/2011",open: 14.98,high: 14.98,low: 14.56,close: 14.7,volume: 22963400},
{date: "6/20/2011",open: 14.66,high: 15.42,low: 14.66,close: 14.99,volume: 32646500},
{date: "6/21/2011",open: 15.03,high: 15.38,low: 14.91,close: 15.35,volume: 17507800},
{date: "6/22/2011",open: 15.29,high: 15.53,low: 15.19,close: 15.23,volume: 30081100},
{date: "6/23/2011",open: 15.08,high: 15.09,low: 14.72,close: 15.08,volume: 32494700},
{date: "6/24/2011",open: 15.08,high: 15.16,low: 14.85,close: 14.89,volume: 25340600},
{date: "6/27/2011",open: 14.87,high: 14.98,low: 14.77,close: 14.88,volume: 13836300},
{date: "6/28/2011",open: 14.95,high: 15.18,low: 14.88,close: 14.95,volume: 16056600},
{date: "6/29/2011",open: 14.96,high: 15.05,low: 14.68,close: 14.89,volume: 25465200},
{date: "6/30/2011",open: 14.98,high: 15.1,low: 14.64,close: 15.04,volume: 34905700},
{date: "7/1/2011",open: 15.08,high: 15.5,low: 15.02,close: 15.45,volume: 16272500},
{date: "7/5/2011",open: 15.4,high: 15.67,low: 15.25,close: 15.49,volume: 20481700},
{date: "7/6/2011",open: 15.53,high: 15.81,low: 15.52,close: 15.72,volume: 18287200},
{date: "7/7/2011",open: 15.78,high: 15.95,low: 15.7,close: 15.81,volume: 20991400},
{date: "7/8/2011",open: 15.62,high: 15.69,low: 15.44,close: 15.61,volume: 14364900},
{date: "7/11/2011",open: 15.43,high: 15.44,low: 14.99,close: 15.05,volume: 21486700},
{date: "7/12/2011",open: 15.01,high: 15.18,low: 14.85,close: 14.86,volume: 22791100},
{date: "7/13/2011",open: 15.01,high: 15.1,low: 14.87,close: 14.91,volume: 16646100},
{date: "7/14/2011",open: 14.88,high: 14.99,low: 14.6,close: 14.63,volume: 27078600},
{date: "7/15/2011",open: 14.75,high: 14.94,low: 14.61,close: 14.69,volume: 19745100},
{date: "7/18/2011",open: 14.68,high: 14.69,low: 14.37,close: 14.42,volume: 24493400},
{date: "7/19/2011",open: 14.57,high: 14.69,low: 14.45,close: 14.59,volume: 30132600},
{date: "7/20/2011",open: 14.15,high: 14.15,low: 13.45,close: 13.48,volume: 63065600},
{date: "7/21/2011",open: 13.5,high: 13.63,low: 13.36,close: 13.59,volume: 30487100},
{date: "7/22/2011",open: 13.65,high: 14.05,low: 13.57,close: 13.98,volume: 30144800},
{date: "7/25/2011",open: 13.84,high: 13.88,low: 13.68,close: 13.69,volume: 16725400},
{date: "7/26/2011",open: 13.7,high: 13.99,low: 13.65,close: 13.94,volume: 20934200},
{date: "7/27/2011",open: 13.87,high: 13.9,low: 13.57,close: 13.59,volume: 20559500},
{date: "7/28/2011",open: 13.6,high: 13.71,low: 13.43,close: 13.5,volume: 20621700},
{date: "7/29/2011",open: 13.89,high: 14.07,low: 13.04,close: 13.1,volume: 67673900},
{date: "8/1/2011",open: 13.24,high: 13.34,low: 12.95,close: 13.1,volume: 26859400},
{date: "8/2/2011",open: 12.96,high: 13.18,low: 12.75,close: 12.76,volume: 25656100},
{date: "8/3/2011",open: 12.77,high: 13.07,low: 12.53,close: 13.02,volume: 26161900},
{date: "8/4/2011",open: 12.8,high: 12.86,low: 11.99,close: 12,volume: 39428900},
{date: "8/5/2011",open: 12.08,high: 12.12,low: 11.41,close: 11.74,volume: 47066200},
{date: "8/8/2011",open: 11.43,high: 11.8,low: 11.09,close: 11.09,volume: 59560200},
{date: "8/9/2011",open: 11.3,high: 12.09,low: 11.25,close: 12.09,volume: 47484100},
{date: "8/10/2011",open: 11.77,high: 12.14,low: 11.62,close: 11.77,volume: 48027400},
{date: "8/11/2011",open: 11.89,high: 12.92,low: 11.88,close: 12.86,volume: 51098800},
{date: "8/12/2011",open: 12.81,high: 13.62,low: 12.76,close: 13.59,volume: 48472500},
{date: "8/15/2011",open: 13.63,high: 13.69,low: 13.27,close: 13.47,volume: 25682800},
{date: "8/16/2011",open: 13.34,high: 13.57,low: 13.18,close: 13.48,volume: 25581900},
{date: "8/17/2011",open: 13.49,high: 13.62,low: 13.32,close: 13.47,volume: 17006500},
{date: "8/18/2011",open: 13.02,high: 13.09,low: 12.8,close: 12.96,volume: 30447700},
{date: "8/19/2011",open: 12.75,high: 13.08,low: 12.72,close: 12.92,volume: 26183900},
{date: "8/22/2011",open: 13.16,high: 13.23,low: 12.77,close: 12.84,volume: 14199400},
{date: "8/23/2011",open: 12.91,high: 13.35,low: 12.75,close: 13.35,volume: 17186500},
{date: "8/24/2011",open: 13.28,high: 13.3,low: 12.79,close: 13.15,volume: 24967200},
{date: "8/25/2011",open: 13.12,high: 13.21,low: 12.81,close: 12.87,volume: 21796100},
{date: "8/26/2011",open: 12.8,high: 12.89,low: 12.52,close: 12.74,volume: 35882600},
{date: "8/29/2011",open: 12.9,high: 13.68,low: 12.69,close: 13.68,volume: 30990800},
{date: "8/30/2011",open: 13.3,high: 13.98,low: 13.23,close: 13.84,volume: 29162300},
{date: "8/31/2011",open: 13.91,high: 13.94,low: 13.54,close: 13.61,volume: 25390700},
{date: "9/1/2011",open: 13.67,high: 13.78,low: 13.32,close: 13.35,volume: 17847800},
{date: "9/2/2011",open: 13.12,high: 13.13,low: 12.86,close: 12.87,volume: 20508600},
{date: "9/6/2011",open: 12.52,high: 12.95,low: 12.45,close: 12.91,volume: 54297300},
{date: "9/7/2011",open: 13.75,high: 14,low: 13.24,close: 13.61,volume: 77324200},
{date: "9/8/2011",open: 13.57,high: 14.49,low: 13.37,close: 14.44,volume: 93945500},
{date: "9/9/2011",open: 14.36,high: 14.57,low: 14.07,close: 14.48,volume: 60009600},
{date: "9/12/2011",open: 14.12,high: 14.28,low: 13.92,close: 14.26,volume: 32692700},
{date: "9/13/2011",open: 14.3,high: 14.34,low: 14.12,close: 14.26,volume: 19928800},
{date: "9/14/2011",open: 14.47,high: 14.94,low: 14.34,close: 14.55,volume: 37385000},
{date: "9/15/2011",open: 14.73,high: 15.4,low: 14.51,close: 14.89,volume: 58548900},
{date: "9/16/2011",open: 15.09,high: 15.34,low: 14.94,close: 14.97,volume: 56810800},
{date: "9/19/2011",open: 14.76,high: 14.79,low: 14.4,close: 14.61,volume: 27249700},
{date: "9/20/2011",open: 14.53,high: 14.66,low: 14.28,close: 14.36,volume: 21767200},
{date: "9/21/2011",open: 14.38,high: 14.6,low: 13.96,close: 13.96,volume: 32012800},
{date: "9/22/2011",open: 14.2,high: 14.25,low: 13.69,close: 13.99,volume: 60439700},
{date: "9/23/2011",open: 14.23,high: 14.83,low: 14.12,close: 14.71,volume: 49322600},
{date: "9/26/2011",open: 14.79,high: 14.8,low: 14.23,close: 14.75,volume: 24466200},
{date: "9/27/2011",open: 14.92,high: 15,low: 14.44,close: 14.54,volume: 25084400},
{date: "9/28/2011",open: 14.61,high: 14.62,low: 14.15,close: 14.19,volume: 21271900},
{date: "9/29/2011",open: 14.34,high: 14.39,low: 13.15,close: 13.42,volume: 45776600},
{date: "9/30/2011",open: 13.21,high: 13.44,low: 13.11,close: 13.17,volume: 30232800}];
rocketcharts.init(document.getElementById("rocketchart"), settings); 
// rocketcharts.addSeries(title, data, type, style, panel);
rocketcharts.addSeries("GOOG", googData, undefined, style);
// rocketcharts.addIndicator(id, params, series, panel);
rocketcharts.addIndicator("movingaverageconvergencedivergence", undefined, 0);
}); 
// ]]&gt;</script></p>
<div id="rocketchart" style="background-color: #000000; height: 400px; margin-bottom: 60px;"></div>
<p>
Utilizing HTML5&#8242;s canvas, I present to you my newest open-source project: <a href="http://www.rocketcharts.com">Rocketcharts</a>. I am following the &#8220;release early, release often&#8221; mantra for this project, so I&#8217;d keep it out of production code for the moment. Speaking of code, how did I generate the above interactive chart?
</p>
<p>&nbsp;</p>
<pre class="brush:js">
&lt;script type="text/javascript" src="http://www.rocketcharts.com/rocketcharts.js">&lt;/script>
&lt;script type="text/javascript" src="http://www.rocketcharts.com/js/jquery-1.5.1.min.js">&lt;/script>
&lt;script type="text/javascript">
   $(document).ready(function () {    

      var style = new Object();
      style.UpColor = {r: 0, g: 255, b: 0};
      style.DownColor = {r: 255, g: 0, b: 0};

      var settings = new Object();
      settings.customUI = true;

      rocketcharts.init(document.getElementById("rocketchart"), settings); 

      var googData = [{date: "11/9/2010", open: 17.22, high: 17.6, low: 16.86, close: 16.97, volume: 56218900},
                      {date: "11/10/2010", open: 17, high: 17.01, low: 16.75, close: 16.94, volume: 17012600},
                      ...
                      {date: "9/29/2011", open: 14.34, high: 14.39, low: 13.15, close: 13.42, volume: 45776600},
                      {date: "9/30/2011", open: 13.21, high: 13.44, low: 13.11, close: 13.17, volume: 30232800}];

      // rocketcharts.addSeries(title, data, type, style, panel);
      rocketcharts.addSeries("GOOG", googData, undefined, style);
      // rocketcharts.addIndicator(id, params, series, panel);
      rocketcharts.addIndicator("movingaverageconvergancedivergance", undefined, 0);

   });
&lt;/script>
</pre>
<p>&nbsp;</p>
<h1>A View of the Architecture from 30,000 Feet</h1>
<p>Before we jump right into how to use rocketcharts, let&#8217;s first understand what is going on &#8216;behind the scenes.&#8217;</p>
<p>If you look at the code above, you&#8217;ll notice the first method call: &#8220;rocketcharts.init(element, settings);&#8221; Element points to a container in your DOM, typically a DIV, where you want the chart displayed. Rocketcharts respects any styles you have set for this element and will not resize it; Instead, JQuery is employed to append a canvas, referred to as a chart panel, to that DIV.</p>
<p><strong>&#8220;But what is a panel?&#8221;</strong> If you look again at the code block above, you&#8217;ll notice I don&#8217;t specify the last parameter of the &#8216;addIndicator&#8217; call which specifies a panel ID. The lack of a panel being passed creates a new panel for the indicator to be drawn on, i.e. a new canvas appended to the DOM. Adding a new element for each panel, rather than drawing all the panels on one large element, brings several advantages to the table. For one, we can leverage the existing power of a modern browser using JQueryUI to allow effortless user-resizing of the panels. No need to worry about vertical scrolling either, as this is taken care of for us.</p>
<p>Once we have our panels appended to the DOM, the real fun can begin: pixel manipulation. (<i>Inner dialog: &#8220;Am I nerd, a geek or just a passionate developer? Playing with pixels is fun, dammit!&#8221;</i>) Essentially, rocketcharts performs some initial calculations, then loops through each panel and calls it&#8217;s draw method:</p>
<div id="attachment_100" class="wp-caption aligncenter" style="width: 331px"><a href="http://blog.chasegale.com/wp-content/uploads/Panel-Drawing-Flow.png"><img src="http://blog.chasegale.com/wp-content/uploads/Panel-Drawing-Flow.png" alt="Panel Drawing Flow" title="Panel Drawing Flow" width="321" height="650" class="size-full wp-image-100" /></a><p class="wp-caption-text">Amateurish UML Activity Diagram Illustrating Panel Drawing</p></div>
<h1>The State of Rocketcharts</h1>
<p>For the moment, rocketcharts is functionally quite basic. You can add your own Open-High-Low-Close-Date data to the chart. You can add one of three indicators and specify to draw over the primary series or on a new panel. Simple for now but the <i>foundation</i> is in place &#8211; adding additional studies/indicators is a simple affair.</p>
<p>Within the immediate future I plan to add zooming and horizontal scrolling, more indicators and a more robust UI allowing the user, rather than you, to manage the chart. I encourage you to <a href="https://github.com/chasebgale/rocketcharts">follow along with the source</a>, all feedback is welcome and will help keep the momentum of the project high.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.chasegale.com/introducing-rocketcharts-open-source-html5-financialstatistical-charts/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Raster Text In HTML5 Canvas</title>
		<link>http://blog.chasegale.com/raster-text-in-html5-canvas/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=raster-text-in-html5-canvas</link>
		<comments>http://blog.chasegale.com/raster-text-in-html5-canvas/#comments</comments>
		<pubDate>Mon, 12 Sep 2011 16:50:27 +0000</pubDate>
		<dc:creator>chasegale</dc:creator>
				<category><![CDATA[Code Tutorials]]></category>
		<category><![CDATA[Canvas]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[Raster]]></category>

		<guid isPermaLink="false">http://blog.chasegale.com/?p=29</guid>
		<description><![CDATA[I love HTML5&#8242;s canvas. Armed with only a modern browser, we now have access to a plethora of drawing/imaging functionality with no third-party plugins required! However, life isn&#8217;t all sunshine and bikinis quite yet; echoed across the interwebs is one resounding cry: &#8220;drawText is performance murder.&#8221; Wouldn&#8217;t you know it, the new opensource project I [...]]]></description>
			<content:encoded><![CDATA[<p>I love HTML5&#8242;s canvas. Armed with only a modern browser, we now have access to a plethora of drawing/imaging functionality with no third-party plugins required! However, life isn&#8217;t all sunshine and bikinis quite yet; echoed across the interwebs is one resounding cry: &#8220;<a href="http://simonsarris.com/blog/322-canvas-drawtext-considered-harmful" target="_blank">drawText is performance murder.</a>&#8221; Wouldn&#8217;t you know it, the new opensource project I am building requires heaps (<em>pun intended</em>) of drawText() calls.</p>
<p>&nbsp;</p>
<p>So, what to do? How do we quickly get a block of text onto a canvas? The answer is to manipulate the pixels of the canvas directly. I feel that code speaks louder than words, so let&#8217;s jump right in. First, you&#8217;ll need an image that represents our font:</p>
<p>&nbsp;</p>
<p><a href="http://blog.chasegale.com/wp-content/uploads/bitmapfont_source.png"><img class="aligncenter size-full wp-image-32" title="bitmapfont_source" src="http://blog.chasegale.com/wp-content/uploads/bitmapfont_source.png" alt="Bitmapfont Image" width="566" height="7" /></a></p>
<p>&nbsp;</p>
<p>As you can see, I am using a bitmap font (no anti-aliasing) and I am outputting all characters in order, starting at charcode 33. <strong>Note: The lookup function we are going to write today depends entirely on these characters being in order. </strong>The next step is to load the font image into an off-screen buffer, or in our particular case, a hidden canvas element.</p>
<p>&nbsp;</p>
<pre class="brush:js">var fontImage = new Image();
fontImage.onload = function(){
  var bufferCanvas = document.getElementById("bufferCanvas");
  var bufferContext = bufferCanvas.getContext("2d");

  bufferContext.drawImage(fontImage, 0, 0, 566, 7);
};
fontImage.src = "bitmapfont.png";</pre>
<p>&nbsp;</p>
<p>Now that we have our image drawn into the canvas, we are able to extract a representative array of pixels and begin processing them. The idea is to iterate over each block (every 4 elements of the array correspond to red, green, blue and alpha channels of a single pixel) and record the significant points for each character.</p>
<p>&nbsp;</p>
<pre class="brush:js">var fontPoints = new Array();
var fontImage = new Image();
fontImage.onload = function(){
  var bufferCanvas = document.getElementById("bufferCanvas");
  var bufferContext = bufferCanvas.getContext("2d");

  bufferContext.drawImage(fontImage, 0, 0, 566, 7);

  var w = 566;
  var fontPixelArray = bufferContext.getImageData(0, 0, w, 7);

  // Store pointer directly to array of data to speed up lookups
  var fontPixelData = fontPixelArray.data;
  var total = -1;
  var x = 0;
  var y = 0;
  var index = -1;
  var pointsLength = -1;

  for (var i=0; i &lt; 95; i++) {
    // Each array element is an array that stores relative x and y coordinates
    fontPoints[i] = new Array();
  }

  for (var i=0; i &lt; fontPixelData.length; i++) {
    // Add up the R, G, B values
    total = fontPixelData[i] + fontPixelData[i+1] + fontPixelData[i+2];
    // If the total = 0 it's a black pixel, if not, we need to record it
    if (total &gt; 0) {
		x = i / 4 % w;
		y = ( i / 4 - x) / w;

        // We can derive the character index by dividing by the character width
		index = Math.floor(x/6);

		x = x - (index * 6);

		pointsLength = fontPoints[index].length;

		fontPoints[index][pointsLength] = {x: x, y: y};
	}

	i += 3;
  }

};
fontImage.src = "bitmapfont.png";</pre>
<p>&nbsp;</p>
<p>As illustrated above, we first create an array containing 94 child arrays &#8211; each will hold the significant points of the associated glyph; next, we iterate over the CanvasPixelData array in chunks of 4, screening out black pixels. Once we come upon a non-black point, we derive it&#8217;s relative coordinates and character index, then add the point to the appropriate array.</p>
<p>&nbsp;</p>
<p>At this point, we have an array of coordinates to reproduce every glyph in our original image. As I&#8217;m sure you have anticipated, we now need a method we can invoke that will imprint these coordinates onto an existing image. Again, code speaks louder than words:</p>
<p>&nbsp;</p>
<pre class="brush:js">
function setPixel(imageData, x, y, r, g, b, a) {
    index = (parseInt(x) + parseInt(y) * imageData.width) * 4;
    imageData[index+0] = r;
    imageData[index+1] = g;
    imageData[index+2] = b;
    imageData[index+3] = a;
}

function rasterText(imageData, text, x, y) {
    var len = text.length;
    var i = 0;
    var code = 0;
    var characterPixelLength = -1;

    var startX = x;
    var startY = y;

    for (i=0; i < len; i++) {
      code = text.charCodeAt(i) - 33;
      if (code > -1) {
        characterPixelLength = fontPoints[code].length;

        for (var j=0; j < characterPixelLength; j++) {
          setPixel(imageData,
            startX + fontPoints[code][j].x,
            startY + fontPoints[code][j].y,
            255, 255, 255, 0xFF);
        };
      }

      startX += 6;
    };
}
</pre>
<p>&nbsp;</p>
<p>Our new "rasterText" method accepts a string as one of it's parameters. For each character in this string, we lookup the number of points required to reproduce it, then for each of those points, we call the helper method "setPixel." You'll also notice on line 19, we determine the index in our array by subtracting 33 from the character code. A space resolves to -1, but you'll notice we increase our "startX" outside that condition, so the space is preserved. </p>
<p>&nbsp;</p>
<p>Here is how we would use this method from start to finish:</p>
<p>&nbsp;</p>
<pre class="brush:js">
  var destinationCanvas = document.getElementById("destinationCanvas");
  var context = destinationCanvas.getContext("2d");
  var imageData = context.getImageData(0, 0, context.width, context.height);

  rasterText(imageData.data, "Hello World!", 10, 10);

  context.putImageData(imageData, 0, 0);
</pre>
<p>&nbsp;</p>
<p>Awesome; However, I'm sure upon reflection you'll be identifying the limitations of this technique. Namely, you'll need to load and pre-process a different image for each font face and font size. Keeping that in mind, also factor in that during testing in my aforementioned opensource venture, our text rendering time was reduced by a mean ~20%. </p>
<p>&nbsp;</p>
<p>In closing, the method we built here today is by no means a nail in the text-rendering-poor-performance coffin. What it is, however, is another tool in your arsenal - I urge you to treat it like a scalpel, not a hammer.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.chasegale.com/raster-text-in-html5-canvas/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>User Activity Tracking in ASP.NET MVC3 via Global Filters</title>
		<link>http://blog.chasegale.com/user-activity-tracking-in-asp-net-mvc3-via-global-filters/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=user-activity-tracking-in-asp-net-mvc3-via-global-filters</link>
		<comments>http://blog.chasegale.com/user-activity-tracking-in-asp-net-mvc3-via-global-filters/#comments</comments>
		<pubDate>Tue, 30 Aug 2011 19:40:31 +0000</pubDate>
		<dc:creator>chasegale</dc:creator>
				<category><![CDATA[Code Tutorials]]></category>
		<category><![CDATA[ActionFilterAttribute]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[C#.NET]]></category>
		<category><![CDATA[MVC3]]></category>

		<guid isPermaLink="false">http://blog.chasegale.com/?p=5</guid>
		<description><![CDATA[When designing enterprise applications I often run into the same request from the business: &#8216;user tracking.&#8217; Who created this entity? What time? Why was this entity created (origin tracking)? In the past I have seen colleagues create additional columns, such as userId and creationTime, on every &#8216;trackable&#8217; table in the system. At the time, this [...]]]></description>
			<content:encoded><![CDATA[<p>When designing enterprise applications I often run into the same request from the business: &#8216;user tracking.&#8217; Who created this entity? What time? Why was this entity created (origin tracking)? In the past I have seen colleagues create additional columns, such as userId and creationTime, on every &#8216;trackable&#8217; table in the system. At the time, this made sense; However, with the advent of Global Filters in MVC3, more efficient ways of doing this have become apparent. Let&#8217;s jump in head first.</p>
<p>Open up Visual Studio 2010 and your MVC3 project. If you&#8217;d like, simply create a new MVC3 Web Application for the purposes of this tutorial. Next, create a folder under the project root named &#8216;Filters.&#8217; In this folder, create a class named &#8216;UserActivityAttribute.cs&#8217; &#8211; please note, these names can be anything you desire and hold no special meaning. When you&#8217;re finished, your solution explorer should look something like the following:</p>
<div id="attachment_6" class="wp-caption aligncenter" style="width: 322px"><a href="http://blog.chasegale.com/wp-content/uploads/solution.png"><img class="size-full wp-image-6" title="Solution Explorer" src="http://blog.chasegale.com/wp-content/uploads/solution.png" alt="Solution Explorer" width="312" height="510" /></a><p class="wp-caption-text">File structure after adding the class.</p></div>
<p>&nbsp;</p>
<p>Still with me? Awesome. Open up your newly created class and inherit from &#8216;ActionFilterAttribute.&#8217; ActionFilterAttribute has several methods you can override which you can read about individually <a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.actionfilterattribute.aspx" target="_blank">over at MSDN</a>; For now though, let&#8217;s focus on &#8216;OnResultExecuting.&#8217; As you might have guessed, this method is fired before the result is executed. Go ahead and override this method, bringing your class to look something like this:</p>
<p>&nbsp;</p>
<pre class="brush:csharp">public class UserActivityAttribute : ActionFilterAttribute
{
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
             base.OnResultExecuting(filterContext);
        }
}</pre>
<p>&nbsp;</p>
<p>Now for the fun stuff! As I&#8217;m sure you&#8217;ve observed, we get one argument from this method: &#8216;filterContext.&#8217; From this ResultExecutingContext we will corral all the information we need to track our user&#8217;s actions. Which ActionFilterAttribute method to override depends on what type of information you are trying to capture and the same goes for the approach. To that end, let&#8217;s say that in your scenario entities are created using the standard &#8216;Create&#8217; action. Furthermore, assume that after successful creation you redirect to that entities &#8216;Details&#8217; action.</p>
<p>So, with these assumptions fresh in our minds, what is the first step to isolate the data we want?</p>
<p>&nbsp;</p>
<pre class="brush:csharp">public class UserActivityAttribute : ActionFilterAttribute
{
  public override void OnResultExecuting(ResultExecutingContext filterContext)
  {
    if ((filterContext.Result is RedirectToRouteResult) &amp;&amp;
        (filterContext.RequestContext.HttpContext.Request.RequestType == "POST"))
    {

    }
    base.OnResultExecuting(filterContext);
  }
}</pre>
<p>&nbsp;</p>
<p>As you can see, we check that the result primed for execution is of the type &#8216;RedirectToRouteResult.&#8217; Additionally, we ensure the request was a POST, the default request type for &#8216;Create&#8217; methods. Now that we have this in place, let&#8217;s extract some information about the RouteData of the <strong>Request</strong> and filter down further.</p>
<p>&nbsp;</p>
<pre class="brush:csharp">public class UserActivityAttribute : ActionFilterAttribute
{
  public override void OnResultExecuting(ResultExecutingContext filterContext)
  {
    if ((filterContext.Result is RedirectToRouteResult) &amp;&amp;
        (filterContext.RequestContext.HttpContext.Request.RequestType == "POST"))
    {
      var originController = filterContext.RouteData.Values["controller"].ToString();
      var originAction = filterContext.RouteData.Values["action"].ToString();

      if (originAction == "Create")
      {

      }
    }
    base.OnResultExecuting(filterContext);
  }
}</pre>
<p>&nbsp;</p>
<p>Now we have the controller and action of the request, using this information we filter out actions not matching our mock scenario, create calls. The final step is to decide what information to store and to format it accordingly. Let&#8217;s assume this application tracks brick-and-mortar stores and the customers that frequent them. We have two different controllers, Customer and Store, and based on what we are creating we want to change the formatting. I&#8217;ll let the code do the talking:</p>
<p>&nbsp;</p>
<pre class="brush:csharp">public class UserActivityAttribute : ActionFilterAttribute
{
  public override void OnResultExecuting(ResultExecutingContext filterContext)
  {
    if ((filterContext.Result is RedirectToRouteResult) &amp;&amp;
        (filterContext.RequestContext.HttpContext.Request.RequestType == "POST"))
    {
      var originController = filterContext.RouteData.Values["controller"].ToString();
      var originAction = filterContext.RouteData.Values["action"].ToString();

      if (originAction == "Create")
      {
        RedirectToRouteResult redirectResult = filterContext.Result as RedirectToRouteResult;
        var form = filterContext.RequestContext.HttpContext.Request.Form;

        var destinationController = redirectResult.RouteValues["controller"];
        var destinationAction = redirectResult.RouteValues["action"];
        var destinationId = redirectResult.RouteValues["id"];

        var destination = "/" + destinationController + "/" + destinationAction + "/" + destinationId;
        var title = "";

        switch (destinationController)
        {
          case "Customer":
            title = form["FirstName"] + " " + form["LastName"];
            break;
          case "Store":
            title = "#" + destinationId.ToString();
            break;
        }

        var action = "Created " + destinationController + " [" + title + "](" + destination + ")";
        Tracking.RecordSystemUserAction(action);
      }
    }
  }
}</pre>
<p>&nbsp;</p>
<p>Too cool, right? I love this stuff. Let&#8217;s break it down; First off, we pull out and cast the Result object. Secondly, because we know we are in a post, we pull out the form the user submitted. Thirdly, we extract all of the destination route information and use it to switch and further format. Finally, we bundle all of this up into a nice, human readable string and send it off to our utility method to be saved.</p>
<p>How you format this information is entirely up to you and your use case. As you may have figured out from the psuedo-markup I am generating, in my case I take anything inside the brackets and generate a url from the destination. When history is presented the end user, they see a friendly &#8216;wall post&#8217; style list, allowing them to easily click through their history.</p>
<p>Ok, so I said that was the last step, I fibbed. Now we have this badass user tracking ActionFilterAttribute, how do we tell MVC3 to execute this code on every request? Under the project root in your Solution Explorer, find and open a file named &#8216;Global.asax.&#8217; You&#8217;ll see a method titled &#8216;RegisterRoutes.&#8217; Paste the following code above it.</p>
<p>&nbsp;</p>
<pre class="brush:csharp">public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
  filters.Add(new UserActivityAttribute());
}</pre>
<p>&nbsp;</p>
<p>Now find the &#8216;Application_Start()&#8217; method in the same file. Ensure that &#8216;RegisterGlobalFilters(GlobalFilters.Filters);&#8217; is called within. That&#8217;s it. For real this time. When your MVC3 application starts, it will call this method; Behind the scenes, every action method is adorned with the attribute we just created.</p>
<p>The possibilities exposed by Global Filters, coupled with their ease of implementation, makes them an essential card up your sleeve. I&#8217;ll leave you with one friendly piece of advice: Remember that these overridden methods will be called <em>with every single request</em>. Make your initial condition as exclusive as possible to save cpu cycles.</p>
<p>Best of luck.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.chasegale.com/user-activity-tracking-in-asp-net-mvc3-via-global-filters/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

