<?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>Extensible Development &#187; charting</title>
	<atom:link href="http://blog.itwarlocks.com/tag/charting/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.itwarlocks.com</link>
	<description>Profession blog about Software Engineering, Web, *nix, Processes, Tools and more.</description>
	<lastBuildDate>Mon, 29 Mar 2010 12:20:30 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=abc</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Advanced charts in Wordpress</title>
		<link>http://blog.itwarlocks.com/2009/12/17/advanced-charts-in-wordpress/</link>
		<comments>http://blog.itwarlocks.com/2009/12/17/advanced-charts-in-wordpress/#comments</comments>
		<pubDate>Thu, 17 Dec 2009 14:11:06 +0000</pubDate>
		<dc:creator><span property="dc:creator" resource="http://blog.itwarlocks.com/2009/12/17/advanced-charts-in-wordpress/">Jeffrey Ridout</span></dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[charting]]></category>
		<category><![CDATA[highcharts]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://blog.itwarlocks.com/?p=257</guid>
		<description><![CDATA[Introduction
I recently came across Highcharts, a great JavaScript library for creating charts. It uses canvas where it can, supports skins (easily changed with CSS). I&#8217;ve been searching/waiting for a decent JavaScript based charting API and it has finally arrived.
Seeing as I&#8217;m a wordpressaholic I&#8217;m of course going to try and get it into Wordpress. Although that [...]]]></description>
			<content:encoded><![CDATA[<h3>Introduction</h3>
<p>I recently <a title="Ajaxian: Highcharts, really nice charting API" href="http://ajaxian.com/archives/highcharts-really-nice-charting-api" target="_blank">came across</a> <a title="Highcharts, Interactive JavaScript Charting Library" href="http://highcharts.com/" target="_blank">Highcharts</a>, a great JavaScript library for creating charts. It uses <a id="aptureLink_taKFFEYWh6" href="http://en.wikipedia.org/wiki/Canvas%20%28HTML%20element%29">canvas</a> where it can, supports skins (easily changed with CSS). I&#8217;ve been searching/waiting for a decent JavaScript based charting API and it has finally arrived.</p>
<p>Seeing as I&#8217;m a wordpressaholic I&#8217;m of course going to try and get it into Wordpress. Although that might turn out to be quite more than I can chew.</p>
<p><span id="more-257"></span></p>
<h3>Data Source</h3>
<p>Highcharts can create charts from different sources; JSON, AJAX and HTML Tables. Each of which are actually scripted, but Highcharts has nice examples. For a Wordpress plug-in the most common source would be an existing HTML table. To be easily used as a source, it would require some structure. As an example I&#8217;ll take the <a title="Browser Statistics 2009" href="http://www.w3schools.com/browsers/browsers_stats.asp" target="_blank">Browser Statistics for 2009 from w3schools.com</a>.</p>
<p>Table:</p>
<pre class="brush: html">
&lt;table width=&quot;100%&quot; cellspacing=&quot;0&quot; id=&quot;chart-1-table&quot;&gt;
	&lt;caption&gt;
		&lt;h2 class=&quot;title&quot;&gt;Browser Statistics 2009&lt;/h2&gt;
		&lt;h3 class=&quot;subtitle&quot;&gt;Source: &lt;a href=&quot;http://www.w3schools.com/browsers/browsers_stats.asp&quot; title=&quot;Browser Statistics at w3schools.com&quot;&gt;w3schools.com&lt;/a&gt;&lt;/h3&gt;
	&lt;/caption&gt;
	&lt;colgroup class=&quot;chart-axis-x&quot;&gt;
		&lt;col width=&quot;16%&quot; align=&quot;left&quot; title=&quot;Month&quot;/&gt;
	&lt;/colgroup&gt;
	&lt;colgroup width=&quot;12%&quot; align=&quot;center&quot; class=&quot;chart-axis-y&quot;&gt;
		&lt;col title=&quot;Internet Explorer 6&quot;/&gt;
		&lt;col title=&quot;Internet Explorer 7&quot;/&gt;
		&lt;col title=&quot;Internet Explorer 8&quot;/&gt;
		&lt;col title=&quot;Firefox&quot;/&gt;
		&lt;col title=&quot;Chrome&quot;/&gt;
		&lt;col title=&quot;Safari&quot;/&gt;
		&lt;col title=&quot;Opera&quot;/&gt;
	&lt;/colgroup&gt;
	&lt;thead&gt;
		&lt;tr&gt;
			&lt;th&gt;2009&lt;/th&gt;
			&lt;th&gt;IE6&lt;/th&gt;
			&lt;th&gt;IE7&lt;/th&gt;
			&lt;th&gt;IE8&lt;/th&gt;
			&lt;th&gt;Firefox&lt;/th&gt;
			&lt;th&gt;Chrome&lt;/th&gt;
			&lt;th&gt;Safari&lt;/th&gt;
			&lt;th&gt;Opera&lt;/th&gt;
		&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
		&lt;tr&gt;
			&lt;th&gt;November&lt;/th&gt;
			&lt;td&gt;13.3%&lt;/td&gt;
			&lt;td&gt;13.3%&lt;/td&gt;
			&lt;td&gt;11.1%&lt;/td&gt;
			&lt;td&gt;47.0%&lt;/td&gt;
			&lt;td&gt;8.5%&lt;/td&gt;
			&lt;td&gt;3.8%&lt;/td&gt;
			&lt;td&gt;2.3%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;October&lt;/th&gt;
			&lt;td&gt;12.8%&lt;/td&gt;
			&lt;td&gt;14.1%&lt;/td&gt;
			&lt;td&gt;10.6%&lt;/td&gt;
			&lt;td&gt;47.5%&lt;/td&gt;
			&lt;td&gt;8.0%&lt;/td&gt;
			&lt;td&gt;3.8%&lt;/td&gt;
			&lt;td&gt;2.3%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;September&lt;/th&gt;
			&lt;td&gt;12.2%&lt;/td&gt;
			&lt;td&gt;15.3%&lt;/td&gt;
			&lt;td&gt;12.1%&lt;/td&gt;
			&lt;td&gt;46.6%&lt;/td&gt;
			&lt;td&gt;7.1%&lt;/td&gt;
			&lt;td&gt;3.6%&lt;/td&gt;
			&lt;td&gt;2.2%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;August&lt;/th&gt;
			&lt;td&gt;10.6%&lt;/td&gt;
			&lt;td&gt;15.1%&lt;/td&gt;
			&lt;td&gt;13.6%&lt;/td&gt;
			&lt;td&gt;47.4%&lt;/td&gt;
			&lt;td&gt;7.0%&lt;/td&gt;
			&lt;td&gt;3.3%&lt;/td&gt;
			&lt;td&gt;2.1%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;July&lt;/th&gt;
			&lt;td&gt;9.1%&lt;/td&gt;
			&lt;td&gt;15.9%&lt;/td&gt;
			&lt;td&gt;14.4%&lt;/td&gt;
			&lt;td&gt;47.9%&lt;/td&gt;
			&lt;td&gt;6.5%&lt;/td&gt;
			&lt;td&gt;3.3%&lt;/td&gt;
			&lt;td&gt;2.1%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;June&lt;/th&gt;
			&lt;td&gt;7.1%&lt;/td&gt;
			&lt;td&gt;18.7%&lt;/td&gt;
			&lt;td&gt;14.9%&lt;/td&gt;
			&lt;td&gt;47.3%&lt;/td&gt;
			&lt;td&gt;6.0%&lt;/td&gt;
			&lt;td&gt;3.1%&lt;/td&gt;
			&lt;td&gt;2.1%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;May&lt;/th&gt;
			&lt;td&gt;5.2%&lt;/td&gt;
			&lt;td&gt;21.3%&lt;/td&gt;
			&lt;td&gt;14.5%&lt;/td&gt;
			&lt;td&gt;47.7%&lt;/td&gt;
			&lt;td&gt;5.5%&lt;/td&gt;
			&lt;td&gt;3.0%&lt;/td&gt;
			&lt;td&gt;2.2%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;April&lt;/th&gt;
			&lt;td&gt;3.5%&lt;/td&gt;
			&lt;td&gt;23.2%&lt;/td&gt;
			&lt;td&gt;15.4%&lt;/td&gt;
			&lt;td&gt;47.1%&lt;/td&gt;
			&lt;td&gt;4.9%&lt;/td&gt;
			&lt;td&gt;3.0%&lt;/td&gt;
			&lt;td&gt;2.2%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;March&lt;/th&gt;
			&lt;td&gt;1.4%&lt;/td&gt;
			&lt;td&gt;24.9%&lt;/td&gt;
			&lt;td&gt;17.0%&lt;/td&gt;
			&lt;td&gt;46.5%&lt;/td&gt;
			&lt;td&gt;4.2%&lt;/td&gt;
			&lt;td&gt;3.1%&lt;/td&gt;
			&lt;td&gt;2.3%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;February&lt;/th&gt;
			&lt;td&gt;0.8%&lt;/td&gt;
			&lt;td&gt;25.4%&lt;/td&gt;
			&lt;td&gt;17.4%&lt;/td&gt;
			&lt;td&gt;46.4%&lt;/td&gt;
			&lt;td&gt;4.0%&lt;/td&gt;
			&lt;td&gt;3.0%&lt;/td&gt;
			&lt;td&gt;2.2%&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;th&gt;January&lt;/th&gt;
			&lt;td&gt;0.6%&lt;/td&gt;
			&lt;td&gt;25.7%&lt;/td&gt;
			&lt;td&gt;18.5%&lt;/td&gt;
			&lt;td&gt;45.5%&lt;/td&gt;
			&lt;td&gt;3.9%&lt;/td&gt;
			&lt;td&gt;3.0%&lt;/td&gt;
			&lt;td&gt;2.3%&lt;/td&gt;
		&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
</pre>
<p>Style:</p>
<pre class="brush: css">
.chart table {
	margin: 0;
	padding: 0;
	font-size: 0.86em;
}
.chart table tr {
	line-height: 1.5em;
}
.chart table th,
.chart table td {
	padding: 0.5em;
	border-top: 1px solid #999999;
}
.chart table thead {
	background-color: #F0F0F0;
	background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#E0E0E0), to(#FFFFFF));
}
.chart table thead th {
	border-bottom: 1px solid #999999;
	border-top: 1px solid #999999;
}
.chart table thead th:first-child,
.chart table tbody th {
	text-align: left;
}
.chart table tbody td {
	text-align: center;
}
.chart table h2.title {
	font-size: 1.2em;
	margin-bottom: 0px;
}
.chart table h3.subtitle {
	font-size: 0.86em;
	margin-top: 0px;
}
</pre>
<h3>Charting</h3>
<p>The Highcharts API has a lot of <a title="Hightcharts: Options Reference" href="http://www.highcharts.com/ref" target="_blank">options</a>, too much to mention here, but a few are key to working successfully with pre-existing tables. The examples don&#8217;t use jQuery to parse the HTML, but since both Wordpress and Highcharts use it, I will too. These properties are static in the examples, but were they&#8217;ll need to be more dynamic; <code>title</code>, <code>subtitle</code>, <code>xAxis</code>, <code>plotOptions.series</code>. These options can be obtained from the table text or structure.</p>
<p><code>title</code> and <code>subtitle</code> can be grabbed from the <code>&lt;caption&gt;</code>:</p>
<pre class="brush: javascript">
title: {
	text: (function(){
		var table = $(&#039;#chart-1-table&#039;);
		var title = $(&#039;caption .title&#039;, table);

		return title ? title.html() : &#039;&#039;;
	}())
},
subtitle: {
	text: (function(){
		var table = $(&#039;#chart-1-table&#039;);
		var subtitle = $(&#039;caption .subtitle&#039;, table);

		return subtitle ? subtitle.html() : &#039;&#039;;
	}())
},
</pre>
<p>Currently the title and subtitle are taken from tags with the &#8216;title&#8217; and &#8217;subtitle&#8217; classes, but just using H2 and H3 (or H1 and H2) would work too of course.</p>
<p><code>xAxis</code> as in the examples:</p>
<pre class="brush: javascript">
xAxis: {
	categories: [
		&#039;Jan&#039;, &#039;Feb&#039;, &#039;Mar&#039;, &#039;Apr&#039;, &#039;May&#039;, &#039;Jun&#039;,
		&#039;Jul&#039;, &#039;Aug&#039;, &#039;Sep&#039;, &#039;Oct&#039;, &#039;Nov&#039;, &#039;Dec&#039;
	],
	title: {
		text: &#039;Month&#039;
	}
}
</pre>
<p>Since we&#8217;ve got an existing table with labels, we can do it more dynamically.<br />
<code>xAxis</code> from existing table:</p>
<pre class="brush: javascript">
xAxis: {
	categories: (function(){
		var table = $(&#039;#chart-1-table&#039;);
		/*
		 * The first column in the table body uses &lt;TH&gt; tags,
		 * to separate them from data.
		 */
		var cats = $(&#039;tbody &gt; tr &gt; th&#039;, table)
			.map(function(){
				return $(this).text();
			});

		return $.makeArray(cats);
	}()),
	title: {
		text: &#039;Month&#039;
	}
}
</pre>
<p><code>map</code> allows you to turn the array with elements into an array with the actual text.</p>
<p><code>series</code> as in the examples:</p>
<pre class="brush: javascript">
series: [{
	name: &#039;November&#039;
}, {
	name: &#039;October&#039;
}, {
	name: &#039;September&#039;
}, {
	name: &#039;August&#039;
}, {
	name: &#039;July&#039;
}, {
	name: &#039;June&#039;
}, {
	name: &#039;May&#039;
}, {
	name: &#039;April&#039;
}, {
	name: &#039;March&#039;
}, {
	name: &#039;February&#039;
}, {
	name: &#039;January&#039;
}]
</pre>
<p>Since we&#8217;ve got an existing table with labels, we can do it more dynamically.<br />
<code>series</code> from existing table:</p>
<pre class="brush: javascript">
series: (function(){
	var result = [];

	var table = $(&#039;#chart-1-table&#039;);
	/*
	 * Get all the columns from the table head.
	 * Some tables have multiple rows in the header.
	 */
	var head = $(&#039;thead &gt; tr:last&#039;, table);
	/*
	 * Skip the first column, it&#039;s the Y-axis label.
	 */
	var columns = $(&#039;th:gt(0)&#039;, head);

	for (var i = 0, iMax = columns.length; i &lt; iMax; i++) {
		result.push(
			{
				name: $(columns[i]).text()
			}
		);
	};

	return result;
}())
</pre>
<p>And lastly the <code>plotOptions.dataParser</code> function:</p>
<pre class="brush: javascript">
dataParser: function(data) {
	var result = [];

	var table = $(&#039;#chart-1-table&#039;);

	/*
	 * The series are defined by names, so we need to check what series we&#039;re in.
	 */
	var head = $(&#039;thead &gt; tr:last&#039;, table);
	var column = $(&#039;th:gt(0)&#039;, head).map(function(){
		return $(this).text();
	}).index(this.options.name);

	/*
	 * Loop through all the data rows for this series.
	 */
	var rows = $(&#039;tbody &gt; tr&#039;, table);
	for (var i = 0, max = rows.length; i &lt; max; i++) {
		/*
		 * The first column (using a &lt;TH&gt; tag) contains the category name.
		 */
		var rowDate = $(&#039;th&#039;, rows[i]);
		if (rowDate.size() &gt; 1) {
			rowDate = rowDate[0];
		}
		rowDate = rowDate.text();

		/*
		 * Grab the cell in the right column for this series.
		 */
		var cell = $(&#039;td&#039;, rows[i])[column];
		cell = $(cell);

		/*
		 * Add an array to the result with the category name and cell value.
		 */
		result.push([
			rowDate,
			parseFloat(cell.text())
		]);
	}

	return result;
}
</pre>
<p>The code for above <code>dataParser</code> is very specific for tables using categories. Tables with a lot more data that use timestamp should use <code>xAxis.type = 'datetime'</code> and convert the value of the row header to a UTC date.</p>
<p>Compete code:</p>
<pre class="brush: javascript">
var chart1 = null;

$(document).ready(function(){
	chart1 = new Highcharts.Chart({
		chart: {
			renderTo: &#039;chart-1-container&#039;,
			defaultSeriesType: &#039;line&#039;
		},
		title: {
			text: (function(){
				var table = $(&#039;#chart-1-table&#039;);
				var title = $(&#039;caption .title&#039;, table);

				return title ? title.html() : &#039;&#039;;
			}())
		},
		subtitle: {
			text: (function(){
				var table = $(&#039;#chart-1-table&#039;);
				var subtitle = $(&#039;caption .subtitle&#039;, table);

				return subtitle ? subtitle.html() : &#039;&#039;;
			}())
		},
		xAxis: {
			categories: (function(){
				var table = $(&#039;#chart-1-table&#039;);
				/*
				 * The first column in the table body uses &lt;TH&gt; tags,
				 * to separate them from data.
				 */
				var cats = $(&#039;tbody &gt; tr &gt; th&#039;, table)
					.map(function(){
						return $(this).text();
					});

				return $.makeArray(cats);
			}()),
			title: {
				text: &#039;Month&#039;
			},
			type: &#039;linear&#039;
		},
		yAxis: {
			title: {
				text: &#039;Percent&#039;
			},
			labels: {
				formatter: function() {
					return this.value + &#039;%&#039;;
				}
			}
		},
		tooltip: {
			formatter: function() {
				return	&#039;&lt;strong&gt;&#039; + this.series.name + &#039;&lt;/strong&gt;&lt;br /&gt;&#039;+
						this.x + &#039;: &#039; + Highcharts.numberFormat(this.y, 1) + &#039;%&#039;;
			}
		},
		plotOptions: {
			line: {
				//stacking: &#039;percent&#039;,
				lineColor: &#039;#ffffff&#039;,
				lineWidth: 1,
				marker: {
					lineWidth: 1,
					lineColor: &#039;#ffffff&#039;
				},
				data: &#039;datatable&#039;,
				dataParser: function(data) {
					var result = [];

					var table = $(&#039;#chart-1-table&#039;);

					/*
					 * The series are defined by names, so we need to check what series we&#039;re in.
					 */
					var head = $(&#039;thead &gt; tr:last&#039;, table);
					var column = $(&#039;th:gt(0)&#039;, head).map(function(){
						return $(this).text();
					}).index(this.options.name);

					/*
					 * Loop through all the data rows for this series.
					 */
					var rows = $(&#039;tbody &gt; tr&#039;, table);
					for (var i = 0, max = rows.length; i &lt; max; i++) {
						/*
						 * The first column (using a &lt;TH&gt; tag) contains the category name.
						 */
						var rowDate = $(&#039;th&#039;, rows[i]);
						if (rowDate.size() &gt; 1) {
							rowDate = rowDate[0];
						}
						rowDate = rowDate.text();

						/*
						 * Grab the cell in the right column for this series.
						 */
						var cell = $(&#039;td&#039;, rows[i])[column];
						cell = $(cell);

						/*
						 * Add an array to the result with the category name and cell value.
						 */
						result.push([
							rowDate,
							parseFloat(cell.text())
						]);
					}

					return result;
				}
			}
		},
		series: (function(){
			var result = [];

			var table = $(&#039;#chart-1-table&#039;);
			/*
			 * Get all the columns from the table head.
			 * Some tables have multiple rows in the header.
			 */
			var head = $(&#039;thead &gt; tr:last&#039;, table);
			/*
			 * Skip the first column, it&#039;s the Y-axis label.
			 */
			var columns = $(&#039;th:gt(0)&#039;, head);

			for (var i = 0, iMax = columns.length; i &lt; iMax; i++) {
				result.push(
					{
						name: $(columns[i]).text()
					}
				);
			};

			return result;
		}())
	});
});
</pre>
<h3>Demo</h3>
<ul>
<li><a rel="nofollow" title="Download version 0.1 of browser-stats.html" href="http://blog.itwarlocks.com/lab/highcharts/browser-stats.html">Browser statistics</a></li>
</ul>
<h3>Next steps</h3>
<p>The above code works great for a single instance, but for use in a plug-in it&#8217;s too scattered. The first thing to do is to create a dataSource Interface or base Class that provides functions for series, categories and dataParser.<br />
For a Wordpress plug-in an advanced GUI for the WYSIWYG editor is needed to allow access to the complex options, not to mention a chart preview inside the editor.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.itwarlocks.com/2009/12/17/advanced-charts-in-wordpress/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk
Page Caching using disk (enhanced) (user agent is rejected)

Served from: blog.itwarlocks.com @ 2010-09-09 01:10:46 -->