You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2012/10/20 19:17:09 UTC

svn commit: r1400490 [3/3] - in /incubator/ambari/branches/AMBARI-666: ./ ambari-web/ ambari-web/app/ ambari-web/app/assets/data/cluster_metrics/ ambari-web/app/assets/licenses/ ambari-web/app/styles/ ambari-web/app/templates/main/ ambari-web/app/templ...

Added: incubator/ambari/branches/AMBARI-666/ambari-web/vendor/scripts/rickshaw.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-web/vendor/scripts/rickshaw.js?rev=1400490&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-web/vendor/scripts/rickshaw.js (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-web/vendor/scripts/rickshaw.js Sat Oct 20 17:17:07 2012
@@ -0,0 +1,2638 @@
+var Rickshaw = {
+
+	namespace: function(namespace, obj) {
+
+		var parts = namespace.split('.');
+
+		var parent = Rickshaw;
+
+		for(var i = 1, length = parts.length; i < length; i++) {
+			currentPart = parts[i];
+			parent[currentPart] = parent[currentPart] || {};
+			parent = parent[currentPart];
+		}
+		return parent;
+	},
+
+	keys: function(obj) {
+		var keys = [];
+		for (var key in obj) keys.push(key);
+		return keys;
+	},
+
+	extend: function(destination, source) {
+
+		for (var property in source) {
+			destination[property] = source[property];
+		}
+		return destination;
+	}
+};
+
+if (typeof module !== 'undefined' && module.exports) {
+	var d3 = require('d3');
+	module.exports = Rickshaw;
+}
+
+/* Adapted from https://github.com/Jakobo/PTClass */
+
+/*
+Copyright (c) 2005-2010 Sam Stephenson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+/* Based on Alex Arnell's inheritance implementation. */
+/** section: Language
+ * class Class
+ *
+ *  Manages Prototype's class-based OOP system.
+ *
+ *  Refer to Prototype's web site for a [tutorial on classes and
+ *  inheritance](http://prototypejs.org/learn/class-inheritance).
+**/
+(function(globalContext) {
+/* ------------------------------------ */
+/* Import from object.js                */
+/* ------------------------------------ */
+var _toString = Object.prototype.toString,
+    NULL_TYPE = 'Null',
+    UNDEFINED_TYPE = 'Undefined',
+    BOOLEAN_TYPE = 'Boolean',
+    NUMBER_TYPE = 'Number',
+    STRING_TYPE = 'String',
+    OBJECT_TYPE = 'Object',
+    FUNCTION_CLASS = '[object Function]';
+function isFunction(object) {
+  return _toString.call(object) === FUNCTION_CLASS;
+}
+function extend(destination, source) {
+  for (var property in source) if (source.hasOwnProperty(property)) // modify protect primitive slaughter
+    destination[property] = source[property];
+  return destination;
+}
+function keys(object) {
+  if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
+  var results = [];
+  for (var property in object) {
+    if (object.hasOwnProperty(property)) {
+      results.push(property);
+    }
+  }
+  return results;
+}
+function Type(o) {
+  switch(o) {
+    case null: return NULL_TYPE;
+    case (void 0): return UNDEFINED_TYPE;
+  }
+  var type = typeof o;
+  switch(type) {
+    case 'boolean': return BOOLEAN_TYPE;
+    case 'number':  return NUMBER_TYPE;
+    case 'string':  return STRING_TYPE;
+  }
+  return OBJECT_TYPE;
+}
+function isUndefined(object) {
+  return typeof object === "undefined";
+}
+/* ------------------------------------ */
+/* Import from Function.js              */
+/* ------------------------------------ */
+var slice = Array.prototype.slice;
+function argumentNames(fn) {
+  var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
+    .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
+    .replace(/\s+/g, '').split(',');
+  return names.length == 1 && !names[0] ? [] : names;
+}
+function wrap(fn, wrapper) {
+  var __method = fn;
+  return function() {
+    var a = update([bind(__method, this)], arguments);
+    return wrapper.apply(this, a);
+  }
+}
+function update(array, args) {
+  var arrayLength = array.length, length = args.length;
+  while (length--) array[arrayLength + length] = args[length];
+  return array;
+}
+function merge(array, args) {
+  array = slice.call(array, 0);
+  return update(array, args);
+}
+function bind(fn, context) {
+  if (arguments.length < 2 && isUndefined(arguments[0])) return this;
+  var __method = fn, args = slice.call(arguments, 2);
+  return function() {
+    var a = merge(args, arguments);
+    return __method.apply(context, a);
+  }
+}
+
+/* ------------------------------------ */
+/* Import from Prototype.js             */
+/* ------------------------------------ */
+var emptyFunction = function(){};
+
+var Class = (function() {
+  
+  // Some versions of JScript fail to enumerate over properties, names of which 
+  // correspond to non-enumerable properties in the prototype chain
+  var IS_DONTENUM_BUGGY = (function(){
+    for (var p in { toString: 1 }) {
+      // check actual property name, so that it works with augmented Object.prototype
+      if (p === 'toString') return false;
+    }
+    return true;
+  })();
+  
+  function subclass() {};
+  function create() {
+    var parent = null, properties = [].slice.apply(arguments);
+    if (isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
+      this.initialize.apply(this, arguments);
+    }
+
+    extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
+
+    if (parent) {
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      try { parent.subclasses.push(klass) } catch(e) {}
+    }
+
+    for (var i = 0, length = properties.length; i < length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = emptyFunction;
+
+    klass.prototype.constructor = klass;
+    return klass;
+  }
+
+  function addMethods(source) {
+    var ancestor   = this.superclass && this.superclass.prototype,
+        properties = keys(source);
+
+    // IE6 doesn't enumerate `toString` and `valueOf` (among other built-in `Object.prototype`) properties,
+    // Force copy if they're not Object.prototype ones.
+    // Do not copy other Object.prototype.* for performance reasons
+    if (IS_DONTENUM_BUGGY) {
+      if (source.toString != Object.prototype.toString)
+        properties.push("toString");
+      if (source.valueOf != Object.prototype.valueOf)
+        properties.push("valueOf");
+    }
+
+    for (var i = 0, length = properties.length; i < length; i++) {
+      var property = properties[i], value = source[property];
+      if (ancestor && isFunction(value) &&
+          argumentNames(value)[0] == "$super") {
+        var method = value;
+        value = wrap((function(m) {
+          return function() { return ancestor[m].apply(this, arguments); };
+        })(property), method);
+
+        value.valueOf = bind(method.valueOf, method);
+        value.toString = bind(method.toString, method);
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+
+  return {
+    create: create,
+    Methods: {
+      addMethods: addMethods
+    }
+  };
+})();
+
+if (globalContext.exports) {
+  globalContext.exports.Class = Class;
+}
+else {
+  globalContext.Class = Class;
+}
+})(Rickshaw);
+Rickshaw.namespace('Rickshaw.Compat.ClassList');
+
+Rickshaw.Compat.ClassList = function() {
+
+	/* adapted from http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
+
+	if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
+
+	(function (view) {
+
+	"use strict";
+
+	var
+		  classListProp = "classList"
+		, protoProp = "prototype"
+		, elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
+		, objCtr = Object
+		, strTrim = String[protoProp].trim || function () {
+			return this.replace(/^\s+|\s+$/g, "");
+		}
+		, arrIndexOf = Array[protoProp].indexOf || function (item) {
+			var
+				  i = 0
+				, len = this.length
+			;
+			for (; i < len; i++) {
+				if (i in this && this[i] === item) {
+					return i;
+				}
+			}
+			return -1;
+		}
+		// Vendors: please allow content code to instantiate DOMExceptions
+		, DOMEx = function (type, message) {
+			this.name = type;
+			this.code = DOMException[type];
+			this.message = message;
+		}
+		, checkTokenAndGetIndex = function (classList, token) {
+			if (token === "") {
+				throw new DOMEx(
+					  "SYNTAX_ERR"
+					, "An invalid or illegal string was specified"
+				);
+			}
+			if (/\s/.test(token)) {
+				throw new DOMEx(
+					  "INVALID_CHARACTER_ERR"
+					, "String contains an invalid character"
+				);
+			}
+			return arrIndexOf.call(classList, token);
+		}
+		, ClassList = function (elem) {
+			var
+				  trimmedClasses = strTrim.call(elem.className)
+				, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
+				, i = 0
+				, len = classes.length
+			;
+			for (; i < len; i++) {
+				this.push(classes[i]);
+			}
+			this._updateClassName = function () {
+				elem.className = this.toString();
+			};
+		}
+		, classListProto = ClassList[protoProp] = []
+		, classListGetter = function () {
+			return new ClassList(this);
+		}
+	;
+	// Most DOMException implementations don't allow calling DOMException's toString()
+	// on non-DOMExceptions. Error's toString() is sufficient here.
+	DOMEx[protoProp] = Error[protoProp];
+	classListProto.item = function (i) {
+		return this[i] || null;
+	};
+	classListProto.contains = function (token) {
+		token += "";
+		return checkTokenAndGetIndex(this, token) !== -1;
+	};
+	classListProto.add = function (token) {
+		token += "";
+		if (checkTokenAndGetIndex(this, token) === -1) {
+			this.push(token);
+			this._updateClassName();
+		}
+	};
+	classListProto.remove = function (token) {
+		token += "";
+		var index = checkTokenAndGetIndex(this, token);
+		if (index !== -1) {
+			this.splice(index, 1);
+			this._updateClassName();
+		}
+	};
+	classListProto.toggle = function (token) {
+		token += "";
+		if (checkTokenAndGetIndex(this, token) === -1) {
+			this.add(token);
+		} else {
+			this.remove(token);
+		}
+	};
+	classListProto.toString = function () {
+		return this.join(" ");
+	};
+
+	if (objCtr.defineProperty) {
+		var classListPropDesc = {
+			  get: classListGetter
+			, enumerable: true
+			, configurable: true
+		};
+		try {
+			objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+		} catch (ex) { // IE 8 doesn't support enumerable:true
+			if (ex.number === -0x7FF5EC54) {
+				classListPropDesc.enumerable = false;
+				objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
+			}
+		}
+	} else if (objCtr[protoProp].__defineGetter__) {
+		elemCtrProto.__defineGetter__(classListProp, classListGetter);
+	}
+
+	}(window));
+
+	}
+};
+
+if ( (typeof RICKSHAW_NO_COMPAT !== "undefined" && !RICKSHAW_NO_COMPAT) || typeof RICKSHAW_NO_COMPAT === "undefined") {
+	new Rickshaw.Compat.ClassList();
+}
+Rickshaw.namespace('Rickshaw.Graph');
+
+Rickshaw.Graph = function(args) {
+
+	this.element = args.element;
+	this.series = args.series;
+
+	this.defaults = {
+		interpolation: 'cardinal',
+		offset: 'zero',
+		min: undefined,
+		max: undefined
+	};
+
+	Rickshaw.keys(this.defaults).forEach( function(k) {
+		this[k] = args[k] || this.defaults[k];
+	}, this );
+
+	this.window = {};
+
+	this.updateCallbacks = [];
+
+	var self = this;
+
+	this.initialize = function(args) {
+
+		this.validateSeries(args.series);
+
+		this.series.active = function() { return self.series.filter( function(s) { return !s.disabled } ) };
+
+		this.setSize({ width: args.width, height: args.height });
+
+		this.element.classList.add('rickshaw_graph');
+		this.vis = d3.select(this.element)
+			.append("svg:svg")
+			.attr('width', this.width)
+			.attr('height', this.height);
+
+		var renderers = [
+			Rickshaw.Graph.Renderer.Stack,
+			Rickshaw.Graph.Renderer.Line,
+			Rickshaw.Graph.Renderer.Bar,
+			Rickshaw.Graph.Renderer.Area,
+			Rickshaw.Graph.Renderer.ScatterPlot
+		];
+
+		renderers.forEach( function(r) {
+			if (!r) return;
+			self.registerRenderer(new r( { graph: self } ));
+		} );
+
+		this.setRenderer(args.renderer || 'stack', args);
+		this.discoverRange();
+	};
+
+	this.validateSeries = function(series) {
+
+		if (!(series instanceof Array) && !(series instanceof Rickshaw.Series)) {
+			var seriesSignature = Object.prototype.toString.apply(series);
+			throw "series is not an array: " + seriesSignature;
+		}
+
+		var pointsCount;
+
+		series.forEach( function(s) {
+
+			if (!(s instanceof Object)) {
+				throw "series element is not an object: " + s;
+			}
+			if (!(s.data)) {
+				throw "series has no data: " + JSON.stringify(s);
+			}
+			if (!(s.data instanceof Array)) {
+				throw "series data is not an array: " + JSON.stringify(s.data);
+			}
+
+			pointsCount = pointsCount || s.data.length;
+
+			if (pointsCount && s.data.length != pointsCount) {
+				throw "series cannot have differing numbers of points: " +
+					pointsCount	+ " vs " + s.data.length + "; see Rickshaw.Series.zeroFill()";
+			}
+
+			var dataTypeX = typeof s.data[0].x;
+			var dataTypeY = typeof s.data[0].y;
+
+			if (dataTypeX != 'number' || dataTypeY != 'number') {
+				throw "x and y properties of points should be numbers instead of " +
+					dataTypeX + " and " + dataTypeY;
+			}
+		} );
+	};
+
+	this.dataDomain = function() {
+
+		// take from the first series
+		var data = this.series[0].data;
+
+		return [ data[0].x, data.slice(-1).shift().x ];
+
+	};
+
+	this.discoverRange = function() {
+
+		var domain = this.renderer.domain();
+
+		this.x = d3.scale.linear().domain(domain.x).range([0, this.width]);
+
+		this.y = d3.scale.linear().domain(domain.y).range([this.height, 0]);
+
+		this.y.magnitude = d3.scale.linear()
+			.domain([domain.y[0] - domain.y[0], domain.y[1] - domain.y[0]])
+			.range([0, this.height]);
+	};
+
+	this.render = function() {
+
+		var stackedData = this.stackData();
+		this.discoverRange();
+
+		this.renderer.render();
+
+		this.updateCallbacks.forEach( function(callback) {
+			callback();
+		} );
+	};
+
+	this.update = this.render;
+
+	this.stackData = function() {
+
+		var data = this.series.active()
+			.map( function(d) { return d.data } )
+			.map( function(d) { return d.filter( function(d) { return this._slice(d) }, this ) }, this);
+
+		this.stackData.hooks.data.forEach( function(entry) {
+			data = entry.f.apply(self, [data]);
+		} );
+
+		var layout = d3.layout.stack();
+		layout.offset( self.offset );
+
+		var stackedData = layout(data);
+
+		this.stackData.hooks.after.forEach( function(entry) {
+			stackedData = entry.f.apply(self, [data]);
+		} );
+
+		var i = 0;
+		this.series.forEach( function(series) {
+			if (series.disabled) return;
+			series.stack = stackedData[i++];
+		} );
+
+		this.stackedData = stackedData;
+		return stackedData;
+	};
+
+	this.stackData.hooks = { data: [], after: [] };
+
+	this._slice = function(d) {
+
+		if (this.window.xMin || this.window.xMax) {
+
+			var isInRange = true;
+
+			if (this.window.xMin && d.x < this.window.xMin) isInRange = false;
+			if (this.window.xMax && d.x > this.window.xMax) isInRange = false;
+
+			return isInRange;
+		}
+
+		return true;
+	};
+
+	this.onUpdate = function(callback) {
+		this.updateCallbacks.push(callback);
+	};
+
+	this.registerRenderer = function(renderer) {
+		this._renderers = this._renderers || {};
+		this._renderers[renderer.name] = renderer;
+	};
+
+	this.configure = function(args) {
+
+		if (args.width || args.height) {
+			this.setSize(args);
+		}
+
+		Rickshaw.keys(this.defaults).forEach( function(k) {
+			this[k] = k in args ? args[k]
+				: k in this ? this[k]
+				: this.defaults[k];
+		}, this );
+
+		this.setRenderer(args.renderer || this.renderer.name, args);
+	};
+
+	this.setRenderer = function(name, args) {
+
+		if (!this._renderers[name]) {
+			throw "couldn't find renderer " + name;
+		}
+		this.renderer = this._renderers[name];
+
+		if (typeof args == 'object') {
+			this.renderer.configure(args);
+		}
+	};
+
+	this.setSize = function(args) {
+
+		args = args || {};
+
+		if (typeof window !== undefined) {
+			var style = window.getComputedStyle(this.element, null);
+			var elementWidth = parseInt(style.getPropertyValue('width'));
+			var elementHeight = parseInt(style.getPropertyValue('height'));
+		}
+
+		this.width = args.width || elementWidth || 400;
+		this.height = args.height || elementHeight || 250;
+
+		this.vis && this.vis
+			.attr('width', this.width)
+			.attr('height', this.height);
+	}
+
+	this.initialize(args);
+};
+Rickshaw.namespace('Rickshaw.Fixtures.Color');
+
+Rickshaw.Fixtures.Color = function() {
+
+	this.schemes = {};
+
+	this.schemes.spectrum14 = [
+		'#ecb796',
+		'#dc8f70',
+		'#b2a470',
+		'#92875a',
+		'#716c49',
+		'#d2ed82',
+		'#bbe468',
+		'#a1d05d',
+		'#e7cbe6',
+		'#d8aad6',
+		'#a888c2',
+		'#9dc2d3',
+		'#649eb9',
+		'#387aa3'
+	].reverse();
+
+	this.schemes.spectrum2000 = [
+		'#57306f',
+		'#514c76',
+		'#646583',
+		'#738394',
+		'#6b9c7d',
+		'#84b665',
+		'#a7ca50',
+		'#bfe746',
+		'#e2f528',
+		'#fff726',
+		'#ecdd00',
+		'#d4b11d',
+		'#de8800',
+		'#de4800',
+		'#c91515',
+		'#9a0000',
+		'#7b0429',
+		'#580839',
+		'#31082b'
+	];
+
+	this.schemes.spectrum2001 = [
+		'#2f243f',
+		'#3c2c55',
+		'#4a3768',
+		'#565270',
+		'#6b6b7c',
+		'#72957f',
+		'#86ad6e',
+		'#a1bc5e',
+		'#b8d954',
+		'#d3e04e',
+		'#ccad2a',
+		'#cc8412',
+		'#c1521d',
+		'#ad3821',
+		'#8a1010',
+		'#681717',
+		'#531e1e',
+		'#3d1818',
+		'#320a1b'
+	];
+
+	this.schemes.classic9 = [
+		'#423d4f',
+		'#4a6860',
+		'#848f39',
+		'#a2b73c',
+		'#ddcb53',
+		'#c5a32f',
+		'#7d5836',
+		'#963b20',
+		'#7c2626',
+		'#491d37',
+		'#2f254a'
+	].reverse();
+
+	this.schemes.httpStatus = {
+		503: '#ea5029',
+		502: '#d23f14',
+		500: '#bf3613',
+		410: '#efacea',
+		409: '#e291dc',
+		403: '#f457e8',
+		408: '#e121d2',
+		401: '#b92dae',
+		405: '#f47ceb',
+		404: '#a82a9f',
+		400: '#b263c6',
+		301: '#6fa024',
+		302: '#87c32b',
+		307: '#a0d84c',
+		304: '#28b55c',
+		200: '#1a4f74',
+		206: '#27839f',
+		201: '#52adc9',
+		202: '#7c979f',
+		203: '#a5b8bd',
+		204: '#c1cdd1'
+	};
+
+	this.schemes.colorwheel = [
+		'#b5b6a9',
+		'#858772',
+		'#785f43',
+		'#96557e',
+		'#4682b4',
+		'#65b9ac',
+		'#73c03a',
+		'#cb513a'
+	].reverse();
+
+	this.schemes.cool = [
+		'#5e9d2f',
+		'#73c03a',
+		'#4682b4',
+		'#7bc3b8',
+		'#a9884e',
+		'#c1b266',
+		'#a47493',
+		'#c09fb5'
+	];
+
+	this.schemes.munin = [
+		'#00cc00',
+		'#0066b3',
+		'#ff8000',
+		'#ffcc00',
+		'#330099',
+		'#990099',
+		'#ccff00',
+		'#ff0000',
+		'#808080',
+		'#008f00',
+		'#00487d',
+		'#b35a00',
+		'#b38f00',
+		'#6b006b',
+		'#8fb300',
+		'#b30000',
+		'#bebebe',
+		'#80ff80',
+		'#80c9ff',
+		'#ffc080',
+		'#ffe680',
+		'#aa80ff',
+		'#ee00cc',
+		'#ff8080',
+		'#666600',
+		'#ffbfff',
+		'#00ffcc',
+		'#cc6699',
+		'#999900'
+	];
+};
+Rickshaw.namespace('Rickshaw.Fixtures.RandomData');
+
+Rickshaw.Fixtures.RandomData = function(timeInterval) {
+
+	var addData;
+	timeInterval = timeInterval || 1;
+
+	var lastRandomValue = 200;
+
+	var timeBase = Math.floor(new Date().getTime() / 1000);
+
+	this.addData = function(data) {
+
+		var randomValue = Math.random() * 100 + 15 + lastRandomValue;
+		var index = data[0].length;
+
+		var counter = 1;
+
+		data.forEach( function(series) {
+			var randomVariance = Math.random() * 20;
+			var v = randomValue / 25  + counter++
+				+ (Math.cos((index * counter * 11) / 960) + 2) * 15 
+				+ (Math.cos(index / 7) + 2) * 7
+				+ (Math.cos(index / 17) + 2) * 1;
+
+			series.push( { x: (index * timeInterval) + timeBase, y: v + randomVariance } );
+		} );
+
+		lastRandomValue = randomValue * .85;
+	}
+};
+
+Rickshaw.namespace('Rickshaw.Fixtures.Time');
+
+Rickshaw.Fixtures.Time = function() {
+
+	var tzOffset = new Date().getTimezoneOffset() * 60;
+
+	var self = this;
+
+	this.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+	this.units = [
+		{
+			name: 'decade',
+			seconds: 86400 * 365.25 * 10,
+			formatter: function(d) { return (parseInt(d.getUTCFullYear() / 10) * 10) }
+		}, {
+			name: 'year',
+			seconds: 86400 * 365.25,
+			formatter: function(d) { return d.getUTCFullYear() }
+		}, {
+			name: 'month',
+			seconds: 86400 * 30.5,
+			formatter: function(d) { return self.months[d.getUTCMonth()] }
+		}, {
+			name: 'week',
+			seconds: 86400 * 7,
+			formatter: function(d) { return self.formatDate(d) }
+		}, {
+			name: 'day',
+			seconds: 86400,
+			formatter: function(d) { return d.getUTCDate() }
+		}, {
+			name: '6 hour',
+			seconds: 3600 * 6,
+			formatter: function(d) { return self.formatTime(d) }
+		}, {
+			name: 'hour',
+			seconds: 3600,
+			formatter: function(d) { return self.formatTime(d) }
+		}, {
+			name: '15 minute',
+			seconds: 60 * 15,
+			formatter: function(d) { return self.formatTime(d) }
+		}, {
+			name: 'minute',
+			seconds: 60,
+			formatter: function(d) { return d.getUTCMinutes() }
+		}, {
+			name: '15 second',
+			seconds: 15,
+			formatter: function(d) { return d.getUTCSeconds() + 's' }
+		}, {
+			name: 'second',
+			seconds: 1,
+			formatter: function(d) { return d.getUTCSeconds() + 's' }
+		}
+	];
+
+	this.unit = function(unitName) {
+		return this.units.filter( function(unit) { return unitName == unit.name } ).shift();
+	};
+
+	this.formatDate = function(d) {
+		return d.toUTCString().match(/, (\w+ \w+ \w+)/)[1];
+	};
+
+	this.formatTime = function(d) {
+		return d.toUTCString().match(/(\d+:\d+):/)[1];
+	};
+
+	this.ceil = function(time, unit) {
+
+		if (unit.name == 'month') {
+
+			var nearFuture = new Date((time + unit.seconds - 1) * 1000);
+
+			var rounded = new Date(0);
+			rounded.setUTCFullYear(nearFuture.getUTCFullYear());
+			rounded.setUTCMonth(nearFuture.getUTCMonth());
+			rounded.setUTCDate(1);
+			rounded.setUTCHours(0);
+			rounded.setUTCMinutes(0);
+			rounded.setUTCSeconds(0);
+			rounded.setUTCMilliseconds(0);
+
+			return rounded.getTime() / 1000;
+		}
+
+		if (unit.name == 'year') {
+
+			var nearFuture = new Date((time + unit.seconds - 1) * 1000);
+
+			var rounded = new Date(0);
+			rounded.setUTCFullYear(nearFuture.getUTCFullYear());
+			rounded.setUTCMonth(0);
+			rounded.setUTCDate(1);
+			rounded.setUTCHours(0);
+			rounded.setUTCMinutes(0);
+			rounded.setUTCSeconds(0);
+			rounded.setUTCMilliseconds(0);
+
+			return rounded.getTime() / 1000;
+		}
+
+		return Math.ceil(time / unit.seconds) * unit.seconds;
+	};
+};
+Rickshaw.namespace('Rickshaw.Fixtures.Number');
+
+Rickshaw.Fixtures.Number.formatKMBT = function(y) {
+	if (y >= 1000000000000)   { return y / 1000000000000 + "T" } 
+	else if (y >= 1000000000) { return y / 1000000000 + "B" } 
+	else if (y >= 1000000)    { return y / 1000000 + "M" } 
+	else if (y >= 1000)       { return y / 1000 + "K" }
+	else if (y < 1 && y > 0)  { return y.toFixed(2) }
+	else if (y == 0)          { return '' }
+	else                      { return y }
+};
+
+Rickshaw.Fixtures.Number.formatBase1024KMGTP = function(y) {
+    if (y >= 1125899906842624)  { return y / 1125899906842624 + "P" }
+    else if (y >= 1099511627776){ return y / 1099511627776 + "T" }
+    else if (y >= 1073741824)   { return y / 1073741824 + "G" }
+    else if (y >= 1048576)      { return y / 1048576 + "M" }
+    else if (y >= 1024)         { return y / 1024 + "K" }
+    else if (y < 1 && y > 0)    { return y.toFixed(2) }
+    else if (y == 0)            { return '' }
+    else                        { return y }
+};
+Rickshaw.namespace("Rickshaw.Color.Palette");
+
+Rickshaw.Color.Palette = function(args) {
+
+	var color = new Rickshaw.Fixtures.Color();
+
+	args = args || {};
+	this.schemes = {};
+
+	this.scheme = color.schemes[args.scheme] || args.scheme || color.schemes.colorwheel;
+	this.runningIndex = 0;
+	this.generatorIndex = 0;
+
+	if (args.interpolatedStopCount) {
+		var schemeCount = this.scheme.length - 1;
+		var i, j, scheme = [];
+		for (i = 0; i < schemeCount; i++) {
+			scheme.push(this.scheme[i]);
+			var generator = d3.interpolateHsl(this.scheme[i], this.scheme[i + 1]);
+			for (j = 1; j < args.interpolatedStopCount; j++) {
+				scheme.push(generator((1 / args.interpolatedStopCount) * j));
+			}
+		}
+		scheme.push(this.scheme[this.scheme.length - 1]);
+		this.scheme = scheme;
+	}
+	this.rotateCount = this.scheme.length;
+
+	this.color = function(key) {
+		return this.scheme[key] || this.scheme[this.runningIndex++] || this.interpolateColor() || '#808080';
+	};
+
+	this.interpolateColor = function() {
+		if (!Array.isArray(this.scheme)) return;
+		var color;
+		if (this.generatorIndex == this.rotateCount * 2 - 1) {
+			color = d3.interpolateHsl(this.scheme[this.generatorIndex], this.scheme[0])(0.5);
+			this.generatorIndex = 0;
+			this.rotateCount *= 2;
+		} else {
+			color = d3.interpolateHsl(this.scheme[this.generatorIndex], this.scheme[this.generatorIndex + 1])(0.5);
+			this.generatorIndex++;
+		}
+		this.scheme.push(color);
+		return color;
+	};
+
+};
+Rickshaw.namespace('Rickshaw.Graph.Ajax');
+
+Rickshaw.Graph.Ajax = Rickshaw.Class.create( {
+
+	initialize: function(args) {
+
+		this.dataURL = args.dataURL;
+
+		this.onData = args.onData || function(d) { return d };
+		this.onComplete = args.onComplete || function() {};
+		this.onError = args.onError || function() {};
+
+		this.args = args; // pass through to Rickshaw.Graph
+
+		this.request();
+	},
+
+	request: function() {
+
+		$.ajax( {
+			url: this.dataURL,
+			dataType: 'json',
+			success: this.success.bind(this),
+			error: this.error.bind(this)
+		} );
+	},
+
+	error: function() {
+
+		console.log("error loading dataURL: " + this.dataURL);
+		this.onError(this);
+	},
+
+	success: function(data, status) {
+
+		data = this.onData(data);
+		this.args.series = this._splice({ data: data, series: this.args.series });
+
+		this.graph = new Rickshaw.Graph(this.args);
+		this.graph.render();
+
+		this.onComplete(this);
+	},
+
+	_splice: function(args) {
+
+		var data = args.data;
+		var series = args.series;
+
+		if (!args.series) return data;
+
+		series.forEach( function(s) {
+
+			var seriesKey = s.key || s.name;
+			if (!seriesKey) throw "series needs a key or a name";
+
+			data.forEach( function(d) {
+
+				var dataKey = d.key || d.name;
+				if (!dataKey) throw "data needs a key or a name";
+
+				if (seriesKey == dataKey) {
+					var properties = ['color', 'name', 'data'];
+					properties.forEach( function(p) {
+						s[p] = s[p] || d[p];
+					} );
+				}
+			} );
+		} );
+
+		return series;
+	}
+} );
+
+Rickshaw.namespace('Rickshaw.Graph.Annotate');
+
+Rickshaw.Graph.Annotate = function(args) {
+
+	var graph = this.graph = args.graph;
+	this.elements = { timeline: args.element };
+	
+	var self = this;
+
+	this.data = {};
+
+	this.elements.timeline.classList.add('rickshaw_annotation_timeline');
+
+	this.add = function(time, content, end_time) {
+		self.data[time] = self.data[time] || {'boxes': []};
+		self.data[time].boxes.push({content: content, end: end_time});
+	};
+
+	this.update = function() {
+
+		Rickshaw.keys(self.data).forEach( function(time) {
+
+			var annotation = self.data[time];
+			var left = self.graph.x(time);
+
+			if (left < 0 || left > self.graph.x.range()[1]) {
+				if (annotation.element) {
+					annotation.line.classList.add('offscreen');
+					annotation.element.style.display = 'none';
+				}
+
+				annotation.boxes.forEach( function(box) {
+					if ( box.rangeElement ) box.rangeElement.classList.add('offscreen');
+				});
+
+				return;
+			}
+
+			if (!annotation.element) {
+				var element = annotation.element = document.createElement('div');
+				element.classList.add('annotation');
+				this.elements.timeline.appendChild(element);
+				element.addEventListener('click', function(e) {
+					element.classList.toggle('active');
+					annotation.line.classList.toggle('active');
+					annotation.boxes.forEach( function(box) {
+						if ( box.rangeElement ) box.rangeElement.classList.toggle('active');
+					});
+				}, false);
+					
+			}
+
+			annotation.element.style.left = left + 'px';
+			annotation.element.style.display = 'block';
+
+			annotation.boxes.forEach( function(box) {
+
+
+				var element = box.element;
+
+				if (!element) {
+					element = box.element = document.createElement('div');
+					element.classList.add('content');
+					element.innerHTML = box.content;
+					annotation.element.appendChild(element);
+
+					annotation.line = document.createElement('div');
+					annotation.line.classList.add('annotation_line');
+					self.graph.element.appendChild(annotation.line);
+
+					if ( box.end ) {
+						box.rangeElement = document.createElement('div');
+						box.rangeElement.classList.add('annotation_range');
+						self.graph.element.appendChild(box.rangeElement);
+					}
+
+				}
+
+				if ( box.end ) {
+
+					var annotationRangeStart = left;
+					var annotationRangeEnd   = Math.min( self.graph.x(box.end), self.graph.x.range()[1] );
+
+					// annotation makes more sense at end
+					if ( annotationRangeStart > annotationRangeEnd ) {
+						annotationRangeEnd   = left;
+						annotationRangeStart = Math.max( self.graph.x(box.end), self.graph.x.range()[0] );
+					}
+
+					var annotationRangeWidth = annotationRangeEnd - annotationRangeStart;
+
+					box.rangeElement.style.left  = annotationRangeStart + 'px';
+					box.rangeElement.style.width = annotationRangeWidth + 'px'
+
+					box.rangeElement.classList.remove('offscreen');
+				}
+
+				annotation.line.classList.remove('offscreen');
+				annotation.line.style.left = left + 'px';
+			} );
+		}, this );
+	};
+
+	this.graph.onUpdate( function() { self.update() } );
+};
+Rickshaw.namespace('Rickshaw.Graph.Axis.Time');
+
+Rickshaw.Graph.Axis.Time = function(args) {
+
+	var self = this;
+
+	this.graph = args.graph;
+	this.elements = [];
+	this.ticksTreatment = args.ticksTreatment || 'plain';
+	this.fixedTimeUnit = args.timeUnit;
+
+	var time = new Rickshaw.Fixtures.Time();
+
+	this.appropriateTimeUnit = function() {
+
+		var unit;
+		var units = time.units;
+
+		var domain = this.graph.x.domain();
+		var rangeSeconds = domain[1] - domain[0];
+
+		units.forEach( function(u) {
+			if (Math.floor(rangeSeconds / u.seconds) >= 2) {
+				unit = unit || u;
+			}
+		} );
+
+		return (unit || time.units[time.units.length - 1]);
+	};
+
+	this.tickOffsets = function() {
+
+		var domain = this.graph.x.domain();
+
+		var unit = this.fixedTimeUnit || this.appropriateTimeUnit();
+		var count = Math.ceil((domain[1] - domain[0]) / unit.seconds);
+
+		var runningTick = domain[0];
+
+		var offsets = [];
+
+		for (var i = 0; i < count; i++) {
+
+			tickValue = time.ceil(runningTick, unit);
+			runningTick = tickValue + unit.seconds / 2;
+
+			offsets.push( { value: tickValue, unit: unit } );
+		}
+
+		return offsets;
+	};
+
+	this.render = function() {
+
+		this.elements.forEach( function(e) {
+			e.parentNode.removeChild(e);
+		} );
+
+		this.elements = [];
+
+		var offsets = this.tickOffsets();
+
+		offsets.forEach( function(o) {
+			
+			if (self.graph.x(o.value) > self.graph.x.range()[1]) return;
+	
+			var element = document.createElement('div');
+			element.style.left = self.graph.x(o.value) + 'px';
+			element.classList.add('x_tick');
+			element.classList.add(self.ticksTreatment);
+
+			var title = document.createElement('div');
+			title.classList.add('title');
+			title.innerHTML = o.unit.formatter(new Date(o.value * 1000));
+			element.appendChild(title);
+
+			self.graph.element.appendChild(element);
+			self.elements.push(element);
+
+		} );
+	};
+
+	this.graph.onUpdate( function() { self.render() } );
+};
+
+Rickshaw.namespace('Rickshaw.Graph.Axis.Y');
+
+Rickshaw.Graph.Axis.Y = function(args) {
+
+	var self = this;
+	var berthRate = 0.10;
+
+	this.initialize = function(args) {
+
+		this.graph = args.graph;
+		this.orientation = args.orientation || 'right';
+
+		var pixelsPerTick = args.pixelsPerTick || 75;
+		this.ticks = args.ticks || Math.floor(this.graph.height / pixelsPerTick);
+		this.tickSize = args.tickSize || 4;
+		this.ticksTreatment = args.ticksTreatment || 'plain';
+
+		if (args.element) {
+
+			this.element = args.element;
+			this.vis = d3.select(args.element)
+				.append("svg:svg")
+				.attr('class', 'rickshaw_graph y_axis');
+
+			this.element = this.vis[0][0];
+			this.element.style.position = 'relative';
+
+			this.setSize({ width: args.width, height: args.height });
+
+		} else {
+			this.vis = this.graph.vis;
+		}
+
+		this.graph.onUpdate( function() { self.render() } );
+	};
+
+	this.setSize = function(args) {
+
+		args = args || {};
+
+		if (!this.element) return;
+
+		if (typeof window !== 'undefined') {
+
+			var style = window.getComputedStyle(this.element.parentNode, null);
+			var elementWidth = parseInt(style.getPropertyValue('width'));
+
+			if (!args.auto) {
+				var elementHeight = parseInt(style.getPropertyValue('height'));
+			}
+		}
+
+		this.width = args.width || elementWidth || this.graph.width * berthRate;
+		this.height = args.height || elementHeight || this.graph.height;
+
+		this.vis
+			.attr('width', this.width)
+			.attr('height', this.height * (1 + berthRate));
+
+		var berth = this.height * berthRate;
+		this.element.style.top = -1 * berth + 'px';
+	};
+
+	this.render = function() {
+
+		if (this.graph.height !== this._renderHeight) this.setSize({ auto: true });
+
+		var axis = d3.svg.axis().scale(this.graph.y).orient(this.orientation);
+		axis.tickFormat( args.tickFormat || function(y) { return y } );
+
+		if (this.orientation == 'left') {
+			var berth = this.height * berthRate;
+			var transform = 'translate(' + this.width + ', ' + berth + ')';
+		}
+
+		if (this.element) {
+			this.vis.selectAll('*').remove();
+		}
+
+		this.vis
+			.append("svg:g")
+			.attr("class", ["y_ticks", this.ticksTreatment].join(" "))
+			.attr("transform", transform)
+			.call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize))
+
+		var gridSize = (this.orientation == 'right' ? 1 : -1) * this.graph.width;
+
+		this.graph.vis
+			.append("svg:g")
+			.attr("class", "y_grid")
+			.call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize));
+
+		this._renderHeight = this.graph.height;
+	};
+
+	this.initialize(args);
+};
+
+Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Highlight');
+
+Rickshaw.Graph.Behavior.Series.Highlight = function(args) {
+
+	this.graph = args.graph;
+	this.legend = args.legend;
+
+	var self = this;
+
+	var colorSafe = {};
+
+	this.addHighlightEvents = function (l) {
+		l.element.addEventListener( 'mouseover', function(e) {
+
+			self.legend.lines.forEach( function(line) {
+				if (l === line) return;
+				colorSafe[line.series.name] = colorSafe[line.series.name] || line.series.color;
+				line.series.color = d3.interpolateRgb(line.series.color, d3.rgb('#d8d8d8'))(0.8).toString();
+			} );
+
+			self.graph.update();
+
+		}, false );
+
+		l.element.addEventListener( 'mouseout', function(e) {
+
+			self.legend.lines.forEach( function(line) {
+				if (colorSafe[line.series.name]) {
+					line.series.color = colorSafe[line.series.name];
+				}
+			} );
+
+			self.graph.update();
+
+		}, false );
+	};
+
+	if (this.legend) {
+		this.legend.lines.forEach( function(l) {
+			self.addHighlightEvents(l);
+		} );
+	}
+
+};
+Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Order');
+
+Rickshaw.Graph.Behavior.Series.Order = function(args) {
+
+	this.graph = args.graph;
+	this.legend = args.legend;
+
+	var self = this;
+
+	$(function() {
+		$(self.legend.list).sortable( { 
+			containment: 'parent',
+			tolerance: 'pointer',
+			update: function( event, ui ) {
+				var series = [];
+				$(self.legend.list).find('li').each( function(index, item) {
+					if (!item.series) return;
+					series.push(item.series);
+				} );
+
+				for (var i = self.graph.series.length - 1; i >= 0; i--) {
+					self.graph.series[i] = series.shift();
+				}
+
+				self.graph.update();
+			}
+		} );
+		$(self.legend.list).disableSelection();
+	});
+
+	//hack to make jquery-ui sortable behave
+	this.graph.onUpdate( function() { 
+		var h = window.getComputedStyle(self.legend.element).height;
+		self.legend.element.style.height = h;
+	} );
+};
+Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Toggle');
+
+Rickshaw.Graph.Behavior.Series.Toggle = function(args) {
+
+	this.graph = args.graph;
+	this.legend = args.legend;
+
+	var self = this;
+
+	this.addAnchor = function(line) {
+		var anchor = document.createElement('a');
+		anchor.innerHTML = '&#10004;';
+		anchor.classList.add('action');
+		line.element.insertBefore(anchor, line.element.firstChild);
+
+		anchor.onclick = function(e) {
+			if (line.series.disabled) {
+				line.series.enable();
+				line.element.classList.remove('disabled');
+			} else { 
+				line.series.disable();
+				line.element.classList.add('disabled');
+			}
+		}
+		
+                var label = line.element.getElementsByTagName('span')[0];
+                label.onclick = function(e){
+
+                        var disableAllOtherLines = line.series.disabled;
+                        if ( ! disableAllOtherLines ) {
+                                for ( var i = 0; i < self.legend.lines.length; i++ ) {
+                                        var l = self.legend.lines[i];
+                                        if ( line.series === l.series ) {
+                                                // noop
+                                        } else if ( l.series.disabled ) {
+                                                // noop
+                                        } else {
+                                                disableAllOtherLines = true;
+                                                break;
+                                        }
+                                }
+                        }
+
+                        // show all or none
+                        if ( disableAllOtherLines ) {
+
+                                // these must happen first or else we try ( and probably fail ) to make a no line graph
+                                line.series.enable();
+                                line.element.classList.remove('disabled');
+
+                                self.legend.lines.forEach(function(l){
+                                        if ( line.series === l.series ) {
+                                                // noop
+                                        } else {
+                                                l.series.disable();
+                                                l.element.classList.add('disabled');
+                                        }
+                                });
+
+                        } else {
+
+                                self.legend.lines.forEach(function(l){
+                                        l.series.enable();
+                                        l.element.classList.remove('disabled');
+                                });
+
+                        }
+
+                };
+
+	};
+
+	if (this.legend) {
+
+                $(this.legend.list).sortable( {
+                        start: function(event, ui) {
+                                ui.item.bind('no.onclick',
+                                        function(event) {
+                                                event.preventDefault();
+                                        }
+                                );
+                        },
+                        stop: function(event, ui) {
+                                setTimeout(function(){
+                                        ui.item.unbind('no.onclick');
+                                }, 250);
+                        }
+                })
+
+		this.legend.lines.forEach( function(l) {
+			self.addAnchor(l);
+		} );
+	}
+
+	this._addBehavior = function() {
+
+		this.graph.series.forEach( function(s) {
+			
+			s.disable = function() {
+
+				if (self.graph.series.length <= 1) {
+					throw('only one series left');
+				}
+				
+				s.disabled = true;
+				self.graph.update();
+			};
+
+			s.enable = function() {
+				s.disabled = false;
+				self.graph.update();
+			};
+		} );
+	};
+	this._addBehavior();
+
+	this.updateBehaviour = function () { this._addBehavior() };
+
+};
+Rickshaw.namespace('Rickshaw.Graph.HoverDetail');
+
+Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({
+
+	initialize: function(args) {
+
+		var graph = this.graph = args.graph;
+
+		this.xFormatter = args.xFormatter || function(x) {
+			return new Date( x * 1000 ).toUTCString();
+		};
+
+		this.yFormatter = args.yFormatter || function(y) {
+			return y.toFixed(2);
+		};
+
+		var element = this.element = document.createElement('div');
+		element.className = 'detail';
+
+		this.visible = true;
+		graph.element.appendChild(element);
+
+		this.lastEvent = null;
+		this._addListeners();
+
+		this.onShow = args.onShow;
+		this.onHide = args.onHide;
+		this.onRender = args.onRender;
+
+		this.formatter = args.formatter || this.formatter;
+	},
+
+	formatter: function(series, x, y, formattedX, formattedY, d) {
+		return series.name + ':&nbsp;' + formattedY;
+	},
+
+	update: function(e) {
+
+		e = e || this.lastEvent;
+		if (!e) return;
+		this.lastEvent = e;
+
+		if (!e.target.nodeName.match(/^(path|svg|rect)$/)) return;
+
+		var graph = this.graph;
+
+		var eventX = e.offsetX || e.layerX;
+		var eventY = e.offsetY || e.layerY;
+
+		var domainX = graph.x.invert(eventX);
+		var stackedData = graph.stackedData;
+
+		var topSeriesData = stackedData.slice(-1).shift();
+
+		var domainIndexScale = d3.scale.linear()
+			.domain([topSeriesData[0].x, topSeriesData.slice(-1).shift().x])
+			.range([0, topSeriesData.length]);
+
+		var approximateIndex = Math.floor(domainIndexScale(domainX));
+		var dataIndex = Math.min(approximateIndex || 0, stackedData[0].length - 1);
+
+		for (var i = approximateIndex; i < stackedData[0].length - 1;) {
+
+			if (!stackedData[0][i] || !stackedData[0][i + 1]) {
+				break;
+			}
+
+			if (stackedData[0][i].x <= domainX && stackedData[0][i + 1].x > domainX) {
+				dataIndex = i;
+				break;
+			}
+			if (stackedData[0][i + 1] <= domainX) { i++ } else { i-- }
+		}
+
+		var domainX = stackedData[0][dataIndex].x;
+		var formattedXValue = this.xFormatter(domainX);
+		var graphX = graph.x(domainX);
+		var order = 0;
+
+		var detail = graph.series.active()
+			.map( function(s) { return { order: order++, series: s, name: s.name, value: s.stack[dataIndex] } } );
+
+		var activeItem;
+
+		var sortFn = function(a, b) {
+			return (a.value.y0 + a.value.y) - (b.value.y0 + b.value.y);
+		};
+
+		var domainMouseY = graph.y.magnitude.invert(graph.element.offsetHeight - eventY);
+
+		detail.sort(sortFn).forEach( function(d) {
+
+			d.formattedYValue = (this.yFormatter.constructor == Array) ?
+				this.yFormatter[detail.indexOf(d)](d.value.y) :
+				this.yFormatter(d.value.y);
+
+			d.graphX = graphX;
+			d.graphY = graph.y(d.value.y0 + d.value.y);
+
+			if (domainMouseY > d.value.y0 && domainMouseY < d.value.y0 + d.value.y && !activeItem) {
+				activeItem = d;
+				d.active = true;
+			}
+
+		}, this );
+
+		this.element.innerHTML = '';
+		this.element.style.left = graph.x(domainX) + 'px';
+
+		if (this.visible) {
+			this.render( {
+				detail: detail,
+				domainX: domainX,
+				formattedXValue: formattedXValue,
+				mouseX: eventX,
+				mouseY: eventY
+			} );
+		}
+	},
+
+	hide: function() {
+		this.visible = false;
+		this.element.classList.add('inactive');
+
+		if (typeof this.onHide == 'function') {
+			this.onHide();
+		}
+	},
+
+	show: function() {
+		this.visible = true;
+		this.element.classList.remove('inactive');
+
+		if (typeof this.onShow == 'function') {
+			this.onShow();
+		}
+	},
+
+	render: function(args) {
+
+		var detail = args.detail;
+		var domainX = args.domainX;
+
+		var mouseX = args.mouseX;
+		var mouseY = args.mouseY;
+
+		var formattedXValue = args.formattedXValue;
+
+		var xLabel = document.createElement('div');
+		xLabel.className = 'x_label';
+		xLabel.innerHTML = formattedXValue;
+		this.element.appendChild(xLabel);
+
+		detail.forEach( function(d) {
+
+			var item = document.createElement('div');
+			item.className = 'item';
+			item.innerHTML = this.formatter(d.series, domainX, d.value.y, formattedXValue, d.formattedYValue, d);
+			item.style.top = this.graph.y(d.value.y0 + d.value.y) + 'px';
+
+			this.element.appendChild(item);
+
+			var dot = document.createElement('div');
+			dot.className = 'dot';
+			dot.style.top = item.style.top;
+			dot.style.borderColor = d.series.color;
+
+			this.element.appendChild(dot);
+
+			if (d.active) {
+				item.className = 'item active';
+				dot.className = 'dot active';
+			}
+
+		}, this );
+
+		this.show();
+
+		if (typeof this.onRender == 'function') {
+			this.onRender(args);
+		}
+	},
+
+	_addListeners: function() {
+
+		this.graph.element.addEventListener(
+			'mousemove',
+			function(e) {
+				this.visible = true;
+				this.update(e)
+			}.bind(this),
+			false
+		);
+
+		this.graph.onUpdate( function() { this.update() }.bind(this) );
+
+		this.graph.element.addEventListener(
+			'mouseout',
+			function(e) {
+				if (e.relatedTarget && !(e.relatedTarget.compareDocumentPosition(this.graph.element) & Node.DOCUMENT_POSITION_CONTAINS)) {
+					this.hide();
+				}
+			 }.bind(this),
+			false
+		);
+	}
+});
+
+Rickshaw.namespace('Rickshaw.Graph.JSONP');
+
+Rickshaw.Graph.JSONP = Rickshaw.Class.create( Rickshaw.Graph.Ajax, {
+
+	request: function() {
+
+		$.ajax( {
+			url: this.dataURL,
+			dataType: 'jsonp',
+			success: this.success.bind(this),
+			error: this.error.bind(this)
+		} );
+	}
+} );
+Rickshaw.namespace('Rickshaw.Graph.Legend');
+
+Rickshaw.Graph.Legend = function(args) {
+
+	var element = this.element = args.element;
+	var graph = this.graph = args.graph;
+
+	var self = this;
+
+	element.classList.add('rickshaw_legend');
+
+	var list = this.list = document.createElement('ul');
+	element.appendChild(list);
+
+	var series = graph.series
+		.map( function(s) { return s } )
+		.reverse();
+
+	this.lines = [];
+
+	this.addLine = function (series) {
+		var line = document.createElement('li');
+		line.className = 'line';
+
+		var swatch = document.createElement('div');
+		swatch.className = 'swatch';
+		swatch.style.backgroundColor = series.color;
+
+		line.appendChild(swatch);
+
+		var label = document.createElement('span');
+		label.className = 'label';
+		label.innerHTML = series.name;
+
+		line.appendChild(label);
+		list.appendChild(line);
+
+		line.series = series;
+
+		if (series.noLegend) {
+			line.style.display = 'none';
+		}
+
+		var _line = { element: line, series: series };
+		if (self.shelving) {
+			self.shelving.addAnchor(_line);
+			self.shelving.updateBehaviour();
+		}
+		if (self.highlighter) {
+			self.highlighter.addHighlightEvents(_line);
+		}
+		self.lines.push(_line);
+	};
+
+	series.forEach( function(s) {
+		self.addLine(s);
+	} );
+
+	graph.onUpdate( function() {} );
+};
+Rickshaw.namespace('Rickshaw.Graph.RangeSlider');
+
+Rickshaw.Graph.RangeSlider = function(args) {
+
+	var element = this.element = args.element;
+	var graph = this.graph = args.graph;
+
+	$( function() {
+		$(element).slider( {
+
+			range: true,
+			min: graph.dataDomain()[0],
+			max: graph.dataDomain()[1],
+			values: [ 
+				graph.dataDomain()[0],
+				graph.dataDomain()[1]
+			],
+			slide: function( event, ui ) {
+
+				graph.window.xMin = ui.values[0];
+				graph.window.xMax = ui.values[1];
+				graph.update();
+
+				// if we're at an extreme, stick there
+				if (graph.dataDomain()[0] == ui.values[0]) {
+					graph.window.xMin = undefined;
+				}
+				if (graph.dataDomain()[1] == ui.values[1]) {
+					graph.window.xMax = undefined;
+				}
+			}
+		} );
+	} );
+
+	element[0].style.width = graph.width + 'px';
+
+	graph.onUpdate( function() {
+
+		var values = $(element).slider('option', 'values');
+
+		$(element).slider('option', 'min', graph.dataDomain()[0]);
+		$(element).slider('option', 'max', graph.dataDomain()[1]);
+
+		if (graph.window.xMin == undefined) {
+			values[0] = graph.dataDomain()[0];
+		}
+		if (graph.window.xMax == undefined) {
+			values[1] = graph.dataDomain()[1];
+		}
+
+		$(element).slider('option', 'values', values);
+
+	} );
+};
+
+Rickshaw.namespace("Rickshaw.Graph.Renderer");
+
+Rickshaw.Graph.Renderer = Rickshaw.Class.create( {
+
+	initialize: function(args) {
+		this.graph = args.graph;
+		this.tension = args.tension || this.tension;
+		this.graph.unstacker = this.graph.unstacker || new Rickshaw.Graph.Unstacker( { graph: this.graph } );
+		this.configure(args);
+	},
+
+	seriesPathFactory: function() {
+		//implement in subclass
+	},
+
+	seriesStrokeFactory: function() {
+		// implement in subclass
+	},
+
+	defaults: function() {
+		return {
+			tension: 0.8,
+			strokeWidth: 2,
+			unstack: true,
+			padding: { top: 0.01, right: 0, bottom: 0.01, left: 0 },
+			stroke: false,
+			fill: false
+		};
+	},
+
+	domain: function() {
+
+		var values = [];
+		var stackedData = this.graph.stackedData || this.graph.stackData();
+
+		var topSeriesData = this.unstack ? stackedData : [ stackedData.slice(-1).shift() ];
+
+		topSeriesData.forEach( function(series) {
+			series.forEach( function(d) {
+				values.push( d.y + d.y0 );
+			} );
+		} );
+
+		var xMin = stackedData[0][0].x;
+		var xMax = stackedData[0][ stackedData[0].length - 1 ].x;
+
+		xMin -= (xMax - xMin) * this.padding.left;
+		xMax += (xMax - xMin) * this.padding.right;
+
+		var yMin = this.graph.min === 'auto' ? d3.min( values ) : this.graph.min || 0;
+		var yMax = this.graph.max || d3.max( values );
+
+		if (this.graph.min === 'auto' || yMin < 0) {
+			yMin -= (yMax - yMin) * this.padding.bottom;
+		}
+
+		if (this.graph.max === undefined) {
+			yMax += (yMax - yMin) * this.padding.top;
+		}
+
+		return { x: [xMin, xMax], y: [yMin, yMax] };
+	},
+
+	render: function() {
+
+		var graph = this.graph;
+
+		graph.vis.selectAll('*').remove();
+
+		var nodes = graph.vis.selectAll("path")
+			.data(this.graph.stackedData)
+			.enter().append("svg:path")
+			.attr("d", this.seriesPathFactory());
+
+		var i = 0;
+		graph.series.forEach( function(series) {
+			if (series.disabled) return;
+			series.path = nodes[0][i++];
+			this._styleSeries(series);
+		}, this );
+	},
+
+	_styleSeries: function(series) {
+
+		var fill = this.fill ? series.color : 'none';
+		var stroke = this.stroke ? series.color : 'none';
+
+		series.path.setAttribute('fill', fill);
+		series.path.setAttribute('stroke', stroke);
+		series.path.setAttribute('stroke-width', this.strokeWidth);
+		series.path.setAttribute('class', series.className);
+	},
+
+	configure: function(args) {
+
+		args = args || {};
+
+		Rickshaw.keys(this.defaults()).forEach( function(key) {
+
+			if (!args.hasOwnProperty(key)) {
+				this[key] = this[key] || this.graph[key] || this.defaults()[key];
+				return;
+			}
+
+			if (typeof this.defaults()[key] == 'object') {
+
+				Rickshaw.keys(this.defaults()[key]).forEach( function(k) {
+
+					this[key][k] =
+						args[key][k] !== undefined ? args[key][k] :
+						this[key][k] !== undefined ? this[key][k] :
+						this.defaults()[key][k];
+				}, this );
+
+			} else {
+				this[key] =
+					args[key] !== undefined ? args[key] :
+					this[key] !== undefined ? this[key] :
+					this.graph[key] !== undefined ? this.graph[key] :
+					this.defaults()[key];
+			}
+
+		}, this );
+	},
+
+	setStrokeWidth: function(strokeWidth) {
+		if (strokeWidth !== undefined) {
+			this.strokeWidth = strokeWidth;
+		}
+	},
+
+	setTension: function(tension) {
+		if (tension !== undefined) {
+			this.tension = tension;
+		}
+	}
+} );
+
+Rickshaw.namespace('Rickshaw.Graph.Renderer.Line');
+
+Rickshaw.Graph.Renderer.Line = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
+
+	name: 'line',
+
+	defaults: function($super) {
+
+		return Rickshaw.extend( $super(), {
+			unstack: true,
+			fill: false,
+			stroke: true
+		} );
+	},
+
+	seriesPathFactory: function() {
+
+		var graph = this.graph;
+
+		return d3.svg.line()
+			.x( function(d) { return graph.x(d.x) } )
+			.y( function(d) { return graph.y(d.y) } )
+			.interpolate(this.graph.interpolation).tension(this.tension);
+	}
+} );
+
+Rickshaw.namespace('Rickshaw.Graph.Renderer.Stack');
+
+Rickshaw.Graph.Renderer.Stack = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
+
+	name: 'stack',
+
+	defaults: function($super) {
+
+		return Rickshaw.extend( $super(), {
+			fill: true,
+			stroke: false,
+			unstack: false
+		} );
+	},
+
+	seriesPathFactory: function() {
+
+		var graph = this.graph;
+
+		return d3.svg.area()
+			.x( function(d) { return graph.x(d.x) } )
+			.y0( function(d) { return graph.y(d.y0) } )
+			.y1( function(d) { return graph.y(d.y + d.y0) } )
+			.interpolate(this.graph.interpolation).tension(this.tension);
+	}
+} );
+
+Rickshaw.namespace('Rickshaw.Graph.Renderer.Bar');
+
+Rickshaw.Graph.Renderer.Bar = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
+
+	name: 'bar',
+
+	defaults: function($super) {
+
+		var defaults = Rickshaw.extend( $super(), {
+			gapSize: 0.05,
+			unstack: false
+		} );
+
+		delete defaults.tension;
+		return defaults;
+	},
+
+	initialize: function($super, args) {
+		args = args || {};
+		this.gapSize = args.gapSize || this.gapSize;
+		$super(args);
+	},
+
+	domain: function($super) {
+
+		var domain = $super();
+
+		var frequentInterval = this._frequentInterval();
+		domain.x[1] += parseInt(frequentInterval.magnitude);
+
+		return domain;
+	},
+
+	barWidth: function() {
+
+		var stackedData = this.graph.stackedData || this.graph.stackData();
+		var data = stackedData.slice(-1).shift();
+
+		var frequentInterval = this._frequentInterval();
+		var barWidth = this.graph.x(data[0].x + frequentInterval.magnitude * (1 - this.gapSize)); 
+
+		return barWidth;
+	},
+
+	render: function() {
+
+		var graph = this.graph;
+
+		graph.vis.selectAll('*').remove();
+
+		var barWidth = this.barWidth();
+		var barXOffset = 0;
+
+		var activeSeriesCount = graph.series.filter( function(s) { return !s.disabled; } ).length;
+		var seriesBarWidth = this.unstack ? barWidth / activeSeriesCount : barWidth;
+
+		var transform = function(d) {
+			// add a matrix transform for negative values
+			var matrix = [ 1, 0, 0, (d.y < 0 ? -1 : 1), 0, (d.y < 0 ? graph.y.magnitude(Math.abs(d.y)) * 2 : 0) ];
+			return "matrix(" + matrix.join(',') + ")";
+		};
+
+		graph.series.forEach( function(series) {
+
+			if (series.disabled) return;
+
+			var nodes = graph.vis.selectAll("path")
+				.data(series.stack)
+				.enter().append("svg:rect")
+				.attr("x", function(d) { return graph.x(d.x) + barXOffset })
+				.attr("y", function(d) { return (graph.y(d.y0 + Math.abs(d.y))) * (d.y < 0 ? -1 : 1 ) })
+				.attr("width", seriesBarWidth)
+				.attr("height", function(d) { return graph.y.magnitude(Math.abs(d.y)) })
+				.attr("transform", transform);
+
+			Array.prototype.forEach.call(nodes[0], function(n) {
+				n.setAttribute('fill', series.color);
+			} );
+
+			if (this.unstack) barXOffset += seriesBarWidth;
+
+		}, this );
+	},
+
+	_frequentInterval: function() {
+
+		var stackedData = this.graph.stackedData || this.graph.stackData();
+		var data = stackedData.slice(-1).shift();
+
+		var intervalCounts = {};
+
+		for (var i = 0; i < data.length - 1; i++) {
+			var interval = data[i + 1].x - data[i].x;
+			intervalCounts[interval] = intervalCounts[interval] || 0;
+			intervalCounts[interval]++;
+		}
+
+		var frequentInterval = { count: 0 };
+
+		Rickshaw.keys(intervalCounts).forEach( function(i) {
+			if (frequentInterval.count < intervalCounts[i]) {
+
+				frequentInterval = {
+					count: intervalCounts[i],
+					magnitude: i
+				};
+			}
+		} );
+
+		this._frequentInterval = function() { return frequentInterval };
+
+		return frequentInterval;
+	}
+} );
+
+Rickshaw.namespace('Rickshaw.Graph.Renderer.Area');
+
+Rickshaw.Graph.Renderer.Area = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
+
+	name: 'area',
+
+	defaults: function($super) {
+
+		return Rickshaw.extend( $super(), {
+			unstack: false,
+			fill: false,
+			stroke: false
+		} );
+	},
+
+	seriesPathFactory: function() {
+
+		var graph = this.graph;
+
+		return d3.svg.area()
+			.x( function(d) { return graph.x(d.x) } )
+			.y0( function(d) { return graph.y(d.y0) } )
+			.y1( function(d) { return graph.y(d.y + d.y0) } )
+			.interpolate(graph.interpolation).tension(this.tension);
+	},
+
+	seriesStrokeFactory: function() {
+
+		var graph = this.graph;
+
+		return d3.svg.line()
+			.x( function(d) { return graph.x(d.x) } )
+			.y( function(d) { return graph.y(d.y + d.y0) } )
+			.interpolate(graph.interpolation).tension(this.tension);
+	},
+
+	render: function() {
+
+		var graph = this.graph;
+
+		graph.vis.selectAll('*').remove();
+
+		var nodes = graph.vis.selectAll("path")
+			.data(this.graph.stackedData)
+			.enter().insert("svg:g", 'g');
+
+		nodes.append("svg:path")
+			.attr("d", this.seriesPathFactory())
+			.attr("class", 'area');
+
+		if (this.stroke) {
+			nodes.append("svg:path")
+				.attr("d", this.seriesStrokeFactory())
+				.attr("class", 'line');
+		}
+
+		var i = 0;
+		graph.series.forEach( function(series) {
+			if (series.disabled) return;
+			series.path = nodes[0][i++];
+			this._styleSeries(series);
+		}, this );
+	},
+
+	_styleSeries: function(series) {
+
+		if (!series.path) return;
+
+		d3.select(series.path).select('.area')
+			.attr('fill', series.color);
+
+		if (this.stroke) {
+			d3.select(series.path).select('.line')
+				.attr('fill', 'none')
+				.attr('stroke', series.stroke || d3.interpolateRgb(series.color, 'black')(0.125))
+				.attr('stroke-width', this.strokeWidth);
+		}
+
+		if (series.className) {
+			series.path.setAttribute('class', series.className);
+		}
+	}
+} );
+
+Rickshaw.namespace('Rickshaw.Graph.Renderer.ScatterPlot');
+
+Rickshaw.Graph.Renderer.ScatterPlot = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
+
+	name: 'scatterplot',
+
+	defaults: function($super) {
+
+		return Rickshaw.extend( $super(), {
+			unstack: true,
+			fill: true,
+			stroke: false,
+			padding:{ top: 0.01, right: 0.01, bottom: 0.01, left: 0.01 },
+			dotSize: 4
+		} );
+	},
+
+	initialize: function($super, args) {
+		$super(args);
+	},
+
+	render: function() {
+
+		var graph = this.graph;
+
+		graph.vis.selectAll('*').remove();
+
+		graph.series.forEach( function(series) {
+
+			if (series.disabled) return;
+
+			var nodes = graph.vis.selectAll("path")
+				.data(series.stack)
+				.enter().append("svg:circle")
+				.attr("cx", function(d) { return graph.x(d.x) })
+				.attr("cy", function(d) { return graph.y(d.y) })
+				.attr("r", function(d) { return ("r" in d) ? d.r : graph.renderer.dotSize});
+
+			Array.prototype.forEach.call(nodes[0], function(n) {
+				n.setAttribute('fill', series.color);
+			} );
+
+		}, this );
+	}
+} );
+Rickshaw.namespace('Rickshaw.Graph.Smoother');
+
+Rickshaw.Graph.Smoother = function(args) {
+
+	this.graph = args.graph;
+	this.element = args.element;
+
+	var self = this;
+
+	this.aggregationScale = 1;
+
+	if (this.element) {
+
+		$( function() {
+			$(self.element).slider( {
+				min: 1,
+				max: 100,
+				slide: function( event, ui ) {
+					self.setScale(ui.value);
+					self.graph.update();
+				}
+			} );
+		} );
+	}
+
+	self.graph.stackData.hooks.data.push( {
+		name: 'smoother',
+		orderPosition: 50,
+		f: function(data) {
+
+			var aggregatedData = [];
+
+			data.forEach( function(seriesData) {
+				
+				var aggregatedSeriesData = [];
+
+				while (seriesData.length) {
+
+					var avgX = 0, avgY = 0;
+					var slice = seriesData.splice(0, self.aggregationScale);
+
+					slice.forEach( function(d) {
+						avgX += d.x / slice.length;
+						avgY += d.y / slice.length;
+					} );
+
+					aggregatedSeriesData.push( { x: avgX, y: avgY } );
+				}
+
+				aggregatedData.push(aggregatedSeriesData);
+			} );
+
+			return aggregatedData;
+		}
+	} );
+
+	this.setScale = function(scale) {
+
+		if (scale < 1) {
+			throw "scale out of range: " + scale;
+		}
+
+		this.aggregationScale = scale;
+		this.graph.update();
+	}
+};
+
+Rickshaw.namespace('Rickshaw.Graph.Unstacker');
+
+Rickshaw.Graph.Unstacker = function(args) {
+
+	this.graph = args.graph;
+	var self = this;
+
+	this.graph.stackData.hooks.after.push( {
+		name: 'unstacker',
+		f: function(data) {
+
+			if (!self.graph.renderer.unstack) return data;
+
+			data.forEach( function(seriesData) {
+				seriesData.forEach( function(d) {
+					d.y0 = 0;
+				} );
+			} );
+
+			return data;
+		}
+	} );
+};
+
+Rickshaw.namespace('Rickshaw.Series');
+
+Rickshaw.Series = Rickshaw.Class.create( Array, {
+
+	initialize: function (data, palette, options) {
+
+		options = options || {}
+
+		this.palette = new Rickshaw.Color.Palette(palette);
+
+		this.timeBase = typeof(options.timeBase) === 'undefined' ? 
+			Math.floor(new Date().getTime() / 1000) : 
+			options.timeBase;
+
+		var timeInterval = typeof(options.timeInterval) == 'undefined' ?
+			1000 :
+			options.timeInterval;
+
+		this.setTimeInterval(timeInterval);
+
+		if (data && (typeof(data) == "object") && (data instanceof Array)) {
+			data.forEach( function(item) { this.addItem(item) }, this );
+		}
+	},
+
+	addItem: function(item) {
+
+		if (typeof(item.name) === 'undefined') {
+			throw('addItem() needs a name');
+		}
+
+		item.color = (item.color || this.palette.color(item.name));
+		item.data = (item.data || []);
+
+		// backfill, if necessary
+		if ((item.data.length == 0) && this.length && (this.getIndex() > 0)) {
+			this[0].data.forEach( function(plot) {
+				item.data.push({ x: plot.x, y: 0 });
+			} );
+		} else if (item.data.length == 0) {
+			item.data.push({ x: this.timeBase - (this.timeInterval || 0), y: 0 });
+		} 
+
+		this.push(item);
+
+		if (this.legend) {
+			this.legend.addLine(this.itemByName(item.name));
+		}
+	},
+
+	addData: function(data) {
+
+		var index = this.getIndex();
+
+		Rickshaw.keys(data).forEach( function(name) {
+			if (! this.itemByName(name)) {
+				this.addItem({ name: name });
+			}
+		}, this );
+
+		this.forEach( function(item) {
+			item.data.push({ 
+				x: (index * this.timeInterval || 1) + this.timeBase, 
+				y: (data[item.name] || 0) 
+			});
+		}, this );
+	},
+
+	getIndex: function () {
+		return (this[0] && this[0].data && this[0].data.length) ? this[0].data.length : 0;
+	},
+
+	itemByName: function(name) {
+
+		for (var i = 0; i < this.length; i++) {
+			if (this[i].name == name)
+				return this[i];
+		}
+	},
+
+	setTimeInterval: function(iv) {
+		this.timeInterval = iv / 1000;
+	},
+
+	setTimeBase: function (t) {
+		this.timeBase = t;
+	},
+
+	dump: function() {
+
+		var data = {
+			timeBase: this.timeBase,
+			timeInterval: this.timeInterval,
+			items: []
+		};
+
+		this.forEach( function(item) {
+
+			var newItem = {
+				color: item.color,
+				name: item.name,
+				data: []
+			};
+
+			item.data.forEach( function(plot) {
+				newItem.data.push({ x: plot.x, y: plot.y });
+			} );
+
+			data.items.push(newItem);
+		} );
+
+		return data;
+	},
+
+	load: function(data) {
+
+		if (data.timeInterval) {
+			this.timeInterval = data.timeInterval;
+		}
+
+		if (data.timeBase) {
+			this.timeBase = data.timeBase;
+		}
+
+		if (data.items) {
+			data.items.forEach( function(item) {
+				this.push(item);
+				if (this.legend) {
+					this.legend.addLine(this.itemByName(item.name));
+				}
+
+			}, this );
+		}
+	}
+} );
+
+Rickshaw.Series.zeroFill = function(series) {
+
+	var x;
+	var i = 0;
+
+	var data = series.map( function(s) { return s.data } );
+
+	while ( i < Math.max.apply(null, data.map( function(d) { return d.length } )) ) {
+
+		x = Math.min.apply( null, 
+			data
+				.filter(function(d) { return d[i] })
+				.map(function(d) { return d[i].x })
+		);
+
+		data.forEach( function(d) {
+			if (!d[i] || d[i].x != x) {
+				d.splice(i, 0, { x: x, y: 0 });
+			}
+		} );
+
+		i++;
+	}
+};
+Rickshaw.namespace('Rickshaw.Series.FixedDuration');
+
+Rickshaw.Series.FixedDuration = Rickshaw.Class.create(Rickshaw.Series, {
+
+	initialize: function (data, palette, options) {
+
+		var options = options || {}
+
+		if (typeof(options.timeInterval) === 'undefined') {
+			throw new Error('FixedDuration series requires timeInterval');
+		}
+
+		if (typeof(options.maxDataPoints) === 'undefined') {
+			throw new Error('FixedDuration series requires maxDataPoints');
+		}
+
+		this.palette = new Rickshaw.Color.Palette(palette);
+		this.timeBase = typeof(options.timeBase) === 'undefined' ? Math.floor(new Date().getTime() / 1000) : options.timeBase;
+		this.setTimeInterval(options.timeInterval);
+
+		if (this[0] && this[0].data && this[0].data.length) {
+			this.currentSize = this[0].data.length;
+			this.currentIndex = this[0].data.length;
+		} else {
+			this.currentSize  = 0;
+			this.currentIndex = 0;
+		}
+
+		this.maxDataPoints = options.maxDataPoints;
+
+
+		if (data && (typeof(data) == "object") && (data instanceof Array)) {
+			data.forEach( function (item) { this.addItem(item) }, this );
+			this.currentSize  += 1;
+			this.currentIndex += 1;
+		}
+
+		// reset timeBase for zero-filled values if needed
+		this.timeBase -= (this.maxDataPoints - this.currentSize) * this.timeInterval;
+
+		// zero-fill up to maxDataPoints size if we don't have that much data yet
+		if ((typeof(this.maxDataPoints) !== 'undefined') && (this.currentSize < this.maxDataPoints)) {
+			for (var i = this.maxDataPoints - this.currentSize - 1; i > 0; i--) {
+				this.currentSize  += 1;
+				this.currentIndex += 1;
+				this.forEach( function (item) {
+					item.data.unshift({ x: ((i-1) * this.timeInterval || 1) + this.timeBase, y: 0, i: i });
+				}, this );
+			}
+		}
+	},
+
+	addData: function($super, data) {
+
+		$super(data)
+
+		this.currentSize += 1;
+		this.currentIndex += 1;
+
+		if (this.maxDataPoints !== undefined) {
+			while (this.currentSize > this.maxDataPoints) {
+				this.dropData();
+			}
+		}
+	},
+
+	dropData: function() {
+
+		this.forEach(function(item) {
+			item.data.splice(0, 1);
+		} );
+
+		this.currentSize -= 1;
+	},
+
+	getIndex: function () {
+		return this.currentIndex;
+	}
+} );
+

Added: incubator/ambari/branches/AMBARI-666/ambari-web/vendor/styles/rickshaw.css
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-web/vendor/styles/rickshaw.css?rev=1400490&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-web/vendor/styles/rickshaw.css (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-web/vendor/styles/rickshaw.css Sat Oct 20 17:17:07 2012
@@ -0,0 +1,307 @@
+.rickshaw_graph .detail {
+	pointer-events: none;
+	position: absolute;
+	top: 0;
+	z-index: 2;
+	background: rgba(0, 0, 0, 0.1);
+	bottom: 0;
+	width: 1px;
+	transition: opacity 0.25s linear;
+	-moz-transition: opacity 0.25s linear;
+	-o-transition: opacity 0.25s linear;
+	-webkit-transition: opacity 0.25s linear;
+}
+.rickshaw_graph .detail.inactive {
+	opacity: 0;
+}
+.rickshaw_graph .detail .item.active {
+	opacity: 1;
+}
+.rickshaw_graph .detail .x_label {
+	font-family: Arial, sans-serif;
+	border-radius: 3px;
+	padding: 6px;
+	opacity: 0.5;
+	border: 1px solid #e0e0e0;
+	font-size: 12px;
+	position: absolute;
+	background: white;
+	white-space: nowrap;
+}
+.rickshaw_graph .detail .item {
+	position: absolute;
+	z-index: 2;
+	border-radius: 3px;
+	padding: 0.25em;
+	font-size: 12px;
+	font-family: Arial, sans-serif;
+	opacity: 0;
+	background: rgba(0, 0, 0, 0.4);
+	color: white;
+	border: 1px solid rgba(0, 0, 0, 0.4);
+	margin-left: 1em;
+	margin-top: -1em;
+	white-space: nowrap;
+}
+.rickshaw_graph .detail .item.active {
+	opacity: 1;
+	background: rgba(0, 0, 0, 0.8);
+}
+.rickshaw_graph .detail .item:before {
+	content: "\25c2";
+	position: absolute;
+	left: -0.5em;
+	color: rgba(0, 0, 0, 0.7);
+	width: 0;
+}
+.rickshaw_graph .detail .dot {
+	width: 4px;
+	height: 4px;
+	margin-left: -4px;
+	margin-top: -3px;
+	border-radius: 5px;
+	position: absolute;
+	box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
+	background: white;
+	border-width: 2px;
+	border-style: solid;
+	display: none;
+	background-clip: padding-box;
+}
+.rickshaw_graph .detail .dot.active {
+	display: block;
+}
+/* graph */
+
+.rickshaw_graph {
+	position: relative;
+}
+.rickshaw_graph svg {
+	display: block;	
+	overflow: hidden;
+}
+
+/* ticks */
+
+.rickshaw_graph .x_tick {
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	width: 0px;
+	border-left: 1px dotted rgba(0, 0, 0, 0.2);
+	pointer-events: none;
+}
+.rickshaw_graph .x_tick .title {
+	position: absolute;
+	font-size: 12px;
+	font-family: Arial, sans-serif;
+	opacity: 0.5;
+	white-space: nowrap;
+	margin-left: 3px;
+	bottom: 1px;
+}
+
+/* annotations */
+
+.rickshaw_annotation_timeline {
+	height: 1px;
+	border-top: 1px solid #e0e0e0;
+	margin-top: 10px;
+	position: relative;
+}
+.rickshaw_annotation_timeline .annotation {
+	position: absolute;
+	height: 6px;
+	width: 6px;
+	margin-left: -2px;
+	top: -3px;
+	border-radius: 5px;
+	background-color: rgba(0, 0, 0, 0.25);
+}
+.rickshaw_graph .annotation_line {
+	position: absolute;
+	top: 0;
+	bottom: -6px;
+	width: 0px;
+	border-left: 2px solid rgba(0, 0, 0, 0.3);
+	display: none;
+}
+.rickshaw_graph .annotation_line.active {
+	display: block;
+}
+
+.rickshaw_graph .annotation_range {
+        background: rgba(0, 0, 0, 0.1);
+        display: none;
+        position: absolute;
+        top: 0;
+        bottom: -6px;
+        z-index: -10;
+}
+.rickshaw_graph .annotation_range.active {
+        display: block;
+}
+.rickshaw_graph .annotation_range.active.offscreen {
+        display: none;
+}
+
+.rickshaw_annotation_timeline .annotation .content {
+	background: white;
+	color: black;
+	opacity: 0.9;
+	padding: 5px 5px;
+	box-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
+	border-radius: 3px;
+	position: relative;
+	z-index: 20;
+	font-size: 12px;
+	padding: 6px 8px 8px;
+	top: 18px;
+	left: -11px;
+	width: 160px;
+	display: none;
+	cursor: pointer;
+}
+.rickshaw_annotation_timeline .annotation .content:before {
+	content: "\25b2";
+	position: absolute;
+	top: -11px;
+	color: white;
+	text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.8);
+}
+.rickshaw_annotation_timeline .annotation.active,
+.rickshaw_annotation_timeline .annotation:hover {
+	background-color: rgba(0, 0, 0, 0.8);
+	cursor: none;
+}
+.rickshaw_annotation_timeline .annotation .content:hover {
+	z-index: 50;
+}
+.rickshaw_annotation_timeline .annotation.active .content {
+	display: block;
+}
+.rickshaw_annotation_timeline .annotation:hover .content {
+	display: block;
+	z-index: 50;
+}
+.rickshaw_graph .y_axis {
+	fill: none;
+}
+.rickshaw_graph .y_ticks .tick {
+	stroke: rgba(0, 0, 0, 0.16);
+	stroke-width: 2px;
+	shape-rendering: crisp-edges;
+	pointer-events: none;
+}
+.rickshaw_graph .y_grid .tick {
+	z-index: -1;
+	stroke: rgba(0, 0, 0, 0.20);
+	stroke-width: 1px;
+	stroke-dasharray: 1 1;
+}
+.rickshaw_graph .y_grid path {
+	fill: none;
+	stroke: none;
+}
+.rickshaw_graph .y_ticks path {
+	fill: none;
+	stroke: #808080;
+}
+.rickshaw_graph .y_ticks text {
+	opacity: 0.5;
+	font-size: 12px;
+	pointer-events: none;
+}
+.rickshaw_graph .x_tick.glow .title,
+.rickshaw_graph .y_ticks.glow text {
+	fill: black;
+	color: black;
+	text-shadow: 
+		-1px 1px 0 rgba(255, 255, 255, 0.1),
+		1px -1px 0 rgba(255, 255, 255, 0.1),
+		1px 1px 0 rgba(255, 255, 255, 0.1),
+		0px 1px 0 rgba(255, 255, 255, 0.1),
+		0px -1px 0 rgba(255, 255, 255, 0.1),
+		1px 0px 0 rgba(255, 255, 255, 0.1),
+		-1px 0px 0 rgba(255, 255, 255, 0.1),
+		-1px -1px 0 rgba(255, 255, 255, 0.1);
+}
+.rickshaw_graph .x_tick.inverse .title,
+.rickshaw_graph .y_ticks.inverse text {
+	fill: white;
+	color: white;
+	text-shadow: 
+		-1px 1px 0 rgba(0, 0, 0, 0.8),
+		1px -1px 0 rgba(0, 0, 0, 0.8),
+		1px 1px 0 rgba(0, 0, 0, 0.8),
+		0px 1px 0 rgba(0, 0, 0, 0.8),
+		0px -1px 0 rgba(0, 0, 0, 0.8),
+		1px 0px 0 rgba(0, 0, 0, 0.8),
+		-1px 0px 0 rgba(0, 0, 0, 0.8),
+		-1px -1px 0 rgba(0, 0, 0, 0.8);
+}
+.rickshaw_legend {
+	font-family: Arial;
+	font-size: 12px;
+	color: white;
+	background: #404040;
+	display: inline-block;
+	padding: 12px 5px; 
+	border-radius: 2px;
+	position: relative;
+}
+.rickshaw_legend:hover {
+	z-index: 10;
+}
+.rickshaw_legend .swatch {
+	width: 10px;
+	height: 10px;
+	border: 1px solid rgba(0, 0, 0, 0.2);
+}
+.rickshaw_legend .line {
+	clear: both;
+	line-height: 140%;
+	padding-right: 15px;
+}
+.rickshaw_legend .line .swatch {
+	display: inline-block;
+	margin-right: 3px;
+	border-radius: 2px;
+}
+.rickshaw_legend .label {
+	white-space: nowrap;
+	display: inline;
+}
+.rickshaw_legend .action:hover {
+	opacity: 0.6;
+}
+.rickshaw_legend .action {
+	margin-right: 0.2em;
+	font-size: 10px;
+	opacity: 0.2;
+	cursor: pointer;
+	font-size: 14px;
+}
+.rickshaw_legend .line.disabled {
+	opacity: 0.4;
+}
+.rickshaw_legend ul {
+	list-style-type: none;
+	margin: 0;
+	padding: 0;
+	margin: 2px;
+	cursor: pointer;
+}
+.rickshaw_legend li {
+	padding: 0 0 0 2px;
+	min-width: 80px;
+	white-space: nowrap;
+}
+.rickshaw_legend li:hover {
+	background: rgba(255, 255, 255, 0.08);
+	border-radius: 3px;
+}
+.rickshaw_legend li:active {
+	background: rgba(255, 255, 255, 0.2);
+	border-radius: 3px;
+}