usergrid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From snoopd...@apache.org
Subject [31/83] [abbrv] [partial] updated to latest Angular-based admin portal
Date Wed, 05 Feb 2014 17:21:23 GMT
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/508ef2f7/portal/js/libs/Highcharts-2.3.5/js/highcharts.src.js
----------------------------------------------------------------------
diff --git a/portal/js/libs/Highcharts-2.3.5/js/highcharts.src.js b/portal/js/libs/Highcharts-2.3.5/js/highcharts.src.js
new file mode 100644
index 0000000..b36bf1e
--- /dev/null
+++ b/portal/js/libs/Highcharts-2.3.5/js/highcharts.src.js
@@ -0,0 +1,15281 @@
+// ==ClosureCompiler==
+// @compilation_level SIMPLE_OPTIMIZATIONS
+
+/**
+ * @license Highcharts JS v2.3.5 (2012-12-19)
+ *
+ * (c) 2009-2012 Torstein H√łnsi
+ *
+ * License: www.highcharts.com/license
+ */
+
+// JSLint options:
+/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */
+
+(function () {
+// encapsulated variables
+var UNDEFINED,
+	doc = document,
+	win = window,
+	math = Math,
+	mathRound = math.round,
+	mathFloor = math.floor,
+	mathCeil = math.ceil,
+	mathMax = math.max,
+	mathMin = math.min,
+	mathAbs = math.abs,
+	mathCos = math.cos,
+	mathSin = math.sin,
+	mathPI = math.PI,
+	deg2rad = mathPI * 2 / 360,
+
+
+	// some variables
+	userAgent = navigator.userAgent,
+	isOpera = win.opera,
+	isIE = /msie/i.test(userAgent) && !isOpera,
+	docMode8 = doc.documentMode === 8,
+	isWebKit = /AppleWebKit/.test(userAgent),
+	isFirefox = /Firefox/.test(userAgent),
+	isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent),
+	SVG_NS = 'http://www.w3.org/2000/svg',
+	hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect,
+	hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38
+	useCanVG = !hasSVG && !isIE && !!doc.createElement('canvas').getContext,
+	Renderer,
+	hasTouch = doc.documentElement.ontouchstart !== UNDEFINED,
+	symbolSizes = {},
+	idCounter = 0,
+	garbageBin,
+	defaultOptions,
+	dateFormat, // function
+	globalAnimation,
+	pathAnim,
+	timeUnits,
+	noop = function () {},
+	charts = [],
+
+	// some constants for frequently used strings
+	DIV = 'div',
+	ABSOLUTE = 'absolute',
+	RELATIVE = 'relative',
+	HIDDEN = 'hidden',
+	PREFIX = 'highcharts-',
+	VISIBLE = 'visible',
+	PX = 'px',
+	NONE = 'none',
+	M = 'M',
+	L = 'L',
+	/*
+	 * Empirical lowest possible opacities for TRACKER_FILL
+	 * IE6: 0.002
+	 * IE7: 0.002
+	 * IE8: 0.002
+	 * IE9: 0.00000000001 (unlimited)
+	 * IE10: 0.0001 (exporting only)
+	 * FF: 0.00000000001 (unlimited)
+	 * Chrome: 0.000001
+	 * Safari: 0.000001
+	 * Opera: 0.00000000001 (unlimited)
+	 */
+	TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.0001 : 0.002) + ')', // invisible but clickable
+	//TRACKER_FILL = 'rgba(192,192,192,0.5)',
+	NORMAL_STATE = '',
+	HOVER_STATE = 'hover',
+	SELECT_STATE = 'select',
+	MILLISECOND = 'millisecond',
+	SECOND = 'second',
+	MINUTE = 'minute',
+	HOUR = 'hour',
+	DAY = 'day',
+	WEEK = 'week',
+	MONTH = 'month',
+	YEAR = 'year',
+
+	// constants for attributes
+	FILL = 'fill',
+	LINEAR_GRADIENT = 'linearGradient',
+	STOPS = 'stops',
+	STROKE = 'stroke',
+	STROKE_WIDTH = 'stroke-width',
+
+	// time methods, changed based on whether or not UTC is used
+	makeTime,
+	getMinutes,
+	getHours,
+	getDay,
+	getDate,
+	getMonth,
+	getFullYear,
+	setMinutes,
+	setHours,
+	setDate,
+	setMonth,
+	setFullYear,
+
+
+	// lookup over the types and the associated classes
+	seriesTypes = {};
+
+// The Highcharts namespace
+win.Highcharts = {};
+
+/**
+ * Extend an object with the members of another
+ * @param {Object} a The object to be extended
+ * @param {Object} b The object to add to the first one
+ */
+function extend(a, b) {
+	var n;
+	if (!a) {
+		a = {};
+	}
+	for (n in b) {
+		a[n] = b[n];
+	}
+	return a;
+}
+
+/**
+ * Take an array and turn into a hash with even number arguments as keys and odd numbers as
+ * values. Allows creating constants for commonly used style properties, attributes etc.
+ * Avoid it in performance critical situations like looping
+ */
+function hash() {
+	var i = 0,
+		args = arguments,
+		length = args.length,
+		obj = {};
+	for (; i < length; i++) {
+		obj[args[i++]] = args[i];
+	}
+	return obj;
+}
+
+/**
+ * Shortcut for parseInt
+ * @param {Object} s
+ * @param {Number} mag Magnitude
+ */
+function pInt(s, mag) {
+	return parseInt(s, mag || 10);
+}
+
+/**
+ * Check for string
+ * @param {Object} s
+ */
+function isString(s) {
+	return typeof s === 'string';
+}
+
+/**
+ * Check for object
+ * @param {Object} obj
+ */
+function isObject(obj) {
+	return typeof obj === 'object';
+}
+
+/**
+ * Check for array
+ * @param {Object} obj
+ */
+function isArray(obj) {
+	return Object.prototype.toString.call(obj) === '[object Array]';
+}
+
+/**
+ * Check for number
+ * @param {Object} n
+ */
+function isNumber(n) {
+	return typeof n === 'number';
+}
+
+function log2lin(num) {
+	return math.log(num) / math.LN10;
+}
+function lin2log(num) {
+	return math.pow(10, num);
+}
+
+/**
+ * Remove last occurence of an item from an array
+ * @param {Array} arr
+ * @param {Mixed} item
+ */
+function erase(arr, item) {
+	var i = arr.length;
+	while (i--) {
+		if (arr[i] === item) {
+			arr.splice(i, 1);
+			break;
+		}
+	}
+	//return arr;
+}
+
+/**
+ * Returns true if the object is not null or undefined. Like MooTools' $.defined.
+ * @param {Object} obj
+ */
+function defined(obj) {
+	return obj !== UNDEFINED && obj !== null;
+}
+
+/**
+ * Set or get an attribute or an object of attributes. Can't use jQuery attr because
+ * it attempts to set expando properties on the SVG element, which is not allowed.
+ *
+ * @param {Object} elem The DOM element to receive the attribute(s)
+ * @param {String|Object} prop The property or an abject of key-value pairs
+ * @param {String} value The value if a single property is set
+ */
+function attr(elem, prop, value) {
+	var key,
+		setAttribute = 'setAttribute',
+		ret;
+
+	// if the prop is a string
+	if (isString(prop)) {
+		// set the value
+		if (defined(value)) {
+
+			elem[setAttribute](prop, value);
+
+		// get the value
+		} else if (elem && elem.getAttribute) { // elem not defined when printing pie demo...
+			ret = elem.getAttribute(prop);
+		}
+
+	// else if prop is defined, it is a hash of key/value pairs
+	} else if (defined(prop) && isObject(prop)) {
+		for (key in prop) {
+			elem[setAttribute](key, prop[key]);
+		}
+	}
+	return ret;
+}
+/**
+ * Check if an element is an array, and if not, make it into an array. Like
+ * MooTools' $.splat.
+ */
+function splat(obj) {
+	return isArray(obj) ? obj : [obj];
+}
+
+
+/**
+ * Return the first value that is defined. Like MooTools' $.pick.
+ */
+function pick() {
+	var args = arguments,
+		i,
+		arg,
+		length = args.length;
+	for (i = 0; i < length; i++) {
+		arg = args[i];
+		if (typeof arg !== 'undefined' && arg !== null) {
+			return arg;
+		}
+	}
+}
+
+/**
+ * Set CSS on a given element
+ * @param {Object} el
+ * @param {Object} styles Style object with camel case property names
+ */
+function css(el, styles) {
+	if (isIE) {
+		if (styles && styles.opacity !== UNDEFINED) {
+			styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')';
+		}
+	}
+	extend(el.style, styles);
+}
+
+/**
+ * Utility function to create element with attributes and styles
+ * @param {Object} tag
+ * @param {Object} attribs
+ * @param {Object} styles
+ * @param {Object} parent
+ * @param {Object} nopad
+ */
+function createElement(tag, attribs, styles, parent, nopad) {
+	var el = doc.createElement(tag);
+	if (attribs) {
+		extend(el, attribs);
+	}
+	if (nopad) {
+		css(el, {padding: 0, border: NONE, margin: 0});
+	}
+	if (styles) {
+		css(el, styles);
+	}
+	if (parent) {
+		parent.appendChild(el);
+	}
+	return el;
+}
+
+/**
+ * Extend a prototyped class by new members
+ * @param {Object} parent
+ * @param {Object} members
+ */
+function extendClass(parent, members) {
+	var object = function () {};
+	object.prototype = new parent();
+	extend(object.prototype, members);
+	return object;
+}
+
+/**
+ * How many decimals are there in a number
+ */
+function getDecimals(number) {
+	
+	number = (number || 0).toString();
+	
+	return number.indexOf('.') > -1 ? 
+		number.split('.')[1].length :
+		0;
+}
+
+/**
+ * Format a number and return a string based on input settings
+ * @param {Number} number The input number to format
+ * @param {Number} decimals The amount of decimals
+ * @param {String} decPoint The decimal point, defaults to the one given in the lang options
+ * @param {String} thousandsSep The thousands separator, defaults to the one given in the lang options
+ */
+function numberFormat(number, decimals, decPoint, thousandsSep) {
+	var lang = defaultOptions.lang,
+		// http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/
+		n = number,
+		c = decimals === -1 ?
+			getDecimals(number) :
+			(isNaN(decimals = mathAbs(decimals)) ? 2 : decimals),
+		d = decPoint === undefined ? lang.decimalPoint : decPoint,
+		t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep,
+		s = n < 0 ? "-" : "",
+		i = String(pInt(n = mathAbs(+n || 0).toFixed(c))),
+		j = i.length > 3 ? i.length % 3 : 0;
+
+	return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) +
+		(c ? d + mathAbs(n - i).toFixed(c).slice(2) : "");
+}
+
+/**
+ * Pad a string to a given length by adding 0 to the beginning
+ * @param {Number} number
+ * @param {Number} length
+ */
+function pad(number, length) {
+	// Create an array of the remaining length +1 and join it with 0's
+	return new Array((length || 2) + 1 - String(number).length).join(0) + number;
+}
+
+/**
+ * Wrap a method with extended functionality, preserving the original function
+ * @param {Object} obj The context object that the method belongs to 
+ * @param {String} method The name of the method to extend
+ * @param {Function} func A wrapper function callback. This function is called with the same arguments
+ * as the original function, except that the original function is unshifted and passed as the first 
+ * argument. 
+ */
+function wrap(obj, method, func) {
+	var proceed = obj[method];
+	obj[method] = function () {
+		var args = Array.prototype.slice.call(arguments);
+		args.unshift(proceed);
+		return func.apply(this, args);
+	};
+}
+
+/**
+ * Based on http://www.php.net/manual/en/function.strftime.php
+ * @param {String} format
+ * @param {Number} timestamp
+ * @param {Boolean} capitalize
+ */
+dateFormat = function (format, timestamp, capitalize) {
+	if (!defined(timestamp) || isNaN(timestamp)) {
+		return 'Invalid date';
+	}
+	format = pick(format, '%Y-%m-%d %H:%M:%S');
+
+	var date = new Date(timestamp),
+		key, // used in for constuct below
+		// get the basic time values
+		hours = date[getHours](),
+		day = date[getDay](),
+		dayOfMonth = date[getDate](),
+		month = date[getMonth](),
+		fullYear = date[getFullYear](),
+		lang = defaultOptions.lang,
+		langWeekdays = lang.weekdays,
+		/* // uncomment this and the 'W' format key below to enable week numbers
+		weekNumber = function () {
+			var clone = new Date(date.valueOf()),
+				day = clone[getDay]() == 0 ? 7 : clone[getDay](),
+				dayNumber;
+			clone.setDate(clone[getDate]() + 4 - day);
+			dayNumber = mathFloor((clone.getTime() - new Date(clone[getFullYear](), 0, 1, -6)) / 86400000);
+			return 1 + mathFloor(dayNumber / 7);
+		},
+		*/
+
+		// list all format keys
+		replacements = {
+
+			// Day
+			'a': langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon'
+			'A': langWeekdays[day], // Long weekday, like 'Monday'
+			'd': pad(dayOfMonth), // Two digit day of the month, 01 to 31
+			'e': dayOfMonth, // Day of the month, 1 through 31
+
+			// Week (none implemented)
+			//'W': weekNumber(),
+
+			// Month
+			'b': lang.shortMonths[month], // Short month, like 'Jan'
+			'B': lang.months[month], // Long month, like 'January'
+			'm': pad(month + 1), // Two digit month number, 01 through 12
+
+			// Year
+			'y': fullYear.toString().substr(2, 2), // Two digits year, like 09 for 2009
+			'Y': fullYear, // Four digits year, like 2009
+
+			// Time
+			'H': pad(hours), // Two digits hours in 24h format, 00 through 23
+			'I': pad((hours % 12) || 12), // Two digits hours in 12h format, 00 through 11
+			'l': (hours % 12) || 12, // Hours in 12h format, 1 through 12
+			'M': pad(date[getMinutes]()), // Two digits minutes, 00 through 59
+			'p': hours < 12 ? 'AM' : 'PM', // Upper case AM or PM
+			'P': hours < 12 ? 'am' : 'pm', // Lower case AM or PM
+			'S': pad(date.getSeconds()), // Two digits seconds, 00 through  59
+			'L': pad(mathRound(timestamp % 1000), 3) // Milliseconds (naming from Ruby)
+		};
+
+
+	// do the replaces
+	for (key in replacements) {
+		while (format.indexOf('%' + key) !== -1) { // regex would do it in one line, but this is faster
+			format = format.replace('%' + key, replacements[key]);
+		}
+	}
+
+	// Optionally capitalize the string and return
+	return capitalize ? format.substr(0, 1).toUpperCase() + format.substr(1) : format;
+};
+
+/**
+ * Take an interval and normalize it to multiples of 1, 2, 2.5 and 5
+ * @param {Number} interval
+ * @param {Array} multiples
+ * @param {Number} magnitude
+ * @param {Object} options
+ */
+function normalizeTickInterval(interval, multiples, magnitude, options) {
+	var normalized, i;
+
+	// round to a tenfold of 1, 2, 2.5 or 5
+	magnitude = pick(magnitude, 1);
+	normalized = interval / magnitude;
+
+	// multiples for a linear scale
+	if (!multiples) {
+		multiples = [1, 2, 2.5, 5, 10];
+
+		// the allowDecimals option
+		if (options && options.allowDecimals === false) {
+			if (magnitude === 1) {
+				multiples = [1, 2, 5, 10];
+			} else if (magnitude <= 0.1) {
+				multiples = [1 / magnitude];
+			}
+		}
+	}
+
+	// normalize the interval to the nearest multiple
+	for (i = 0; i < multiples.length; i++) {
+		interval = multiples[i];
+		if (normalized <= (multiples[i] + (multiples[i + 1] || multiples[i])) / 2) {
+			break;
+		}
+	}
+
+	// multiply back to the correct magnitude
+	interval *= magnitude;
+
+	return interval;
+}
+
+/**
+ * Get a normalized tick interval for dates. Returns a configuration object with
+ * unit range (interval), count and name. Used to prepare data for getTimeTicks. 
+ * Previously this logic was part of getTimeTicks, but as getTimeTicks now runs
+ * of segments in stock charts, the normalizing logic was extracted in order to 
+ * prevent it for running over again for each segment having the same interval. 
+ * #662, #697.
+ */
+function normalizeTimeTickInterval(tickInterval, unitsOption) {
+	var units = unitsOption || [[
+				MILLISECOND, // unit name
+				[1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples
+			], [
+				SECOND,
+				[1, 2, 5, 10, 15, 30]
+			], [
+				MINUTE,
+				[1, 2, 5, 10, 15, 30]
+			], [
+				HOUR,
+				[1, 2, 3, 4, 6, 8, 12]
+			], [
+				DAY,
+				[1, 2]
+			], [
+				WEEK,
+				[1, 2]
+			], [
+				MONTH,
+				[1, 2, 3, 4, 6]
+			], [
+				YEAR,
+				null
+			]],
+		unit = units[units.length - 1], // default unit is years
+		interval = timeUnits[unit[0]],
+		multiples = unit[1],
+		count,
+		i;
+		
+	// loop through the units to find the one that best fits the tickInterval
+	for (i = 0; i < units.length; i++) {
+		unit = units[i];
+		interval = timeUnits[unit[0]];
+		multiples = unit[1];
+
+
+		if (units[i + 1]) {
+			// lessThan is in the middle between the highest multiple and the next unit.
+			var lessThan = (interval * multiples[multiples.length - 1] +
+						timeUnits[units[i + 1][0]]) / 2;
+
+			// break and keep the current unit
+			if (tickInterval <= lessThan) {
+				break;
+			}
+		}
+	}
+
+	// prevent 2.5 years intervals, though 25, 250 etc. are allowed
+	if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
+		multiples = [1, 2, 5];
+	}
+	
+	// prevent 2.5 years intervals, though 25, 250 etc. are allowed
+	if (interval === timeUnits[YEAR] && tickInterval < 5 * interval) {
+		multiples = [1, 2, 5];
+	}
+
+	// get the count
+	count = normalizeTickInterval(tickInterval / interval, multiples);
+	
+	return {
+		unitRange: interval,
+		count: count,
+		unitName: unit[0]
+	};
+}
+
+/**
+ * Set the tick positions to a time unit that makes sense, for example
+ * on the first of each month or on every Monday. Return an array
+ * with the time positions. Used in datetime axes as well as for grouping
+ * data on a datetime axis.
+ *
+ * @param {Object} normalizedInterval The interval in axis values (ms) and the count
+ * @param {Number} min The minimum in axis values
+ * @param {Number} max The maximum in axis values
+ * @param {Number} startOfWeek
+ */
+function getTimeTicks(normalizedInterval, min, max, startOfWeek) {
+	var tickPositions = [],
+		i,
+		higherRanks = {},
+		useUTC = defaultOptions.global.useUTC,
+		minYear, // used in months and years as a basis for Date.UTC()
+		minDate = new Date(min),
+		interval = normalizedInterval.unitRange,
+		count = normalizedInterval.count;
+
+	if (defined(min)) { // #1300
+		if (interval >= timeUnits[SECOND]) { // second
+			minDate.setMilliseconds(0);
+			minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 :
+				count * mathFloor(minDate.getSeconds() / count));
+		}
+	
+		if (interval >= timeUnits[MINUTE]) { // minute
+			minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 :
+				count * mathFloor(minDate[getMinutes]() / count));
+		}
+	
+		if (interval >= timeUnits[HOUR]) { // hour
+			minDate[setHours](interval >= timeUnits[DAY] ? 0 :
+				count * mathFloor(minDate[getHours]() / count));
+		}
+	
+		if (interval >= timeUnits[DAY]) { // day
+			minDate[setDate](interval >= timeUnits[MONTH] ? 1 :
+				count * mathFloor(minDate[getDate]() / count));
+		}
+	
+		if (interval >= timeUnits[MONTH]) { // month
+			minDate[setMonth](interval >= timeUnits[YEAR] ? 0 :
+				count * mathFloor(minDate[getMonth]() / count));
+			minYear = minDate[getFullYear]();
+		}
+	
+		if (interval >= timeUnits[YEAR]) { // year
+			minYear -= minYear % count;
+			minDate[setFullYear](minYear);
+		}
+	
+		// week is a special case that runs outside the hierarchy
+		if (interval === timeUnits[WEEK]) {
+			// get start of current week, independent of count
+			minDate[setDate](minDate[getDate]() - minDate[getDay]() +
+				pick(startOfWeek, 1));
+		}
+	
+	
+		// get tick positions
+		i = 1;
+		minYear = minDate[getFullYear]();
+		var time = minDate.getTime(),
+			minMonth = minDate[getMonth](),
+			minDateDate = minDate[getDate](),
+			timezoneOffset = useUTC ? 
+				0 : 
+				(24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950
+	
+		// iterate and add tick positions at appropriate values
+		while (time < max) {
+			tickPositions.push(time);
+	
+			// if the interval is years, use Date.UTC to increase years
+			if (interval === timeUnits[YEAR]) {
+				time = makeTime(minYear + i * count, 0);
+	
+			// if the interval is months, use Date.UTC to increase months
+			} else if (interval === timeUnits[MONTH]) {
+				time = makeTime(minYear, minMonth + i * count);
+	
+			// if we're using global time, the interval is not fixed as it jumps
+			// one hour at the DST crossover
+			} else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) {
+				time = makeTime(minYear, minMonth, minDateDate +
+					i * count * (interval === timeUnits[DAY] ? 1 : 7));
+	
+			// else, the interval is fixed and we use simple addition
+			} else {
+				time += interval * count;
+				
+				// mark new days if the time is dividable by day
+				if (interval <= timeUnits[HOUR] && time % timeUnits[DAY] === timezoneOffset) {
+					higherRanks[time] = DAY;
+				}
+			}
+	
+			i++;
+		}
+	
+		// push the last time
+		tickPositions.push(time);
+	}
+
+	// record information on the chosen unit - for dynamic label formatter
+	tickPositions.info = extend(normalizedInterval, {
+		higherRanks: higherRanks,
+		totalRange: interval * count
+	});
+
+	return tickPositions;
+}
+
+/**
+ * Helper class that contains variuos counters that are local to the chart.
+ */
+function ChartCounters() {
+	this.color = 0;
+	this.symbol = 0;
+}
+
+ChartCounters.prototype =  {
+	/**
+	 * Wraps the color counter if it reaches the specified length.
+	 */
+	wrapColor: function (length) {
+		if (this.color >= length) {
+			this.color = 0;
+		}
+	},
+
+	/**
+	 * Wraps the symbol counter if it reaches the specified length.
+	 */
+	wrapSymbol: function (length) {
+		if (this.symbol >= length) {
+			this.symbol = 0;
+		}
+	}
+};
+
+
+/**
+ * Utility method that sorts an object array and keeping the order of equal items.
+ * ECMA script standard does not specify the behaviour when items are equal.
+ */
+function stableSort(arr, sortFunction) {
+	var length = arr.length,
+		sortValue,
+		i;
+
+	// Add index to each item
+	for (i = 0; i < length; i++) {
+		arr[i].ss_i = i; // stable sort index
+	}
+
+	arr.sort(function (a, b) {
+		sortValue = sortFunction(a, b);
+		return sortValue === 0 ? a.ss_i - b.ss_i : sortValue;
+	});
+
+	// Remove index from items
+	for (i = 0; i < length; i++) {
+		delete arr[i].ss_i; // stable sort index
+	}
+}
+
+/**
+ * Non-recursive method to find the lowest member of an array. Math.min raises a maximum
+ * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This
+ * method is slightly slower, but safe.
+ */
+function arrayMin(data) {
+	var i = data.length,
+		min = data[0];
+
+	while (i--) {
+		if (data[i] < min) {
+			min = data[i];
+		}
+	}
+	return min;
+}
+
+/**
+ * Non-recursive method to find the lowest member of an array. Math.min raises a maximum
+ * call stack size exceeded error in Chrome when trying to apply more than 150.000 points. This
+ * method is slightly slower, but safe.
+ */
+function arrayMax(data) {
+	var i = data.length,
+		max = data[0];
+
+	while (i--) {
+		if (data[i] > max) {
+			max = data[i];
+		}
+	}
+	return max;
+}
+
+/**
+ * Utility method that destroys any SVGElement or VMLElement that are properties on the given object.
+ * It loops all properties and invokes destroy if there is a destroy method. The property is
+ * then delete'ed.
+ * @param {Object} The object to destroy properties on
+ * @param {Object} Exception, do not destroy this property, only delete it.
+ */
+function destroyObjectProperties(obj, except) {
+	var n;
+	for (n in obj) {
+		// If the object is non-null and destroy is defined
+		if (obj[n] && obj[n] !== except && obj[n].destroy) {
+			// Invoke the destroy
+			obj[n].destroy();
+		}
+
+		// Delete the property from the object.
+		delete obj[n];
+	}
+}
+
+
+/**
+ * Discard an element by moving it to the bin and delete
+ * @param {Object} The HTML node to discard
+ */
+function discardElement(element) {
+	// create a garbage bin element, not part of the DOM
+	if (!garbageBin) {
+		garbageBin = createElement(DIV);
+	}
+
+	// move the node and empty bin
+	if (element) {
+		garbageBin.appendChild(element);
+	}
+	garbageBin.innerHTML = '';
+}
+
+/**
+ * Provide error messages for debugging, with links to online explanation 
+ */
+function error(code, stop) {
+	var msg = 'Highcharts error #' + code + ': www.highcharts.com/errors/' + code;
+	if (stop) {
+		throw msg;
+	} else if (win.console) {
+		console.log(msg);
+	}
+}
+
+/**
+ * Fix JS round off float errors
+ * @param {Number} num
+ */
+function correctFloat(num) {
+	return parseFloat(
+		num.toPrecision(14)
+	);
+}
+
+/**
+ * Set the global animation to either a given value, or fall back to the
+ * given chart's animation option
+ * @param {Object} animation
+ * @param {Object} chart
+ */
+function setAnimation(animation, chart) {
+	globalAnimation = pick(animation, chart.animation);
+}
+
+/**
+ * The time unit lookup
+ */
+/*jslint white: true*/
+timeUnits = hash(
+	MILLISECOND, 1,
+	SECOND, 1000,
+	MINUTE, 60000,
+	HOUR, 3600000,
+	DAY, 24 * 3600000,
+	WEEK, 7 * 24 * 3600000,
+	MONTH, 31 * 24 * 3600000,
+	YEAR, 31556952000
+);
+/*jslint white: false*/
+/**
+ * Path interpolation algorithm used across adapters
+ */
+pathAnim = {
+	/**
+	 * Prepare start and end values so that the path can be animated one to one
+	 */
+	init: function (elem, fromD, toD) {
+		fromD = fromD || '';
+		var shift = elem.shift,
+			bezier = fromD.indexOf('C') > -1,
+			numParams = bezier ? 7 : 3,
+			endLength,
+			slice,
+			i,
+			start = fromD.split(' '),
+			end = [].concat(toD), // copy
+			startBaseLine,
+			endBaseLine,
+			sixify = function (arr) { // in splines make move points have six parameters like bezier curves
+				i = arr.length;
+				while (i--) {
+					if (arr[i] === M) {
+						arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]);
+					}
+				}
+			};
+
+		if (bezier) {
+			sixify(start);
+			sixify(end);
+		}
+
+		// pull out the base lines before padding
+		if (elem.isArea) {
+			startBaseLine = start.splice(start.length - 6, 6);
+			endBaseLine = end.splice(end.length - 6, 6);
+		}
+
+		// if shifting points, prepend a dummy point to the end path
+		if (shift <= end.length / numParams) {
+			while (shift--) {
+				end = [].concat(end).splice(0, numParams).concat(end);
+			}
+		}
+		elem.shift = 0; // reset for following animations
+
+		// copy and append last point until the length matches the end length
+		if (start.length) {
+			endLength = end.length;
+			while (start.length < endLength) {
+
+				//bezier && sixify(start);
+				slice = [].concat(start).splice(start.length - numParams, numParams);
+				if (bezier) { // disable first control point
+					slice[numParams - 6] = slice[numParams - 2];
+					slice[numParams - 5] = slice[numParams - 1];
+				}
+				start = start.concat(slice);
+			}
+		}
+
+		if (startBaseLine) { // append the base lines for areas
+			start = start.concat(startBaseLine);
+			end = end.concat(endBaseLine);
+		}
+		return [start, end];
+	},
+
+	/**
+	 * Interpolate each value of the path and return the array
+	 */
+	step: function (start, end, pos, complete) {
+		var ret = [],
+			i = start.length,
+			startVal;
+
+		if (pos === 1) { // land on the final path without adjustment points appended in the ends
+			ret = complete;
+
+		} else if (i === end.length && pos < 1) {
+			while (i--) {
+				startVal = parseFloat(start[i]);
+				ret[i] =
+					isNaN(startVal) ? // a letter instruction like M or L
+						start[i] :
+						pos * (parseFloat(end[i] - startVal)) + startVal;
+
+			}
+		} else { // if animation is finished or length not matching, land on right value
+			ret = end;
+		}
+		return ret;
+	}
+};
+
+(function ($) {
+	/**
+	 * The default HighchartsAdapter for jQuery
+	 */
+	win.HighchartsAdapter = win.HighchartsAdapter || ($ && {
+		
+		/**
+		 * Initialize the adapter by applying some extensions to jQuery
+		 */
+		init: function (pathAnim) {
+			
+			// extend the animate function to allow SVG animations
+			var Fx = $.fx,
+				Step = Fx.step,
+				dSetter,
+				Tween = $.Tween,
+				propHooks = Tween && Tween.propHooks;
+			
+			/*jslint unparam: true*//* allow unused param x in this function */
+			$.extend($.easing, {
+				easeOutQuad: function (x, t, b, c, d) {
+					return -c * (t /= d) * (t - 2) + b;
+				}
+			});
+			/*jslint unparam: false*/
+		
+		
+			// extend some methods to check for elem.attr, which means it is a Highcharts SVG object
+			$.each(['cur', '_default', 'width', 'height'], function (i, fn) {
+				var obj = Step,
+					base,
+					elem;
+					
+				// Handle different parent objects
+				if (fn === 'cur') {
+					obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype
+				
+				} else if (fn === '_default' && Tween) { // jQuery 1.8 model
+					obj = propHooks[fn];
+					fn = 'set';
+				}
+		
+				// Overwrite the method
+				base = obj[fn];
+				if (base) { // step.width and step.height don't exist in jQuery < 1.7
+		
+					// create the extended function replacement
+					obj[fn] = function (fx) {
+		
+						// Fx.prototype.cur does not use fx argument
+						fx = i ? fx : this;
+		
+						// shortcut
+						elem = fx.elem;
+		
+						// Fx.prototype.cur returns the current value. The other ones are setters
+						// and returning a value has no effect.
+						return elem.attr ? // is SVG element wrapper
+							elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method
+							base.apply(this, arguments); // use jQuery's built-in method
+					};
+				}
+			});
+			
+			
+			// Define the setter function for d (path definitions)
+			dSetter = function (fx) {
+				var elem = fx.elem,
+					ends;
+		
+				// Normally start and end should be set in state == 0, but sometimes,
+				// for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped
+				// in these cases
+				if (!fx.started) {
+					ends = pathAnim.init(elem, elem.d, elem.toD);
+					fx.start = ends[0];
+					fx.end = ends[1];
+					fx.started = true;
+				}
+		
+		
+				// interpolate each value of the path
+				elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD));
+			};
+			
+			// jQuery 1.8 style
+			if (Tween) {
+				propHooks.d = {
+					set: dSetter
+				};
+			// pre 1.8
+			} else {
+				// animate paths
+				Step.d = dSetter;
+			}
+			
+			/**
+			 * Utility for iterating over an array. Parameters are reversed compared to jQuery.
+			 * @param {Array} arr
+			 * @param {Function} fn
+			 */
+			this.each = Array.prototype.forEach ?
+				function (arr, fn) { // modern browsers
+					return Array.prototype.forEach.call(arr, fn);
+					
+				} : 
+				function (arr, fn) { // legacy
+					var i = 0, 
+						len = arr.length;
+					for (; i < len; i++) {
+						if (fn.call(arr[i], arr[i], i, arr) === false) {
+							return i;
+						}
+					}
+				};
+			
+			// Register Highcharts as a jQuery plugin
+			// TODO: MooTools and prototype as well?
+			// TODO: StockChart
+			/*$.fn.highcharts = function(options, callback) {
+		        options.chart = merge(options.chart, { renderTo: this[0] });
+		        this.chart = new Chart(options, callback);
+		        return this;
+		    };*/
+		},
+	
+		/**
+		 * Downloads a script and executes a callback when done.
+		 * @param {String} scriptLocation
+		 * @param {Function} callback
+		 */
+		getScript: $.getScript,
+		
+		/**
+		 * Return the index of an item in an array, or -1 if not found
+		 */
+		inArray: $.inArray,
+		
+		/**
+		 * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method.
+		 * @param {Object} elem The HTML element
+		 * @param {String} method Which method to run on the wrapped element
+		 */
+		adapterRun: function (elem, method) {
+			return $(elem)[method]();
+		},
+	
+		/**
+		 * Filter an array
+		 */
+		grep: $.grep,
+	
+		/**
+		 * Map an array
+		 * @param {Array} arr
+		 * @param {Function} fn
+		 */
+		map: function (arr, fn) {
+			//return jQuery.map(arr, fn);
+			var results = [],
+				i = 0,
+				len = arr.length;
+			for (; i < len; i++) {
+				results[i] = fn.call(arr[i], arr[i], i, arr);
+			}
+			return results;
+	
+		},
+	
+		/**
+		 * Deep merge two objects and return a third object
+		 */
+		merge: function () {
+			var args = arguments;
+			return $.extend(true, null, args[0], args[1], args[2], args[3]);
+		},
+	
+		/**
+		 * Get the position of an element relative to the top left of the page
+		 */
+		offset: function (el) {
+			return $(el).offset();
+		},
+	
+		/**
+		 * Add an event listener
+		 * @param {Object} el A HTML element or custom object
+		 * @param {String} event The event type
+		 * @param {Function} fn The event handler
+		 */
+		addEvent: function (el, event, fn) {
+			$(el).bind(event, fn);
+		},
+	
+		/**
+		 * Remove event added with addEvent
+		 * @param {Object} el The object
+		 * @param {String} eventType The event type. Leave blank to remove all events.
+		 * @param {Function} handler The function to remove
+		 */
+		removeEvent: function (el, eventType, handler) {
+			// workaround for jQuery issue with unbinding custom events:
+			// http://forum.jQuery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jQuery-1-4-2
+			var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent';
+			if (doc[func] && !el[func]) {
+				el[func] = function () {};
+			}
+	
+			$(el).unbind(eventType, handler);
+		},
+	
+		/**
+		 * Fire an event on a custom object
+		 * @param {Object} el
+		 * @param {String} type
+		 * @param {Object} eventArguments
+		 * @param {Function} defaultFunction
+		 */
+		fireEvent: function (el, type, eventArguments, defaultFunction) {
+			var event = $.Event(type),
+				detachedType = 'detached' + type,
+				defaultPrevented;
+	
+			// Remove warnings in Chrome when accessing layerX and layerY. Although Highcharts
+			// never uses these properties, Chrome includes them in the default click event and
+			// raises the warning when they are copied over in the extend statement below.
+			//
+			// To avoid problems in IE (see #1010) where we cannot delete the properties and avoid
+			// testing if they are there (warning in chrome) the only option is to test if running IE.
+			if (!isIE && eventArguments) {
+				delete eventArguments.layerX;
+				delete eventArguments.layerY;
+			}
+	
+			extend(event, eventArguments);
+	
+			// Prevent jQuery from triggering the object method that is named the
+			// same as the event. For example, if the event is 'select', jQuery
+			// attempts calling el.select and it goes into a loop.
+			if (el[type]) {
+				el[detachedType] = el[type];
+				el[type] = null;
+			}
+	
+			// Wrap preventDefault and stopPropagation in try/catch blocks in
+			// order to prevent JS errors when cancelling events on non-DOM
+			// objects. #615.
+			/*jslint unparam: true*/
+			$.each(['preventDefault', 'stopPropagation'], function (i, fn) {
+				var base = event[fn];
+				event[fn] = function () {
+					try {
+						base.call(event);
+					} catch (e) {
+						if (fn === 'preventDefault') {
+							defaultPrevented = true;
+						}
+					}
+				};
+			});
+			/*jslint unparam: false*/
+	
+			// trigger it
+			$(el).trigger(event);
+	
+			// attach the method
+			if (el[detachedType]) {
+				el[type] = el[detachedType];
+				el[detachedType] = null;
+			}
+	
+			if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) {
+				defaultFunction(event);
+			}
+		},
+		
+		/**
+		 * Extension method needed for MooTools
+		 */
+		washMouseEvent: function (e) {
+			var ret = e.originalEvent || e;
+			
+			// computed by jQuery, needed by IE8
+			if (ret.pageX === UNDEFINED) { // #1236
+				ret.pageX = e.pageX;
+				ret.pageY = e.pageY;
+			}
+			
+			return ret;
+		},
+	
+		/**
+		 * Animate a HTML element or SVG element wrapper
+		 * @param {Object} el
+		 * @param {Object} params
+		 * @param {Object} options jQuery-like animation options: duration, easing, callback
+		 */
+		animate: function (el, params, options) {
+			var $el = $(el);
+			if (params.d) {
+				el.toD = params.d; // keep the array form for paths, used in $.fx.step.d
+				params.d = 1; // because in jQuery, animating to an array has a different meaning
+			}
+	
+			$el.stop();
+			$el.animate(params, options);
+	
+		},
+		/**
+		 * Stop running animation
+		 */
+		stop: function (el) {
+			$(el).stop();
+		}
+	});
+}(win.jQuery));
+
+
+// check for a custom HighchartsAdapter defined prior to this file
+var globalAdapter = win.HighchartsAdapter,
+	adapter = globalAdapter || {};
+	
+// Initialize the adapter
+if (globalAdapter) {
+	globalAdapter.init.call(globalAdapter, pathAnim);
+}
+
+
+	// Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object
+	// and all the utility functions will be null. In that case they are populated by the
+	// default adapters below.
+var adapterRun = adapter.adapterRun,
+	getScript = adapter.getScript,
+	inArray = adapter.inArray,
+	each = adapter.each,
+	grep = adapter.grep,
+	offset = adapter.offset,
+	map = adapter.map,
+	merge = adapter.merge,
+	addEvent = adapter.addEvent,
+	removeEvent = adapter.removeEvent,
+	fireEvent = adapter.fireEvent,
+	washMouseEvent = adapter.washMouseEvent,
+	animate = adapter.animate,
+	stop = adapter.stop;
+
+
+
+/* ****************************************************************************
+ * Handle the options                                                         *
+ *****************************************************************************/
+var
+
+defaultLabelOptions = {
+	enabled: true,
+	// rotation: 0,
+	align: 'center',
+	x: 0,
+	y: 15,
+	/*formatter: function () {
+		return this.value;
+	},*/
+	style: {
+		color: '#666',
+		fontSize: '11px',
+		lineHeight: '14px'
+	}
+};
+
+defaultOptions = {
+	colors: ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE',
+		'#DB843D', '#92A8CD', '#A47D7C', '#B5CA92'],
+	symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'],
+	lang: {
+		loading: 'Loading...',
+		months: ['January', 'February', 'March', 'April', 'May', 'June', 'July',
+				'August', 'September', 'October', 'November', 'December'],
+		shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+		weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+		decimalPoint: '.',
+		numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'], // SI prefixes used in axis labels
+		resetZoom: 'Reset zoom',
+		resetZoomTitle: 'Reset zoom level 1:1',
+		thousandsSep: ','
+	},
+	global: {
+		useUTC: true,
+		canvasToolsURL: 'http://code.highcharts.com/2.3.5/modules/canvas-tools.js',
+		VMLRadialGradientURL: 'http://code.highcharts.com/2.3.5/gfx/vml-radial-gradient.png'
+	},
+	chart: {
+		//animation: true,
+		//alignTicks: false,
+		//reflow: true,
+		//className: null,
+		//events: { load, selection },
+		//margin: [null],
+		//marginTop: null,
+		//marginRight: null,
+		//marginBottom: null,
+		//marginLeft: null,
+		borderColor: '#4572A7',
+		//borderWidth: 0,
+		borderRadius: 5,
+		defaultSeriesType: 'line',
+		ignoreHiddenSeries: true,
+		//inverted: false,
+		//shadow: false,
+		spacingTop: 10,
+		spacingRight: 10,
+		spacingBottom: 15,
+		spacingLeft: 10,
+		style: {
+			fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', // default font
+			fontSize: '12px'
+		},
+		backgroundColor: '#FFFFFF',
+		//plotBackgroundColor: null,
+		plotBorderColor: '#C0C0C0',
+		//plotBorderWidth: 0,
+		//plotShadow: false,
+		//zoomType: ''
+		resetZoomButton: {
+			theme: {
+				zIndex: 20
+			},
+			position: {
+				align: 'right',
+				x: -10,
+				//verticalAlign: 'top',
+				y: 10
+			}
+			// relativeTo: 'plot'
+		}
+	},
+	title: {
+		text: 'Chart title',
+		align: 'center',
+		// floating: false,
+		// margin: 15,
+		// x: 0,
+		// verticalAlign: 'top',
+		y: 15,
+		style: {
+			color: '#3E576F',
+			fontSize: '16px'
+		}
+
+	},
+	subtitle: {
+		text: '',
+		align: 'center',
+		// floating: false
+		// x: 0,
+		// verticalAlign: 'top',
+		y: 30,
+		style: {
+			color: '#6D869F'
+		}
+	},
+
+	plotOptions: {
+		line: { // base series options
+			allowPointSelect: false,
+			showCheckbox: false,
+			animation: {
+				duration: 1000
+			},
+			//connectNulls: false,
+			//cursor: 'default',
+			//clip: true,
+			//dashStyle: null,
+			//enableMouseTracking: true,
+			events: {},
+			//legendIndex: 0,
+			lineWidth: 2,
+			shadow: true,
+			// stacking: null,
+			marker: {
+				enabled: true,
+				//symbol: null,
+				lineWidth: 0,
+				radius: 4,
+				lineColor: '#FFFFFF',
+				//fillColor: null,
+				states: { // states for a single point
+					hover: {
+						enabled: true
+						//radius: base + 2
+					},
+					select: {
+						fillColor: '#FFFFFF',
+						lineColor: '#000000',
+						lineWidth: 2
+					}
+				}
+			},
+			point: {
+				events: {}
+			},
+			dataLabels: merge(defaultLabelOptions, {
+				enabled: false,
+				formatter: function () {
+					return this.y;
+				},
+				verticalAlign: 'bottom', // above singular point
+				y: 0
+				// backgroundColor: undefined,
+				// borderColor: undefined,
+				// borderRadius: undefined,
+				// borderWidth: undefined,
+				// padding: 3,
+				// shadow: false
+			}),
+			cropThreshold: 300, // draw points outside the plot area when the number of points is less than this
+			pointRange: 0,
+			//pointStart: 0,
+			//pointInterval: 1,
+			showInLegend: true,
+			states: { // states for the entire series
+				hover: {
+					//enabled: false,
+					//lineWidth: base + 1,
+					marker: {
+						// lineWidth: base + 1,
+						// radius: base + 1
+					}
+				},
+				select: {
+					marker: {}
+				}
+			},
+			stickyTracking: true
+			//tooltip: {
+				//pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b>'
+				//valueDecimals: null,
+				//xDateFormat: '%A, %b %e, %Y',
+				//valuePrefix: '',
+				//ySuffix: ''				
+			//}
+			// turboThreshold: 1000
+			// zIndex: null
+		}
+	},
+	labels: {
+		//items: [],
+		style: {
+			//font: defaultFont,
+			position: ABSOLUTE,
+			color: '#3E576F'
+		}
+	},
+	legend: {
+		enabled: true,
+		align: 'center',
+		//floating: false,
+		layout: 'horizontal',
+		labelFormatter: function () {
+			return this.name;
+		},
+		borderWidth: 1,
+		borderColor: '#909090',
+		borderRadius: 5,
+		navigation: {
+			// animation: true,
+			activeColor: '#3E576F',
+			// arrowSize: 12
+			inactiveColor: '#CCC'
+			// style: {} // text styles
+		},
+		// margin: 10,
+		// reversed: false,
+		shadow: false,
+		// backgroundColor: null,
+		/*style: {
+			padding: '5px'
+		},*/
+		itemStyle: {
+			cursor: 'pointer',
+			color: '#3E576F',
+			fontSize: '12px'
+		},
+		itemHoverStyle: {
+			//cursor: 'pointer', removed as of #601
+			color: '#000'
+		},
+		itemHiddenStyle: {
+			color: '#CCC'
+		},
+		itemCheckboxStyle: {
+			position: ABSOLUTE,
+			width: '13px', // for IE precision
+			height: '13px'
+		},
+		// itemWidth: undefined,
+		symbolWidth: 16,
+		symbolPadding: 5,
+		verticalAlign: 'bottom',
+		// width: undefined,
+		x: 0,
+		y: 0
+	},
+
+	loading: {
+		// hideDuration: 100,
+		labelStyle: {
+			fontWeight: 'bold',
+			position: RELATIVE,
+			top: '1em'
+		},
+		// showDuration: 0,
+		style: {
+			position: ABSOLUTE,
+			backgroundColor: 'white',
+			opacity: 0.5,
+			textAlign: 'center'
+		}
+	},
+
+	tooltip: {
+		enabled: true,
+		//crosshairs: null,
+		backgroundColor: 'rgba(255, 255, 255, .85)',
+		borderWidth: 2,
+		borderRadius: 5,
+		dateTimeLabelFormats: { 
+			millisecond: '%A, %b %e, %H:%M:%S.%L',
+			second: '%A, %b %e, %H:%M:%S',
+			minute: '%A, %b %e, %H:%M',
+			hour: '%A, %b %e, %H:%M',
+			day: '%A, %b %e, %Y',
+			week: 'Week from %A, %b %e, %Y',
+			month: '%B %Y',
+			year: '%Y'
+		},
+		//formatter: defaultFormatter,
+		headerFormat: '<span style="font-size: 10px">{point.key}</span><br/>',
+		pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b><br/>',
+		shadow: true,
+		shared: useCanVG,
+		snap: isTouchDevice ? 25 : 10,
+		style: {
+			color: '#333333',
+			fontSize: '12px',
+			padding: '5px',
+			whiteSpace: 'nowrap'
+		}
+		//xDateFormat: '%A, %b %e, %Y',
+		//valueDecimals: null,
+		//valuePrefix: '',
+		//valueSuffix: ''
+	},
+
+	credits: {
+		enabled: true,
+		text: 'Highcharts.com',
+		href: 'http://www.highcharts.com',
+		position: {
+			align: 'right',
+			x: -10,
+			verticalAlign: 'bottom',
+			y: -5
+		},
+		style: {
+			cursor: 'pointer',
+			color: '#909090',
+			fontSize: '10px'
+		}
+	}
+};
+
+
+
+
+// Series defaults
+var defaultPlotOptions = defaultOptions.plotOptions,
+	defaultSeriesOptions = defaultPlotOptions.line;
+
+// set the default time methods
+setTimeMethods();
+
+
+
+/**
+ * Set the time methods globally based on the useUTC option. Time method can be either
+ * local time or UTC (default).
+ */
+function setTimeMethods() {
+	var useUTC = defaultOptions.global.useUTC,
+		GET = useUTC ? 'getUTC' : 'get',
+		SET = useUTC ? 'setUTC' : 'set';
+
+	makeTime = useUTC ? Date.UTC : function (year, month, date, hours, minutes, seconds) {
+		return new Date(
+			year,
+			month,
+			pick(date, 1),
+			pick(hours, 0),
+			pick(minutes, 0),
+			pick(seconds, 0)
+		).getTime();
+	};
+	getMinutes =  GET + 'Minutes';
+	getHours =    GET + 'Hours';
+	getDay =      GET + 'Day';
+	getDate =     GET + 'Date';
+	getMonth =    GET + 'Month';
+	getFullYear = GET + 'FullYear';
+	setMinutes =  SET + 'Minutes';
+	setHours =    SET + 'Hours';
+	setDate =     SET + 'Date';
+	setMonth =    SET + 'Month';
+	setFullYear = SET + 'FullYear';
+
+}
+
+/**
+ * Merge the default options with custom options and return the new options structure
+ * @param {Object} options The new custom options
+ */
+function setOptions(options) {
+	
+	// Pull out axis options and apply them to the respective default axis options 
+	/*defaultXAxisOptions = merge(defaultXAxisOptions, options.xAxis);
+	defaultYAxisOptions = merge(defaultYAxisOptions, options.yAxis);
+	options.xAxis = options.yAxis = UNDEFINED;*/
+	
+	// Merge in the default options
+	defaultOptions = merge(defaultOptions, options);
+	
+	// Apply UTC
+	setTimeMethods();
+
+	return defaultOptions;
+}
+
+/**
+ * Get the updated default options. Merely exposing defaultOptions for outside modules
+ * isn't enough because the setOptions method creates a new object.
+ */
+function getOptions() {
+	return defaultOptions;
+}
+
+
+
+/**
+ * Handle color operations. The object methods are chainable.
+ * @param {String} input The input color in either rbga or hex format
+ */
+var Color = function (input) {
+	// declare variables
+	var rgba = [], result;
+
+	/**
+	 * Parse the input color to rgba array
+	 * @param {String} input
+	 */
+	function init(input) {
+
+		// rgba
+		result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/.exec(input);
+		if (result) {
+			rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)];
+		} else { // hex
+			result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(input);
+			if (result) {
+				rgba = [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1];
+			}
+		}
+
+	}
+	/**
+	 * Return the color a specified format
+	 * @param {String} format
+	 */
+	function get(format) {
+		var ret;
+
+		// it's NaN if gradient colors on a column chart
+		if (rgba && !isNaN(rgba[0])) {
+			if (format === 'rgb') {
+				ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')';
+			} else if (format === 'a') {
+				ret = rgba[3];
+			} else {
+				ret = 'rgba(' + rgba.join(',') + ')';
+			}
+		} else {
+			ret = input;
+		}
+		return ret;
+	}
+
+	/**
+	 * Brighten the color
+	 * @param {Number} alpha
+	 */
+	function brighten(alpha) {
+		if (isNumber(alpha) && alpha !== 0) {
+			var i;
+			for (i = 0; i < 3; i++) {
+				rgba[i] += pInt(alpha * 255);
+
+				if (rgba[i] < 0) {
+					rgba[i] = 0;
+				}
+				if (rgba[i] > 255) {
+					rgba[i] = 255;
+				}
+			}
+		}
+		return this;
+	}
+	/**
+	 * Set the color's opacity to a given alpha value
+	 * @param {Number} alpha
+	 */
+	function setOpacity(alpha) {
+		rgba[3] = alpha;
+		return this;
+	}
+
+	// initialize: parse the input
+	init(input);
+
+	// public methods
+	return {
+		get: get,
+		brighten: brighten,
+		setOpacity: setOpacity
+	};
+};
+
+
+/**
+ * A wrapper object for SVG elements
+ */
+function SVGElement() {}
+
+SVGElement.prototype = {
+	/**
+	 * Initialize the SVG renderer
+	 * @param {Object} renderer
+	 * @param {String} nodeName
+	 */
+	init: function (renderer, nodeName) {
+		var wrapper = this;
+		wrapper.element = nodeName === 'span' ?
+			createElement(nodeName) :
+			doc.createElementNS(SVG_NS, nodeName);
+		wrapper.renderer = renderer;
+		/**
+		 * A collection of attribute setters. These methods, if defined, are called right before a certain
+		 * attribute is set on an element wrapper. Returning false prevents the default attribute
+		 * setter to run. Returning a value causes the default setter to set that value. Used in
+		 * Renderer.label.
+		 */
+		wrapper.attrSetters = {};
+	},
+	/**
+	 * Animate a given attribute
+	 * @param {Object} params
+	 * @param {Number} options The same options as in jQuery animation
+	 * @param {Function} complete Function to perform at the end of animation
+	 */
+	animate: function (params, options, complete) {
+		var animOptions = pick(options, globalAnimation, true);
+		stop(this); // stop regardless of animation actually running, or reverting to .attr (#607)
+		if (animOptions) {
+			animOptions = merge(animOptions);
+			if (complete) { // allows using a callback with the global animation without overwriting it
+				animOptions.complete = complete;
+			}
+			animate(this, params, animOptions);
+		} else {
+			this.attr(params);
+			if (complete) {
+				complete();
+			}
+		}
+	},
+	/**
+	 * Set or get a given attribute
+	 * @param {Object|String} hash
+	 * @param {Mixed|Undefined} val
+	 */
+	attr: function (hash, val) {
+		var wrapper = this,
+			key,
+			value,
+			result,
+			i,
+			child,
+			element = wrapper.element,
+			nodeName = element.nodeName.toLowerCase(), // Android2 requires lower for "text"
+			renderer = wrapper.renderer,
+			skipAttr,
+			titleNode,
+			attrSetters = wrapper.attrSetters,
+			shadows = wrapper.shadows,
+			hasSetSymbolSize,
+			doTransform,
+			ret = wrapper;
+
+		// single key-value pair
+		if (isString(hash) && defined(val)) {
+			key = hash;
+			hash = {};
+			hash[key] = val;
+		}
+
+		// used as a getter: first argument is a string, second is undefined
+		if (isString(hash)) {
+			key = hash;
+			if (nodeName === 'circle') {
+				key = { x: 'cx', y: 'cy' }[key] || key;
+			} else if (key === 'strokeWidth') {
+				key = 'stroke-width';
+			}
+			ret = attr(element, key) || wrapper[key] || 0;
+
+			if (key !== 'd' && key !== 'visibility') { // 'd' is string in animation step
+				ret = parseFloat(ret);
+			}
+
+		// setter
+		} else {
+
+			for (key in hash) {
+				skipAttr = false; // reset
+				value = hash[key];
+
+				// check for a specific attribute setter
+				result = attrSetters[key] && attrSetters[key].call(wrapper, value, key);
+
+				if (result !== false) {
+					if (result !== UNDEFINED) {
+						value = result; // the attribute setter has returned a new value to set
+					}
+
+					// paths
+					if (key === 'd') {
+						if (value && value.join) { // join path
+							value = value.join(' ');
+						}
+						if (/(NaN| {2}|^$)/.test(value)) {
+							value = 'M 0 0';
+						}
+						//wrapper.d = value; // shortcut for animations
+
+					// update child tspans x values
+					} else if (key === 'x' && nodeName === 'text') {
+						for (i = 0; i < element.childNodes.length; i++) {
+							child = element.childNodes[i];
+							// if the x values are equal, the tspan represents a linebreak
+							if (attr(child, 'x') === attr(element, 'x')) {
+								//child.setAttribute('x', value);
+								attr(child, 'x', value);
+							}
+						}
+
+						if (wrapper.rotation) {
+							attr(element, 'transform', 'rotate(' + wrapper.rotation + ' ' + value + ' ' +
+								pInt(hash.y || attr(element, 'y')) + ')');
+						}
+
+					// apply gradients
+					} else if (key === 'fill') {
+						value = renderer.color(value, element, key);
+
+					// circle x and y
+					} else if (nodeName === 'circle' && (key === 'x' || key === 'y')) {
+						key = { x: 'cx', y: 'cy' }[key] || key;
+
+					// rectangle border radius
+					} else if (nodeName === 'rect' && key === 'r') {
+						attr(element, {
+							rx: value,
+							ry: value
+						});
+						skipAttr = true;
+
+					// translation and text rotation
+					} else if (key === 'translateX' || key === 'translateY' || key === 'rotation' || key === 'verticalAlign') {
+						doTransform = true;
+						skipAttr = true;
+
+					// apply opacity as subnode (required by legacy WebKit and Batik)
+					} else if (key === 'stroke') {
+						value = renderer.color(value, element, key);
+
+					// emulate VML's dashstyle implementation
+					} else if (key === 'dashstyle') {
+						key = 'stroke-dasharray';
+						value = value && value.toLowerCase();
+						if (value === 'solid') {
+							value = NONE;
+						} else if (value) {
+							value = value
+								.replace('shortdashdotdot', '3,1,1,1,1,1,')
+								.replace('shortdashdot', '3,1,1,1')
+								.replace('shortdot', '1,1,')
+								.replace('shortdash', '3,1,')
+								.replace('longdash', '8,3,')
+								.replace(/dot/g, '1,3,')
+								.replace('dash', '4,3,')
+								.replace(/,$/, '')
+								.split(','); // ending comma
+
+							i = value.length;
+							while (i--) {
+								value[i] = pInt(value[i]) * hash['stroke-width'];
+							}
+							value = value.join(',');
+						}
+
+					// special
+					} else if (key === 'isTracker') {
+						wrapper[key] = value;
+
+					// IE9/MooTools combo: MooTools returns objects instead of numbers and IE9 Beta 2
+					// is unable to cast them. Test again with final IE9.
+					} else if (key === 'width') {
+						value = pInt(value);
+
+					// Text alignment
+					} else if (key === 'align') {
+						key = 'text-anchor';
+						value = { left: 'start', center: 'middle', right: 'end' }[value];
+
+					// Title requires a subnode, #431
+					} else if (key === 'title') {
+						titleNode = element.getElementsByTagName('title')[0];
+						if (!titleNode) {
+							titleNode = doc.createElementNS(SVG_NS, 'title');
+							element.appendChild(titleNode);
+						}
+						titleNode.textContent = value;
+					}
+
+					// jQuery animate changes case
+					if (key === 'strokeWidth') {
+						key = 'stroke-width';
+					}
+
+					// Chrome/Win < 6 bug (http://code.google.com/p/chromium/issues/detail?id=15461), #1369
+					if (key === 'stroke-width' && value === 0 && (isWebKit || renderer.forExport)) {
+						value = 0.000001;
+					}
+
+					// symbols
+					if (wrapper.symbolName && /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(key)) {
+
+
+						if (!hasSetSymbolSize) {
+							wrapper.symbolAttr(hash);
+							hasSetSymbolSize = true;
+						}
+						skipAttr = true;
+					}
+
+					// let the shadow follow the main element
+					if (shadows && /^(width|height|visibility|x|y|d|transform)$/.test(key)) {
+						i = shadows.length;
+						while (i--) {
+							attr(
+								shadows[i], 
+								key, 
+								key === 'height' ? 
+									mathMax(value - (shadows[i].cutHeight || 0), 0) :
+									value
+							);
+						}
+					}
+
+					// validate heights
+					if ((key === 'width' || key === 'height') && nodeName === 'rect' && value < 0) {
+						value = 0;
+					}
+
+					// Record for animation and quick access without polling the DOM
+					wrapper[key] = value;
+					
+					// Update transform
+					if (doTransform) {
+						wrapper.updateTransform();
+					}
+
+
+					if (key === 'text') {
+						// Delete bBox memo when the text changes
+						if (value !== wrapper.textStr) {
+							delete wrapper.bBox;
+						}
+						wrapper.textStr = value;
+						if (wrapper.added) {
+							renderer.buildText(wrapper);
+						}
+					} else if (!skipAttr) {
+						attr(element, key, value);
+					}
+
+				}
+
+			}
+
+		}
+		
+		return ret;
+	},
+
+	/**
+	 * If one of the symbol size affecting parameters are changed,
+	 * check all the others only once for each call to an element's
+	 * .attr() method
+	 * @param {Object} hash
+	 */
+	symbolAttr: function (hash) {
+		var wrapper = this;
+
+		each(['x', 'y', 'r', 'start', 'end', 'width', 'height', 'innerR', 'anchorX', 'anchorY'], function (key) {
+			wrapper[key] = pick(hash[key], wrapper[key]);
+		});
+
+		wrapper.attr({
+			d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
+		});
+	},
+
+	/**
+	 * Apply a clipping path to this object
+	 * @param {String} id
+	 */
+	clip: function (clipRect) {
+		return this.attr('clip-path', clipRect ? 'url(' + this.renderer.url + '#' + clipRect.id + ')' : NONE);
+	},
+
+	/**
+	 * Calculate the coordinates needed for drawing a rectangle crisply and return the
+	 * calculated attributes
+	 * @param {Number} strokeWidth
+	 * @param {Number} x
+	 * @param {Number} y
+	 * @param {Number} width
+	 * @param {Number} height
+	 */
+	crisp: function (strokeWidth, x, y, width, height) {
+
+		var wrapper = this,
+			key,
+			attribs = {},
+			values = {},
+			normalizer;
+
+		strokeWidth = strokeWidth || wrapper.strokeWidth || (wrapper.attr && wrapper.attr('stroke-width')) || 0;
+		normalizer = mathRound(strokeWidth) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors
+
+		// normalize for crisp edges
+		values.x = mathFloor(x || wrapper.x || 0) + normalizer;
+		values.y = mathFloor(y || wrapper.y || 0) + normalizer;
+		values.width = mathFloor((width || wrapper.width || 0) - 2 * normalizer);
+		values.height = mathFloor((height || wrapper.height || 0) - 2 * normalizer);
+		values.strokeWidth = strokeWidth;
+
+		for (key in values) {
+			if (wrapper[key] !== values[key]) { // only set attribute if changed
+				wrapper[key] = attribs[key] = values[key];
+			}
+		}
+
+		return attribs;
+	},
+
+	/**
+	 * Set styles for the element
+	 * @param {Object} styles
+	 */
+	css: function (styles) {
+		/*jslint unparam: true*//* allow unused param a in the regexp function below */
+		var elemWrapper = this,
+			elem = elemWrapper.element,
+			textWidth = styles && styles.width && elem.nodeName.toLowerCase() === 'text',
+			n,
+			serializedCss = '',
+			hyphenate = function (a, b) { return '-' + b.toLowerCase(); };
+		/*jslint unparam: false*/
+
+		// convert legacy
+		if (styles && styles.color) {
+			styles.fill = styles.color;
+		}
+
+		// Merge the new styles with the old ones
+		styles = extend(
+			elemWrapper.styles,
+			styles
+		);
+
+		// store object
+		elemWrapper.styles = styles;
+		
+		
+		// Don't handle line wrap on canvas
+		if (useCanVG && textWidth) {
+			delete styles.width;
+		}
+			
+		// serialize and set style attribute
+		if (isIE && !hasSVG) { // legacy IE doesn't support setting style attribute
+			if (textWidth) {
+				delete styles.width;
+			}
+			css(elemWrapper.element, styles);
+		} else {
+			for (n in styles) {
+				serializedCss += n.replace(/([A-Z])/g, hyphenate) + ':' + styles[n] + ';';
+			}
+			elemWrapper.attr({
+				style: serializedCss
+			});
+		}
+
+
+		// re-build text
+		if (textWidth && elemWrapper.added) {
+			elemWrapper.renderer.buildText(elemWrapper);
+		}
+
+		return elemWrapper;
+	},
+
+	/**
+	 * Add an event listener
+	 * @param {String} eventType
+	 * @param {Function} handler
+	 */
+	on: function (eventType, handler) {
+		// touch
+		if (hasTouch && eventType === 'click') {
+			this.element.ontouchstart = function (e) {
+				e.preventDefault();
+				handler();
+			};
+		}
+		// simplest possible event model for internal use
+		this.element['on' + eventType] = handler;
+		return this;
+	},
+	
+	/**
+	 * Set the coordinates needed to draw a consistent radial gradient across
+	 * pie slices regardless of positioning inside the chart. The format is
+	 * [centerX, centerY, diameter] in pixels.
+	 */
+	setRadialReference: function (coordinates) {
+		this.element.radialReference = coordinates;
+		return this;
+	},
+
+	/**
+	 * Move an object and its children by x and y values
+	 * @param {Number} x
+	 * @param {Number} y
+	 */
+	translate: function (x, y) {
+		return this.attr({
+			translateX: x,
+			translateY: y
+		});
+	},
+
+	/**
+	 * Invert a group, rotate and flip
+	 */
+	invert: function () {
+		var wrapper = this;
+		wrapper.inverted = true;
+		wrapper.updateTransform();
+		return wrapper;
+	},
+
+	/**
+	 * Apply CSS to HTML elements. This is used in text within SVG rendering and
+	 * by the VML renderer
+	 */
+	htmlCss: function (styles) {
+		var wrapper = this,
+			element = wrapper.element,
+			textWidth = styles && element.tagName === 'SPAN' && styles.width;
+
+		if (textWidth) {
+			delete styles.width;
+			wrapper.textWidth = textWidth;
+			wrapper.updateTransform();
+		}
+
+		wrapper.styles = extend(wrapper.styles, styles);
+		css(wrapper.element, styles);
+
+		return wrapper;
+	},
+
+
+
+	/**
+	 * VML and useHTML method for calculating the bounding box based on offsets
+	 * @param {Boolean} refresh Whether to force a fresh value from the DOM or to
+	 * use the cached value
+	 *
+	 * @return {Object} A hash containing values for x, y, width and height
+	 */
+
+	htmlGetBBox: function () {
+		var wrapper = this,
+			element = wrapper.element,
+			bBox = wrapper.bBox;
+
+		// faking getBBox in exported SVG in legacy IE
+		if (!bBox) {
+			// faking getBBox in exported SVG in legacy IE (is this a duplicate of the fix for #1079?)
+			if (element.nodeName === 'text') {
+				element.style.position = ABSOLUTE;
+			}
+
+			bBox = wrapper.bBox = {
+				x: element.offsetLeft,
+				y: element.offsetTop,
+				width: element.offsetWidth,
+				height: element.offsetHeight
+			};
+		}
+
+		return bBox;
+	},
+
+	/**
+	 * VML override private method to update elements based on internal
+	 * properties based on SVG transform
+	 */
+	htmlUpdateTransform: function () {
+		// aligning non added elements is expensive
+		if (!this.added) {
+			this.alignOnAdd = true;
+			return;
+		}
+
+		var wrapper = this,
+			renderer = wrapper.renderer,
+			elem = wrapper.element,
+			translateX = wrapper.translateX || 0,
+			translateY = wrapper.translateY || 0,
+			x = wrapper.x || 0,
+			y = wrapper.y || 0,
+			align = wrapper.textAlign || 'left',
+			alignCorrection = { left: 0, center: 0.5, right: 1 }[align],
+			nonLeft = align && align !== 'left',
+			shadows = wrapper.shadows;
+
+		// apply translate
+		if (translateX || translateY) {
+			css(elem, {
+				marginLeft: translateX,
+				marginTop: translateY
+			});
+			if (shadows) { // used in labels/tooltip
+				each(shadows, function (shadow) {
+					css(shadow, {
+						marginLeft: translateX + 1,
+						marginTop: translateY + 1
+					});
+				});
+			}
+		}
+
+		// apply inversion
+		if (wrapper.inverted) { // wrapper is a group
+			each(elem.childNodes, function (child) {
+				renderer.invertChild(child, elem);
+			});
+		}
+
+		if (elem.tagName === 'SPAN') {
+
+			var width, height,
+				rotation = wrapper.rotation,
+				baseline,
+				radians = 0,
+				costheta = 1,
+				sintheta = 0,
+				quad,
+				textWidth = pInt(wrapper.textWidth),
+				xCorr = wrapper.xCorr || 0,
+				yCorr = wrapper.yCorr || 0,
+				currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(','),
+				rotationStyle = {},
+				cssTransformKey;
+
+			if (currentTextTransform !== wrapper.cTT) { // do the calculations and DOM access only if properties changed
+
+				if (defined(rotation)) {
+					
+					if (renderer.isSVG) { // #916
+						cssTransformKey = isIE ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : '';
+						rotationStyle[cssTransformKey] = rotationStyle.transform = 'rotate(' + rotation + 'deg)';
+						
+					} else {
+						radians = rotation * deg2rad; // deg to rad
+						costheta = mathCos(radians);
+						sintheta = mathSin(radians);
+	
+						// Adjust for alignment and rotation. Rotation of useHTML content is not yet implemented
+						// but it can probably be implemented for Firefox 3.5+ on user request. FF3.5+
+						// has support for CSS3 transform. The getBBox method also needs to be updated
+						// to compensate for the rotation, like it currently does for SVG.
+						// Test case: http://highcharts.com/tests/?file=text-rotation
+						rotationStyle.filter = rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta,
+								', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta,
+								', sizingMethod=\'auto expand\')'].join('') : NONE;
+					}
+					css(elem, rotationStyle);
+				}
+
+				width = pick(wrapper.elemWidth, elem.offsetWidth);
+				height = pick(wrapper.elemHeight, elem.offsetHeight);
+
+				// update textWidth
+				if (width > textWidth && /[ \-]/.test(elem.textContent || elem.innerText)) { // #983, #1254
+					css(elem, {
+						width: textWidth + PX,
+						display: 'block',
+						whiteSpace: 'normal'
+					});
+					width = textWidth;
+				}
+
+				// correct x and y
+				baseline = renderer.fontMetrics(elem.style.fontSize).b;
+				xCorr = costheta < 0 && -width;
+				yCorr = sintheta < 0 && -height;
+
+				// correct for baseline and corners spilling out after rotation
+				quad = costheta * sintheta < 0;
+				xCorr += sintheta * baseline * (quad ? 1 - alignCorrection : alignCorrection);
+				yCorr -= costheta * baseline * (rotation ? (quad ? alignCorrection : 1 - alignCorrection) : 1);
+
+				// correct for the length/height of the text
+				if (nonLeft) {
+					xCorr -= width * alignCorrection * (costheta < 0 ? -1 : 1);
+					if (rotation) {
+						yCorr -= height * alignCorrection * (sintheta < 0 ? -1 : 1);
+					}
+					css(elem, {
+						textAlign: align
+					});
+				}
+
+				// record correction
+				wrapper.xCorr = xCorr;
+				wrapper.yCorr = yCorr;
+			}
+
+			// apply position with correction
+			css(elem, {
+				left: (x + xCorr) + PX,
+				top: (y + yCorr) + PX
+			});
+			
+			// force reflow in webkit to apply the left and top on useHTML element (#1249)
+			if (isWebKit) {
+				height = elem.offsetHeight; // assigned to height for JSLint purpose
+			}
+
+			// record current text transform
+			wrapper.cTT = currentTextTransform;
+		}
+	},
+
+	/**
+	 * Private method to update the transform attribute based on internal
+	 * properties
+	 */
+	updateTransform: function () {
+		var wrapper = this,
+			translateX = wrapper.translateX || 0,
+			translateY = wrapper.translateY || 0,
+			inverted = wrapper.inverted,
+			rotation = wrapper.rotation,
+			transform = [];
+
+		// flipping affects translate as adjustment for flipping around the group's axis
+		if (inverted) {
+			translateX += wrapper.attr('width');
+			translateY += wrapper.attr('height');
+		}
+
+		// apply translate
+		if (translateX || translateY) {
+			transform.push('translate(' + translateX + ',' + translateY + ')');
+		}
+
+		// apply rotation
+		if (inverted) {
+			transform.push('rotate(90) scale(-1,1)');
+		} else if (rotation) { // text rotation
+			transform.push('rotate(' + rotation + ' ' + (wrapper.x || 0) + ' ' + (wrapper.y || 0) + ')');
+		}
+
+		if (transform.length) {
+			attr(wrapper.element, 'transform', transform.join(' '));
+		}
+	},
+	/**
+	 * Bring the element to the front
+	 */
+	toFront: function () {
+		var element = this.element;
+		element.parentNode.appendChild(element);
+		return this;
+	},
+
+
+	/**
+	 * Break down alignment options like align, verticalAlign, x and y
+	 * to x and y relative to the chart.
+	 *
+	 * @param {Object} alignOptions
+	 * @param {Boolean} alignByTranslate
+	 * @param {Object} box The box to align to, needs a width and height
+	 *
+	 */
+	align: function (alignOptions, alignByTranslate, box) {
+		var elemWrapper = this;
+
+		if (!alignOptions) { // called on resize
+			alignOptions = elemWrapper.alignOptions;
+			alignByTranslate = elemWrapper.alignByTranslate;
+		} else { // first call on instanciate
+			elemWrapper.alignOptions = alignOptions;
+			elemWrapper.alignByTranslate = alignByTranslate;
+			if (!box) { // boxes other than renderer handle this internally
+				elemWrapper.renderer.alignedObjects.push(elemWrapper);
+			}
+		}
+
+		box = pick(box, elemWrapper.renderer);
+
+		var align = alignOptions.align,
+			vAlign = alignOptions.verticalAlign,
+			x = (box.x || 0) + (alignOptions.x || 0), // default: left align
+			y = (box.y || 0) + (alignOptions.y || 0), // default: top align
+			attribs = {};
+
+
+		// align
+		if (align === 'right' || align === 'center') {
+			x += (box.width - (alignOptions.width || 0)) /
+					{ right: 1, center: 2 }[align];
+		}
+		attribs[alignByTranslate ? 'translateX' : 'x'] = mathRound(x);
+
+
+		// vertical align
+		if (vAlign === 'bottom' || vAlign === 'middle') {
+			y += (box.height - (alignOptions.height || 0)) /
+					({ bottom: 1, middle: 2 }[vAlign] || 1);
+
+		}
+		attribs[alignByTranslate ? 'translateY' : 'y'] = mathRound(y);
+
+		// animate only if already placed
+		elemWrapper[elemWrapper.placed ? 'animate' : 'attr'](attribs);
+		elemWrapper.placed = true;
+		elemWrapper.alignAttr = attribs;
+
+		return elemWrapper;
+	},
+
+	/**
+	 * Get the bounding box (width, height, x and y) for the element
+	 */
+	getBBox: function () {
+		var wrapper = this,
+			bBox = wrapper.bBox,
+			renderer = wrapper.renderer,
+			width,
+			height,
+			rotation = wrapper.rotation,
+			element = wrapper.element,
+			styles = wrapper.styles,
+			rad = rotation * deg2rad;
+			
+		if (!bBox) {
+			// SVG elements
+			if (element.namespaceURI === SVG_NS || renderer.forExport) {
+				try { // Fails in Firefox if the container has display: none.
+					
+					bBox = element.getBBox ?
+						// SVG: use extend because IE9 is not allowed to change width and height in case
+						// of rotation (below)
+						extend({}, element.getBBox()) :
+						// Canvas renderer and legacy IE in export mode
+						{
+							width: element.offsetWidth,
+							height: element.offsetHeight
+						};
+				} catch (e) {}
+				
+				// If the bBox is not set, the try-catch block above failed. The other condition
+				// is for Opera that returns a width of -Infinity on hidden elements.
+				if (!bBox || bBox.width < 0) {
+					bBox = { width: 0, height: 0 };
+				}
+				
+	
+			// VML Renderer or useHTML within SVG
+			} else {
+				
+				bBox = wrapper.htmlGetBBox();
+				
+			}
+			
+			// True SVG elements as well as HTML elements in modern browsers using the .useHTML option
+			// need to compensated for rotation
+			if (renderer.isSVG) {
+				width = bBox.width;
+				height = bBox.height;
+				
+				// Workaround for wrong bounding box in IE9 and IE10 (#1101)
+				if (isIE && styles && styles.fontSize === '11px' && height === 22.700000762939453) {
+					bBox.height = height = 14;
+				}
+			
+				// Adjust for rotated text
+				if (rotation) {
+					bBox.width = mathAbs(height * mathSin(rad)) + mathAbs(width * mathCos(rad));
+					bBox.height = mathAbs(height * mathCos(rad)) + mathAbs(width * mathSin(rad));
+				}
+			}
+			
+			wrapper.bBox = bBox;
+		}
+		return bBox;
+	},
+
+	/**
+	 * Show the element
+	 */
+	show: function () {
+		return this.attr({ visibility: VISIBLE });
+	},
+
+	/**
+	 * Hide the element
+	 */
+	hide: function () {
+		return this.attr({ visibility: HIDDEN });
+	},
+	
+	/**
+	 * Add the element
+	 * @param {Object|Undefined} parent Can be an element, an element wrapper or undefined
+	 *    to append the element to the renderer.box.
+	 */
+	add: function (parent) {
+
+		var renderer = this.renderer,
+			parentWrapper = parent || renderer,
+			parentNode = parentWrapper.element || renderer.box,
+			childNodes = parentNode.childNodes,
+			element = this.element,
+			zIndex = attr(element, 'zIndex'),
+			otherElement,
+			otherZIndex,
+			i,
+			inserted;
+			
+		if (parent) {
+			this.parentGroup = parent;
+		}
+
+		// mark as inverted
+		this.parentInverted = parent && parent.inverted;
+
+		// build formatted text
+		if (this.textStr !== undefined) {
+			renderer.buildText(this);
+		}
+
+		// mark the container as having z indexed children
+		if (zIndex) {
+			parentWrapper.handleZ = true;
+			zIndex = pInt(zIndex);
+		}
+
+		// insert according to this and other elements' zIndex
+		if (parentWrapper.handleZ) { // this element or any of its siblings has a z index
+			for (i = 0; i < childNodes.length; i++) {
+				otherElement = childNodes[i];
+				otherZIndex = attr(otherElement, 'zIndex');
+				if (otherElement !== element && (
+						// insert before the first element with a higher zIndex
+						pInt(otherZIndex) > zIndex ||
+						// if no zIndex given, insert before the first element with a zIndex
+						(!defined(zIndex) && defined(otherZIndex))
+
+						)) {
+					parentNode.insertBefore(element, otherElement);
+					inserted = true;
+					break;
+				}
+			}
+		}
+
+		// default: append at the end
+		if (!inserted) {
+			parentNode.appendChild(element);
+		}
+
+		// mark as added
+		this.added = true;
+
+		// fire an event for internal hooks
+		fireEvent(this, 'add');
+
+		return this;
+	},
+
+	/**
+	 * Removes a child either by removeChild or move to garbageBin.
+	 * Issue 490; in VML removeChild results in Orphaned nodes according to sIEve, discardElement does not.
+	 */
+	safeRemoveChild: function (element) {
+		var parentNode = element.parentNode;
+		if (parentNode) {
+			parentNode.removeChild(element);
+		}
+	},
+
+	/**
+	 * Destroy the element and element wrapper
+	 */
+	destroy: function () {
+		var wrapper = this,
+			element = wrapper.element || {},
+			shadows = wrapper.shadows,
+			key,
+			i;
+
+		// remove events
+		element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = null;
+		stop(wrapper); // stop running animations
+
+		if (wrapper.clipPath) {
+			wrapper.clipPath = wrapper.clipPath.destroy();
+		}
+
+		// Destroy stops in case this is a gradient object
+		if (wrapper.stops) {
+			for (i = 0; i < wrapper.stops.length; i++) {
+				wrapper.stops[i] = wrapper.stops[i].destroy();
+			}
+			wrapper.stops = null;
+		}
+
+		// remove element
+		wrapper.safeRemoveChild(element);
+
+		// destroy shadows
+		if (shadows) {
+			each(shadows, function (shadow) {
+				wrapper.safeRemoveChild(shadow);
+			});
+		}
+
+		// remove from alignObjects
+		erase(wrapper.renderer.alignedObjects, wrapper);
+
+		for (key in wrapper) {
+			delete wrapper[key];
+		}
+
+		return null;
+	},
+
+	/**
+	 * Empty a group element
+	 */
+	empty: function () {
+		var element = this.element,
+			childNodes = element.childNodes,
+			i = childNodes.length;
+
+		while (i--) {
+			element.removeChild(childNodes[i]);
+		}
+	},
+
+	/**
+	 * Add a shadow to the element. Must be done after the element is added to the DOM
+	 * @param {Boolean|Object} shadowOptions
+	 */
+	shadow: function (shadowOptions, group, cutOff) {
+		var shadows = [],
+			i,
+			shadow,
+			element = this.element,
+			strokeWidth,
+			shadowWidth,
+			shadowElementOpacity,
+
+			// compensate for inverted plot area
+			transform;
+
+
+		if (shadowOptions) {
+			shadowWidth = pick(shadowOptions.width, 3);
+			shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth;
+			transform = this.parentInverted ? 
+				'(-1,-1)' : 
+				'(' + pick(shadowOptions.offsetX, 1) + ', ' + pick(shadowOptions.offsetY, 1) + ')';
+			for (i = 1; i <= shadowWidth; i++) {
+				shadow = element.cloneNode(0);
+				strokeWidth = (shadowWidth * 2) + 1 - (2 * i);
+				attr(shadow, {
+					'isShadow': 'true',
+					'stroke': shadowOptions.color || 'black',
+					'stroke-opacity': shadowElementOpacity * i,
+					'stroke-width': strokeWidth,
+					'transform': 'translate' + transform,
+					'fill': NONE
+				});
+				if (cutOff) {
+					attr(shadow, 'height', mathMax(attr(shadow, 'height') - strokeWidth, 0));
+					shadow.cutHeight = strokeWidth;
+				}
+
+				if (group) {
+					group.element.appendChild(shadow);
+				} else {
+					element.parentNode.insertBefore(shadow, element);
+				}
+
+				shadows.push(shadow);
+			}
+
+			this.shadows = shadows;
+		}
+		return this;
+
+	}
+};
+
+
+/**
+ * The default SVG renderer
+ */
+var SVGRenderer = function () {
+	this.init.apply(this, arguments);
+};
+SVGRenderer.prototype = {
+	Element: SVGElement,
+
+	/**
+	 * Initialize the SVGRenderer
+	 * @param {Object} container
+	 * @param {Number} width
+	 * @param {Number} height
+	 * @param {Boolean} forExport
+	 */
+	init: function (container, width, height, forExport) {
+		var renderer = this,
+			loc = location,
+			boxWrapper;
+
+		boxWrapper = renderer.createElement('svg')
+			.attr({
+				xmlns: SVG_NS,
+				version: '1.1'
+			});
+		container.appendChild(boxWrapper.element);
+
+		// object properties
+		renderer.isSVG = true;
+		renderer.box = boxWrapper.element;
+		renderer.boxWrapper = boxWrapper;
+		renderer.alignedObjects = [];
+		
+		// Page url used for internal references. #24, #672, #1070
+		renderer.url = (isFirefox || isWebKit) && doc.getElementsByTagName('base').length ? 
+			loc.href
+				.replace(/#.*?$/, '') // remove the hash
+				.replace(/([\('\)])/g, '\\$1') // escape parantheses and quotes
+				.replace(/ /g, '%20') : // replace spaces (needed for Safari only)
+			''; 
+			
+		renderer.defs = this.createElement('defs').add();
+		renderer.forExport = forExport;
+		renderer.gradients = {}; // Object where gradient SvgElements are stored
+
+		renderer.setSize(width, height, false);
+
+
+
+		// Issue 110 workaround:
+		// In Firefox, if a div is positioned by percentage, its pixel position may land
+		// between pixels. The container itself doesn't display this, but an SVG element
+		// inside this container will be drawn at subpixel precision. In order to draw
+		// sharp lines, this must be compensated for. This doesn't seem to work inside
+		// iframes though (like in jsFiddle).
+		var subPixelFix, rect;
+		if (isFirefox && container.getBoundingClientRect) {
+			renderer.subPixelFix = subPixelFix = function () {
+				css(container, { left: 0, top: 0 });
+				rect = container.getBoundingClientRect();
+				css(container, {
+					left: (mathCeil(rect.left) - rect.left) + PX,
+					top: (mathCeil(rect.top) - rect.top) + PX
+				});
+			};
+
+			// run the fix now
+			subPixelFix();
+
+			// run it on resize
+			addEvent(win, 'resize', subPixelFix);
+		}
+	},
+
+	/**
+	 * Detect whether the renderer is hidden. This happens when one of the parent elements
+	 * has display: none. #608.
+	 */
+	isHidden: function () {
+		return !this.boxWrapper.getBBox().width;			
+	},
+
+	/**
+	 * Destroys the renderer and its allocated members.
+	 */
+	destroy: function () {
+		var renderer = this,
+			rendererDefs = renderer.defs;
+		renderer.box = null;
+		renderer.boxWrapper = renderer.boxWrapper.destroy();
+
+		// Call destroy on all gradient elements
+		destroyObjectProperties(renderer.gradients || {});
+		renderer.gradients = null;
+
+		// Defs are null in VMLRenderer
+		// Otherwise, destroy them here.
+		if (rendererDefs) {
+			renderer.defs = rendererDefs.destroy();
+		}
+
+		// Remove sub pixel fix handler
+		// We need to check that there is a handler, otherwise all functions that are registered for event 'resize' are removed
+		// See issue #982
+		if (renderer.subPixelFix) {
+			removeEvent(win, 'resize', renderer.subPixelFix);
+		}
+
+		renderer.alignedObjects = null;
+
+		return null;
+	},
+
+	/**
+	 * Create a wrapper for an SVG element
+	 * @param {Object} nodeName
+	 */
+	createElement: function (nodeName) {
+		var wrapper = new this.Element();
+		wrapper.init(this, nodeName);
+		return wrapper;
+	},
+
+	/**
+	 * Dummy function for use in canvas renderer
+	 */
+	draw: function () {},
+
+	/**
+	 * Parse a simple HTML string into SVG tspans
+	 *
+	 * @param {Object} textNode The parent text SVG node
+	 */
+	buildText: function (wrapper) {
+		var textNode = wrapper.element,
+			lines = pick(wrapper.textStr, '').toString()
+				.replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
+				.replace(/<(i|em)>/g, '<span style="font-style:italic">')
+				.replace(/<a/g, '<span')
+				.replace(/<\/(b|strong|i|em|a)>/g, '</span>')
+				.split(/<br.*?>/g),
+			childNodes = textNode.childNodes,
+			styleRegex = /style="([^"]+)"/,
+			hrefRegex = /href="([^"]+)"/,
+			parentX = attr(textNode, 'x'),
+			textStyles = wrapper.styles,
+			width = textStyles && textStyles.width && pInt(textStyles.width),
+			textLineHeight = textStyles && textStyles.lineHeight,
+			lastLine,
+			GET_COMPUTED_STYLE = 'getComputedStyle',
+			i = childNodes.length,
+			linePositions = [];
+		
+		// Needed in IE9 because it doesn't report tspan's offsetHeight (#893)
+		function getLineHeightByBBox(lineNo) {
+			linePositions[lineNo] = textNode.getBBox ?
+				textNode.getBBox().height :
+				wrapper.renderer.fontMetrics(textNode.style.fontSize).h; // #990
+			return mathRound(linePositions[lineNo] - (linePositions[lineNo - 1] || 0));
+		}
+
+		// remove old text
+		while (i--) {
+			textNode.removeChild(childNodes[i]);
+		}
+
+		if (width && !wrapper.added) {
+			this.box.appendChild(textNode); // attach it to the DOM to read offset width
+		}
+
+		// remove empty line at end
+		if (lines[lines.length - 1] === '') {
+			lines.pop();
+		}
+
+		// build the lines
+		each(lines, function (line, lineNo) {
+			var spans, spanNo = 0, lineHeight;
+
+			line = line.replace(/<span/g, '|||<span').replace(/<\/span>/g, '</span>|||');
+			spans = line.split('|||');
+
+			each(spans, function (span) {
+				if (span !== '' || spans.length === 1) {
+					var attributes = {},
+						tspan = doc.createElementNS(SVG_NS, 'tspan'),
+						spanStyle; // #390
+					if (styleRegex.test(span)) {
+						spanStyle = span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2');
+						attr(tspan, 'style', spanStyle);
+					}
+					if (hrefRegex.test(span)) {
+						attr(tspan, 'onclick', 'location.href=\"' + span.match(hrefRegex)[1] + '\"');
+						css(tspan, { cursor: 'pointer' });
+					}
+
+					span = (span.replace(/<(.|\n)*?>/g, '') || ' ')
+						.replace(/&lt;/g, '<')
+						.replace(/&gt;/g, '>');
+
+					// issue #38 workaround.
+					/*if (reverse) {
+						arr = [];
+						i = span.length;
+						while (i--) {
+							arr.push(span.charAt(i));
+						}
+						span = arr.join('');
+					}*/
+
+					// add the text node
+					tspan.appendChild(doc.createTextNode(span));
+
+					if (!spanNo) { // first span in a line, align it to the left
+						attributes.x = parentX;
+					} else {
+						// Firefox ignores spaces at the front or end of the tspan
+						attributes.dx = 3; // space
+					}
+
+					// first span on subsequent line, add the line height
+					if (!spanNo) {
+						if (lineNo) {
+
+							// allow getting the right offset height in exporting in IE
+							if (!hasSVG && wrapper.renderer.forExport) {
+								css(tspan, { display: 'block' });
+							}
+
+							// Webkit and opera sometimes return 'normal' as the line height. In that
+							// case, webkit uses offsetHeight, while Opera falls back to 18
+							lineHeight = win[GET_COMPUTED_STYLE] &&
+								pInt(win[GET_COMPUTED_STYLE](lastLine, null).getPropertyValue('line-height'));
+
+							if (!lineHeight || isNaN(lineHeight)) {
+								lineHeight = textLineHeight || lastLine.offsetHeight || getLineHeightByBBox(lineNo) || 18;
+							}
+							attr(tspan, 'dy', lineHeight);
+						}
+						lastLine = tspan; // record for use in next line
+					}
+
+					// add attributes
+					attr(tspan, attributes);
+
+					// append it
+					textNode.appendChild(tspan);
+
+					spanNo++;
+
+					// check width and apply soft breaks
+					if (width) {
+						var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273
+							tooLong,
+							actualWidth,
+							rest = [];
+
+						while (words.length || rest.length) {
+							delete wrapper.bBox; // delete cache
+							actualWidth = wrapper.getBBox().width;
+							tooLong = actualWidth > width;
+							if (!tooLong || words.length === 1) { // new line needed
+								words = rest;
+								rest = [];
+								if (words.length) {
+									tspan = doc.createElementNS(SVG_NS, 'tspan');
+									attr(tspan, {
+										dy: textLineHeight || 16,
+										x: parentX
+									});
+									if (spanStyle) { // #390
+										attr(tspan, 'style', spanStyle);
+									}
+									textNode.appendChild(tspan);
+
+									if (actualWidth > width) { // a single word is pressing it out
+										width = actualWidth;
+									}
+								}
+							} else { // append to existing line tspan
+								tspan.removeChild(tspan.firstChild);
+								rest.unshift(words.pop());
+							}
+							if (words.length) {
+								tspan.appendChild(doc.createTextNode(words.join(' ').replace(/- /g, '-')));
+							}
+						}
+					}
+				}
+			});
+		});
+	},
+
+	/**
+	 * Create a button with preset states
+	 * @param {String} text
+	 * @param {Number} x
+	 * @param {Number} y
+	 * @param {Function} callback
+	 * @param {Object} normalState
+	 * @param {Object} hoverState
+	 * @param {Object} pressedState
+	 */
+	button: function (text, x, y, callback, normalState, hoverState, pressedState) {
+		var label = this.label(text, x, y),
+			curState = 0,
+			stateOptions,
+			stateStyle,
+			normalStyle,
+			hoverStyle,
+			pressedStyle,
+			STYLE = 'style',
+			verticalGradient = { x1: 0, y1: 0, x2: 0, y2: 1 };
+
+		// prepare the attributes
+		/*jslint white: true*/
+		normalState = merge(hash(
+			STROKE_WIDTH, 1,
+			STROKE, '#999',
+			FILL, hash(
+				LINEAR_GRADIENT, verticalGradient,
+				STOPS, [
+					[0, '#FFF'],
+					[1, '#DDD']
+				]
+			),
+			'r', 3,
+			'padding', 3,
+			STYLE, hash(
+				'color', 'black'
+			)
+		), normalState);
+		/*jslint white: false*/
+		normalStyle = normalState[STYLE];
+		delete normalState[STYLE];
+
+		/*jslint white: true*/
+		hoverState = merge(normalState, hash(
+			STROKE, '#68A',
+			FILL, hash(
+				LINEAR_GRADIENT, verticalGradient,
+				STOPS, [
+					[0, '#FFF'],
+					[1, '#ACF']
+				]
+			)
+		), hoverState);
+		/*jslint white: false*/
+		hoverStyle = hoverState[STYLE];
+		delete hoverState[STYLE];
+
+		/*jslint white: true*/
+		pressedState = merge(normalState, hash(
+			STROKE, '#68A',
+			FILL, hash(
+				LINEAR_GRADIENT, verticalGradient,
+				STOPS, [
+					[0, '#9BD'],
+					[1, '#CDF']
+				]
+			)
+		), pressedState);
+		/*jslint white: false*/
+		pressedStyle = pressedState[STYLE];
+		delete pressedState[STYLE];
+
+		// add the events
+		addEvent(label.element, 'mouseenter', function () {
+			label.attr(hoverState)
+				.css(hoverStyle);
+		});
+		addEvent(label.element, 'mouseleave', function () {
+			stateOptions = [normalState, hoverState, pressedState][curState];
+			stateStyle = [normalStyle, hoverStyle, pressedStyle][curState];
+			label.attr(stateOptions)
+				.css(stateStyle);
+		});
+
+		label.setState = function (state) {
+			curState = state;
+			if (!state) {
+				label.attr(normalState)
+					.css(normalStyle);
+			} else if (state === 2) {
+				label.attr(pressedState)
+					.css(pressedStyle);
+			}
+		};
+
+		return label
+			.on('click', function () {
+				callback.call(label);
+			})
+			.attr(normalState)
+			.css(extend({ cursor: 'default' }, normalStyle));
+	},
+
+	/**
+	 * Make a straight line crisper by not spilling out to neighbour pixels
+	 * @param {Array} points
+	 * @param {Number} width
+	 */
+	crispLine: function (points, width) {
+		// points format: [M, 0, 0, L, 100, 0]
+		// normalize to a crisp line
+		if (points[1] === points[4]) {
+			// Substract due to #1129. Now bottom and left axis gridlines behave the same.
+			points[1] = points[4] = mathRound(points[1]) - (width % 2 / 2); 
+		}
+		if (points[2] === points[5]) {
+			points[2] = points[5] = mathRound(points[2]) + (width % 2 / 2);
+		}
+		return points;
+	},
+
+
+	/**
+	 * Draw a path
+	 * @param {Array} path An SVG path in array form
+	 */
+	path: function (path) {
+		var attr = {
+			fill: NONE
+		};
+		if (isArray(path)) {
+			attr.d = path;
+		} else if (isObject(path)) { // attributes
+			extend(attr, path);
+		}
+		return this.createElement('path').attr(attr);
+	},
+
+	/**
+	 * Draw and return an SVG circle
+	 * @param {Number} x The x position
+	 * @param {Number} y The y position
+	 * @param {Number} r The radius
+	 */
+	circle: function (x, y, r) {
+		var attr = isObject(x) ?
+			x :
+			{
+				x: x,
+				y: y,
+				r: r
+			};
+
+		return this.createElement('circle').attr(attr);
+	},
+
+	/**
+	 * Draw and return an arc
+	 * @param {Number} x X position
+	 * @param {Number} y Y position
+	 * @param {Number} r Radius
+	 * @param {Number} innerR Inner radius like used in donut charts
+	 * @param {Number} start Starting angle
+	 * @param {Number} end Ending angle
+	 */
+	arc: function (x, y, r, innerR, start, end) {
+		// arcs are defined as symbols for the ability to set
+		// attributes in attr and animate
+
+		if (isObject(x)) {
+			y = x.y;
+			r = x.r;
+			innerR = x.innerR;
+			start = x.start;
+			end = x.end;
+			x = x.x;
+		}
+		return this.symbol('arc', x || 0, y || 0, r || 0, r || 0, {
+			innerR: innerR || 0,
+			start: start || 0,
+			end: end || 0
+		});
+	},
+	
+	/**
+	 * Draw and return a rectangle
+	 * @param {Number} x Left position
+	 * @param {Number} y Top position
+	 * @param {Number} width
+	 * @param {Number} height
+	 * @param {Number} r Border corner radius
+	 * @param {Number} strokeWidth A stroke width can be supplied to allow crisp drawing
+	 */
+	rect: function (x, y, width, height, r, strokeWidth) {
+		
+		r = isObject(x) ? x.r : r;
+		
+		var wrapper = this.createElement('rect').attr({
+				rx: r,
+				ry: r,
+				fill: NONE
+			});
+		return wrapper.attr(
+				isObject(x) ? 
+					x : 
+					// do not crispify when an object is passed in (as in column charts)
+					wrapper.crisp(strokeWidth, x, y, mathMax(width, 0), mathMax(height, 0))
+			);
+	},
+
+	/**
+	 * Resize the box and re-align all aligned elements
+	 * @param {Object} width
+	 * @param {Object} height
+	 * @param {Boolean} animate
+	 *
+	 */
+	setSize: function (width, height, animate) {
+		var renderer = this,
+			alignedObjects = renderer.alignedObjects,
+			i = alignedObjects.length;
+
+		renderer.width = width;
+		renderer.height = height;
+
+		renderer.boxWrapper[pick(animate, true) ? 'animate' : 'attr']({
+			width: width,


<TRUNCATED>

Mime
View raw message