on the whole page.
+ left += chartContainer.offsetLeft + svgOffset.left - 2*chartContainer.scrollLeft;
+ top += chartContainer.offsetTop + svgOffset.top - 2*chartContainer.scrollTop;
+ }
+
+ if (snapDistance && snapDistance > 0) {
+ top = Math.floor(top/snapDistance) * snapDistance;
+ }
+
+ nv.tooltip.calcTooltipPosition([left,top], gravity, distance, container);
+ return nvtooltip;
+ }
+
+ nvtooltip.nvPointerEventsClass = nvPointerEventsClass;
+
+ nvtooltip.content = function(_) {
+ if (!arguments.length) return content;
+ content = _;
+ return nvtooltip;
+ };
+
+ //Returns tooltipElem...not able to set it.
+ nvtooltip.tooltipElem = function() {
+ return tooltipElem;
+ };
+
+ nvtooltip.contentGenerator = function(_) {
+ if (!arguments.length) return contentGenerator;
+ if (typeof _ === 'function') {
+ contentGenerator = _;
+ }
+ return nvtooltip;
+ };
+
+ nvtooltip.data = function(_) {
+ if (!arguments.length) return data;
+ data = _;
+ return nvtooltip;
+ };
+
+ nvtooltip.gravity = function(_) {
+ if (!arguments.length) return gravity;
+ gravity = _;
+ return nvtooltip;
+ };
+
+ nvtooltip.distance = function(_) {
+ if (!arguments.length) return distance;
+ distance = _;
+ return nvtooltip;
+ };
+
+ nvtooltip.snapDistance = function(_) {
+ if (!arguments.length) return snapDistance;
+ snapDistance = _;
+ return nvtooltip;
+ };
+
+ nvtooltip.classes = function(_) {
+ if (!arguments.length) return classes;
+ classes = _;
+ return nvtooltip;
+ };
+
+ nvtooltip.chartContainer = function(_) {
+ if (!arguments.length) return chartContainer;
+ chartContainer = _;
+ return nvtooltip;
+ };
+
+ nvtooltip.position = function(_) {
+ if (!arguments.length) return position;
+ position.left = (typeof _.left !== 'undefined') ? _.left : position.left;
+ position.top = (typeof _.top !== 'undefined') ? _.top : position.top;
+ return nvtooltip;
+ };
+
+ nvtooltip.fixedTop = function(_) {
+ if (!arguments.length) return fixedTop;
+ fixedTop = _;
+ return nvtooltip;
+ };
+
+ nvtooltip.enabled = function(_) {
+ if (!arguments.length) return enabled;
+ enabled = _;
+ return nvtooltip;
+ };
+
+ nvtooltip.valueFormatter = function(_) {
+ if (!arguments.length) return valueFormatter;
+ if (typeof _ === 'function') {
+ valueFormatter = _;
+ }
+ return nvtooltip;
+ };
+
+ nvtooltip.headerFormatter = function(_) {
+ if (!arguments.length) return headerFormatter;
+ if (typeof _ === 'function') {
+ headerFormatter = _;
+ }
+ return nvtooltip;
+ };
+
+ //id() is a read-only function. You can't use it to set the id.
+ nvtooltip.id = function() {
+ return id;
+ };
+
+ return nvtooltip;
+ };
+
+ //Original tooltip.show function. Kept for backward compatibility.
+ // pos = [left,top]
+ nv.tooltip.show = function(pos, content, gravity, dist, parentContainer, classes) {
+
+ //Create new tooltip div if it doesn't exist on DOM.
+ var container = document.createElement('div');
+ container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip');
+
+ var body = parentContainer;
+ if ( !parentContainer || parentContainer.tagName.match(/g|svg/i)) {
+ //If the parent element is an SVG element, place tooltip in the element.
+ body = document.getElementsByTagName('body')[0];
+ }
+
+ container.style.left = 0;
+ container.style.top = 0;
+ container.style.opacity = 0;
+ // Content can also be dom element
+ if (typeof content !== 'string')
+ container.appendChild(content);
+ else
+ container.innerHTML = content;
+ body.appendChild(container);
+
+ //If the parent container is an overflow
with scrollbars, subtract the scroll offsets.
+ if (parentContainer) {
+ pos[0] = pos[0] - parentContainer.scrollLeft;
+ pos[1] = pos[1] - parentContainer.scrollTop;
+ }
+ nv.tooltip.calcTooltipPosition(pos, gravity, dist, container);
+ };
+
+ //Looks up the ancestry of a DOM element, and returns the first NON-svg node.
+ nv.tooltip.findFirstNonSVGParent = function(Elem) {
+ while(Elem.tagName.match(/^g|svg$/i) !== null) {
+ Elem = Elem.parentNode;
+ }
+ return Elem;
+ };
+
+ //Finds the total offsetTop of a given DOM element.
+ //Looks up the entire ancestry of an element, up to the first relatively positioned element.
+ nv.tooltip.findTotalOffsetTop = function ( Elem, initialTop ) {
+ var offsetTop = initialTop;
+
+ do {
+ if( !isNaN( Elem.offsetTop ) ) {
+ offsetTop += (Elem.offsetTop);
+ }
+ } while( Elem = Elem.offsetParent );
+ return offsetTop;
+ };
+
+ //Finds the total offsetLeft of a given DOM element.
+ //Looks up the entire ancestry of an element, up to the first relatively positioned element.
+ nv.tooltip.findTotalOffsetLeft = function ( Elem, initialLeft) {
+ var offsetLeft = initialLeft;
+
+ do {
+ if( !isNaN( Elem.offsetLeft ) ) {
+ offsetLeft += (Elem.offsetLeft);
+ }
+ } while( Elem = Elem.offsetParent );
+ return offsetLeft;
+ };
+
+ //Global utility function to render a tooltip on the DOM.
+ //pos = [left,top] coordinates of where to place the tooltip, relative to the SVG chart container.
+ //gravity = how to orient the tooltip
+ //dist = how far away from the mouse to place tooltip
+ //container = tooltip DIV
+ nv.tooltip.calcTooltipPosition = function(pos, gravity, dist, container) {
+
+ var height = parseInt(container.offsetHeight),
+ width = parseInt(container.offsetWidth),
+ windowWidth = nv.utils.windowSize().width,
+ windowHeight = nv.utils.windowSize().height,
+ scrollTop = window.pageYOffset,
+ scrollLeft = window.pageXOffset,
+ left, top;
+
+ windowHeight = window.innerWidth >= document.body.scrollWidth ? windowHeight : windowHeight - 16;
+ windowWidth = window.innerHeight >= document.body.scrollHeight ? windowWidth : windowWidth - 16;
+
+ gravity = gravity || 's';
+ dist = dist || 20;
+
+ var tooltipTop = function ( Elem ) {
+ return nv.tooltip.findTotalOffsetTop(Elem, top);
+ };
+
+ var tooltipLeft = function ( Elem ) {
+ return nv.tooltip.findTotalOffsetLeft(Elem,left);
+ };
+
+ switch (gravity) {
+ case 'e':
+ left = pos[0] - width - dist;
+ top = pos[1] - (height / 2);
+ var tLeft = tooltipLeft(container);
+ var tTop = tooltipTop(container);
+ if (tLeft < scrollLeft) left = pos[0] + dist > scrollLeft ? pos[0] + dist : scrollLeft - tLeft + left;
+ if (tTop < scrollTop) top = scrollTop - tTop + top;
+ if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
+ break;
+ case 'w':
+ left = pos[0] + dist;
+ top = pos[1] - (height / 2);
+ var tLeft = tooltipLeft(container);
+ var tTop = tooltipTop(container);
+ if (tLeft + width > windowWidth) left = pos[0] - width - dist;
+ if (tTop < scrollTop) top = scrollTop + 5;
+ if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
+ break;
+ case 'n':
+ left = pos[0] - (width / 2) - 5;
+ top = pos[1] + dist;
+ var tLeft = tooltipLeft(container);
+ var tTop = tooltipTop(container);
+ if (tLeft < scrollLeft) left = scrollLeft + 5;
+ if (tLeft + width > windowWidth) left = left - width/2 + 5;
+ if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
+ break;
+ case 's':
+ left = pos[0] - (width / 2);
+ top = pos[1] - height - dist;
+ var tLeft = tooltipLeft(container);
+ var tTop = tooltipTop(container);
+ if (tLeft < scrollLeft) left = scrollLeft + 5;
+ if (tLeft + width > windowWidth) left = left - width/2 + 5;
+ if (scrollTop > tTop) top = scrollTop;
+ break;
+ case 'none':
+ left = pos[0];
+ top = pos[1] - dist;
+ var tLeft = tooltipLeft(container);
+ var tTop = tooltipTop(container);
+ break;
+ }
+
+ container.style.left = left+'px';
+ container.style.top = top+'px';
+ container.style.opacity = 1;
+ container.style.position = 'absolute';
+
+ return container;
+ };
+
+ //Global utility function to remove tooltips from the DOM.
+ nv.tooltip.cleanup = function() {
+
+ // Find the tooltips, mark them for removal by this class (so others cleanups won't find it)
+ var tooltips = document.getElementsByClassName('nvtooltip');
+ var purging = [];
+ while(tooltips.length) {
+ purging.push(tooltips[0]);
+ tooltips[0].style.transitionDelay = '0 !important';
+ tooltips[0].style.opacity = 0;
+ tooltips[0].className = 'nvtooltip-pending-removal';
+ }
+
+ setTimeout(function() {
+
+ while (purging.length) {
+ var removeMe = purging.pop();
+ removeMe.parentNode.removeChild(removeMe);
+ }
+ }, 500);
+ };
+
+})();
+
+
+/*
+Gets the browser window size
+
+Returns object with height and width properties
+ */
+nv.utils.windowSize = function() {
+ // Sane defaults
+ var size = {width: 640, height: 480};
+
+ // Earlier IE uses Doc.body
+ if (document.body && document.body.offsetWidth) {
+ size.width = document.body.offsetWidth;
+ size.height = document.body.offsetHeight;
+ }
+
+ // IE can use depending on mode it is in
+ if (document.compatMode=='CSS1Compat' &&
+ document.documentElement &&
+ document.documentElement.offsetWidth ) {
+
+ size.width = document.documentElement.offsetWidth;
+ size.height = document.documentElement.offsetHeight;
+ }
+
+ // Most recent browsers use
+ if (window.innerWidth && window.innerHeight) {
+ size.width = window.innerWidth;
+ size.height = window.innerHeight;
+ }
+ return (size);
+};
+
+
+/*
+Binds callback function to run when window is resized
+ */
+nv.utils.windowResize = function(handler) {
+ if (window.addEventListener) {
+ window.addEventListener('resize', handler);
+ } else {
+ nv.log("ERROR: Failed to bind to window.resize with: ", handler);
+ }
+ // return object with clear function to remove the single added callback.
+ return {
+ callback: handler,
+ clear: function() {
+ window.removeEventListener('resize', handler);
+ }
+ }
+};
+
+
+/*
+Backwards compatible way to implement more d3-like coloring of graphs.
+If passed an array, wrap it in a function which implements the old behavior
+Else return what was passed in
+*/
+nv.utils.getColor = function(color) {
+ //if you pass in nothing, get default colors back
+ if (!arguments.length) {
+ return nv.utils.defaultColor();
+
+ //if passed an array, wrap it in a function
+ } else if(color instanceof Array) {
+ return function(d, i) { return d.color || color[i % color.length]; };
+
+ //if passed a function, return the function, or whatever it may be
+ //external libs, such as angularjs-nvd3-directives use this
+ } else {
+ //can't really help it if someone passes rubbish as color
+ return color;
+ }
+};
+
+
+/*
+Default color chooser uses the index of an object as before.
+ */
+nv.utils.defaultColor = function() {
+ var colors = d3.scale.category20().range();
+ return function(d, i) {
+ return d.color || colors[i % colors.length]
+ };
+};
+
+
+/*
+Returns a color function that takes the result of 'getKey' for each series and
+looks for a corresponding color from the dictionary
+*/
+nv.utils.customTheme = function(dictionary, getKey, defaultColors) {
+ // use default series.key if getKey is undefined
+ getKey = getKey || function(series) { return series.key };
+ defaultColors = defaultColors || d3.scale.category20().range();
+
+ // start at end of default color list and walk back to index 0
+ var defIndex = defaultColors.length;
+
+ return function(series, index) {
+ var key = getKey(series);
+ if (typeof dictionary[key] === 'function') {
+ return dictionary[key]();
+ } else if (dictionary[key] !== undefined) {
+ return dictionary[key];
+ } else {
+ // no match in dictionary, use a default color
+ if (!defIndex) {
+ // used all the default colors, start over
+ defIndex = defaultColors.length;
+ }
+ defIndex = defIndex - 1;
+ return defaultColors[defIndex];
+ }
+ };
+};
+
+
+/*
+From the PJAX example on d3js.org, while this is not really directly needed
+it's a very cool method for doing pjax, I may expand upon it a little bit,
+open to suggestions on anything that may be useful
+*/
+nv.utils.pjax = function(links, content) {
+
+ var load = function(href) {
+ d3.html(href, function(fragment) {
+ var target = d3.select(content).node();
+ target.parentNode.replaceChild(
+ d3.select(fragment).select(content).node(),
+ target);
+ nv.utils.pjax(links, content);
+ });
+ };
+
+ d3.selectAll(links).on("click", function() {
+ history.pushState(this.href, this.textContent, this.href);
+ load(this.href);
+ d3.event.preventDefault();
+ });
+
+ d3.select(window).on("popstate", function() {
+ if (d3.event.state) {
+ load(d3.event.state);
+ }
+ });
+};
+
+
+/*
+For when we want to approximate the width in pixels for an SVG:text element.
+Most common instance is when the element is in a display:none; container.
+Forumla is : text.length * font-size * constant_factor
+*/
+nv.utils.calcApproxTextWidth = function (svgTextElem) {
+ if (typeof svgTextElem.style === 'function'
+ && typeof svgTextElem.text === 'function') {
+
+ var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""));
+ var textLength = svgTextElem.text().length;
+ return textLength * fontSize * 0.5;
+ }
+ return 0;
+};
+
+
+/*
+Numbers that are undefined, null or NaN, convert them to zeros.
+*/
+nv.utils.NaNtoZero = function(n) {
+ if (typeof n !== 'number'
+ || isNaN(n)
+ || n === null
+ || n === Infinity
+ || n === -Infinity) {
+
+ return 0;
+ }
+ return n;
+};
+
+/*
+Add a way to watch for d3 transition ends to d3
+*/
+d3.selection.prototype.watchTransition = function(renderWatch){
+ var args = [this].concat([].slice.call(arguments, 1));
+ return renderWatch.transition.apply(renderWatch, args);
+};
+
+
+/*
+Helper object to watch when d3 has rendered something
+*/
+nv.utils.renderWatch = function(dispatch, duration) {
+ if (!(this instanceof nv.utils.renderWatch)) {
+ return new nv.utils.renderWatch(dispatch, duration);
+ }
+
+ var _duration = duration !== undefined ? duration : 250;
+ var renderStack = [];
+ var self = this;
+
+ this.models = function(models) {
+ models = [].slice.call(arguments, 0);
+ models.forEach(function(model){
+ model.__rendered = false;
+ (function(m){
+ m.dispatch.on('renderEnd', function(arg){
+ m.__rendered = true;
+ self.renderEnd('model');
+ });
+ })(model);
+
+ if (renderStack.indexOf(model) < 0) {
+ renderStack.push(model);
+ }
+ });
+ return this;
+ };
+
+ this.reset = function(duration) {
+ if (duration !== undefined) {
+ _duration = duration;
+ }
+ renderStack = [];
+ };
+
+ this.transition = function(selection, args, duration) {
+ args = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
+
+ if (args.length > 1) {
+ duration = args.pop();
+ } else {
+ duration = _duration !== undefined ? _duration : 250;
+ }
+ selection.__rendered = false;
+
+ if (renderStack.indexOf(selection) < 0) {
+ renderStack.push(selection);
+ }
+
+ if (duration === 0) {
+ selection.__rendered = true;
+ selection.delay = function() { return this; };
+ selection.duration = function() { return this; };
+ return selection;
+ } else {
+ if (selection.length === 0) {
+ selection.__rendered = true;
+ } else if (selection.every( function(d){ return !d.length; } )) {
+ selection.__rendered = true;
+ } else {
+ selection.__rendered = false;
+ }
+
+ var n = 0;
+ return selection
+ .transition()
+ .duration(duration)
+ .each(function(){ ++n; })
+ .each('end', function(d, i) {
+ if (--n === 0) {
+ selection.__rendered = true;
+ self.renderEnd.apply(this, args);
+ }
+ });
+ }
+ };
+
+ this.renderEnd = function() {
+ if (renderStack.every( function(d){ return d.__rendered; } )) {
+ renderStack.forEach( function(d){ d.__rendered = false; });
+ dispatch.renderEnd.apply(this, arguments);
+ }
+ }
+
+};
+
+
+/*
+Takes multiple objects and combines them into the first one (dst)
+example: nv.utils.deepExtend({a: 1}, {a: 2, b: 3}, {c: 4});
+gives: {a: 2, b: 3, c: 4}
+*/
+nv.utils.deepExtend = function(dst){
+ var sources = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
+ sources.forEach(function(source) {
+ for (key in source) {
+ var isArray = dst[key] instanceof Array;
+ var isObject = typeof dst[key] === 'object';
+ var srcObj = typeof source[key] === 'object';
+
+ if (isObject && !isArray && srcObj) {
+ nv.utils.deepExtend(dst[key], source[key]);
+ } else {
+ dst[key] = source[key];
+ }
+ }
+ });
+};
+
+
+/*
+state utility object, used to track d3 states in the models
+*/
+nv.utils.state = function(){
+ if (!(this instanceof nv.utils.state)) {
+ return new nv.utils.state();
+ }
+ var state = {};
+ var _self = this;
+ var _setState = function(){};
+ var _getState = function(){ return {}; };
+ var init = null;
+ var changed = null;
+
+ this.dispatch = d3.dispatch('change', 'set');
+
+ this.dispatch.on('set', function(state){
+ _setState(state, true);
+ });
+
+ this.getter = function(fn){
+ _getState = fn;
+ return this;
+ };
+
+ this.setter = function(fn, callback) {
+ if (!callback) {
+ callback = function(){};
+ }
+ _setState = function(state, update){
+ fn(state);
+ if (update) {
+ callback();
+ }
+ };
+ return this;
+ };
+
+ this.init = function(state){
+ init = init || {};
+ nv.utils.deepExtend(init, state);
+ };
+
+ var _set = function(){
+ var settings = _getState();
+
+ if (JSON.stringify(settings) === JSON.stringify(state)) {
+ return false;
+ }
+
+ for (var key in settings) {
+ if (state[key] === undefined) {
+ state[key] = {};
+ }
+ state[key] = settings[key];
+ changed = true;
+ }
+ return true;
+ };
+
+ this.update = function(){
+ if (init) {
+ _setState(init, false);
+ init = null;
+ }
+ if (_set.call(this)) {
+ this.dispatch.change(state);
+ }
+ };
+
+};
+
+
+/*
+Snippet of code you can insert into each nv.models.* to give you the ability to
+do things like:
+chart.options({
+ showXAxis: true,
+ tooltips: true
+});
+
+To enable in the chart:
+chart.options = nv.utils.optionsFunc.bind(chart);
+*/
+nv.utils.optionsFunc = function(args) {
+ nv.deprecated('nv.utils.optionsFunc');
+ if (args) {
+ d3.map(args).forEach((function(key,value) {
+ if (typeof this[key] === "function") {
+ this[key](value);
+ }
+ }).bind(this));
+ }
+ return this;
+};
+
+
+/*
+numTicks: requested number of ticks
+data: the chart data
+
+returns the number of ticks to actually use on X axis, based on chart data
+to avoid duplicate ticks with the same value
+*/
+nv.utils.calcTicksX = function(numTicks, data) {
+ // find max number of values from all data streams
+ var numValues = 1;
+ var i = 0;
+ for (i; i < data.length; i += 1) {
+ var stream_len = data[i] && data[i].values ? data[i].values.length : 0;
+ numValues = stream_len > numValues ? stream_len : numValues;
+ }
+ nv.log("Requested number of ticks: ", numTicks);
+ nv.log("Calculated max values to be: ", numValues);
+ // make sure we don't have more ticks than values to avoid duplicates
+ numTicks = numTicks > numValues ? numTicks = numValues - 1 : numTicks;
+ // make sure we have at least one tick
+ numTicks = numTicks < 1 ? 1 : numTicks;
+ // make sure it's an integer
+ numTicks = Math.floor(numTicks);
+ nv.log("Calculating tick count as: ", numTicks);
+ return numTicks;
+};
+
+
+/*
+returns number of ticks to actually use on Y axis, based on chart data
+*/
+nv.utils.calcTicksY = function(numTicks, data) {
+ // currently uses the same logic but we can adjust here if needed later
+ return nv.utils.calcTicksX(numTicks, data);
+};
+
+
+/*
+Add a particular option from an options object onto chart
+Options exposed on a chart are a getter/setter function that returns chart
+on set to mimic typical d3 option chaining, e.g. svg.option1('a').option2('b');
+
+option objects should be generated via Object.create() to provide
+the option of manipulating data via get/set functions.
+*/
+nv.utils.initOption = function(chart, name) {
+ // if it's a call option, just call it directly, otherwise do get/set
+ if (chart._calls && chart._calls[name]) {
+ chart[name] = chart._calls[name];
+ } else {
+ chart[name] = function (_) {
+ if (!arguments.length) return chart._options[name];
+ chart._options[name] = _;
+ return chart;
+ };
+ }
+};
+
+
+/*
+Add all options in an options object to the chart
+*/
+nv.utils.initOptions = function(chart) {
+ var ops = Object.getOwnPropertyNames(chart._options || {});
+ var calls = Object.getOwnPropertyNames(chart._calls || {});
+ ops = ops.concat(calls);
+ for (var i in ops) {
+ nv.utils.initOption(chart, ops[i]);
+ }
+};
+
+
+/*
+Inherit options from a D3 object
+d3.rebind makes calling the function on target actually call it on source
+Also use _d3options so we can track what we inherit for documentation and chained inheritance
+*/
+nv.utils.inheritOptionsD3 = function(target, d3_source, oplist) {
+ target._d3options = oplist.concat(target._d3options || []);
+ oplist.unshift(d3_source);
+ oplist.unshift(target);
+ d3.rebind.apply(this, oplist);
+};
+
+
+/*
+Remove duplicates from an array
+*/
+nv.utils.arrayUnique = function(a) {
+ return a.sort().filter(function(item, pos) {
+ return !pos || item != a[pos - 1];
+ });
+};
+
+
+/*
+Keeps a list of custom symbols to draw from in addition to d3.svg.symbol
+Necessary since d3 doesn't let you extend its list -_-
+Add new symbols by doing nv.utils.symbols.set('name', function(size){...});
+*/
+nv.utils.symbolMap = d3.map();
+
+
+/*
+Replaces d3.svg.symbol so that we can look both there and our own map
+ */
+nv.utils.symbol = function() {
+ var type,
+ size = 64;
+ function symbol(d,i) {
+ var t = type.call(this,d,i);
+ var s = size.call(this,d,i);
+ if (d3.svg.symbolTypes.indexOf(t) !== -1) {
+ return d3.svg.symbol().type(t).size(s)();
+ } else {
+ return nv.utils.symbolMap.get(t)(s);
+ }
+ }
+ symbol.type = function(_) {
+ if (!arguments.length) return type;
+ type = d3.functor(_);
+ return symbol;
+ };
+ symbol.size = function(_) {
+ if (!arguments.length) return size;
+ size = d3.functor(_);
+ return symbol;
+ };
+ return symbol;
+};
+
+
+/*
+Inherit option getter/setter functions from source to target
+d3.rebind makes calling the function on target actually call it on source
+Also track via _inherited and _d3options so we can track what we inherit
+for documentation generation purposes and chained inheritance
+*/
+nv.utils.inheritOptions = function(target, source) {
+ // inherit all the things
+ var ops = Object.getOwnPropertyNames(source._options || {});
+ var calls = Object.getOwnPropertyNames(source._calls || {});
+ var inherited = source._inherited || [];
+ var d3ops = source._d3options || [];
+ var args = ops.concat(calls).concat(inherited).concat(d3ops);
+ args.unshift(source);
+ args.unshift(target);
+ d3.rebind.apply(this, args);
+ // pass along the lists to keep track of them, don't allow duplicates
+ target._inherited = nv.utils.arrayUnique(ops.concat(calls).concat(inherited).concat(ops).concat(target._inherited || []));
+ target._d3options = nv.utils.arrayUnique(d3ops.concat(target._d3options || []));
+};
+
+
+/*
+Runs common initialize code on the svg before the chart builds
+*/
+nv.utils.initSVG = function(svg) {
+ svg.classed({'nvd3-svg':true});
+};nv.models.axis = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var axis = d3.svg.axis();
+ var scale = d3.scale.linear();
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 75 //only used for tickLabel currently
+ , height = 60 //only used for tickLabel currently
+ , axisLabelText = null
+ , showMaxMin = true //TODO: showMaxMin should be disabled on all ordinal scaled axes
+ , highlightZero = true
+ , rotateLabels = 0
+ , rotateYLabel = true
+ , staggerLabels = false
+ , isOrdinal = false
+ , ticks = null
+ , axisLabelDistance = 0
+ , duration = 250
+ , dispatch = d3.dispatch('renderEnd')
+ , axisRendered = false
+ , maxMinRendered = false
+ ;
+ axis
+ .scale(scale)
+ .orient('bottom')
+ .tickFormat(function(d) { return d })
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var scale0;
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-axis').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-axis');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g')
+
+ if (ticks !== null)
+ axis.ticks(ticks);
+ else if (axis.orient() == 'top' || axis.orient() == 'bottom')
+ axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
+
+ //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
+ g.watchTransition(renderWatch, 'axis').call(axis);
+
+ scale0 = scale0 || axis.scale();
+
+ var fmt = axis.tickFormat();
+ if (fmt == null) {
+ fmt = scale0.tickFormat();
+ }
+
+ var axisLabel = g.selectAll('text.nv-axislabel')
+ .data([axisLabelText || null]);
+ axisLabel.exit().remove();
+
+ switch (axis.orient()) {
+ case 'top':
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ var w;
+ if (scale.range().length < 2) {
+ w = 0;
+ } else if (scale.range().length === 2) {
+ w = scale.range()[1];
+ } else {
+ w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]);
+ }
+ axisLabel
+ .attr('text-anchor', 'middle')
+ .attr('y', 0)
+ .attr('x', w/2);
+ if (showMaxMin) {
+ var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
+ .data(scale.domain());
+ axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
+ axisMaxMin.exit().remove();
+ axisMaxMin
+ .attr('transform', function(d,i) {
+ return 'translate(' + nv.utils.NaNtoZero(scale(d)) + ',0)'
+ })
+ .select('text')
+ .attr('dy', '-0.5em')
+ .attr('y', -axis.tickPadding())
+ .attr('text-anchor', 'middle')
+ .text(function(d,i) {
+ var v = fmt(d);
+ return ('' + v).match('NaN') ? '' : v;
+ });
+ axisMaxMin.watchTransition(renderWatch, 'min-max top')
+ .attr('transform', function(d,i) {
+ return 'translate(' + nv.utils.NaNtoZero(scale.range()[i]) + ',0)'
+ });
+ }
+ break;
+ case 'bottom':
+ var xLabelMargin = axisLabelDistance + 36;
+ var maxTextWidth = 30;
+ var xTicks = g.selectAll('g').select("text");
+ if (rotateLabels%360) {
+ //Calculate the longest xTick width
+ xTicks.each(function(d,i){
+ var width = this.getBoundingClientRect().width;
+ if(width > maxTextWidth) maxTextWidth = width;
+ });
+ //Convert to radians before calculating sin. Add 30 to margin for healthy padding.
+ var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180));
+ var xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30;
+ //Rotate all xTicks
+ xTicks
+ .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
+ .style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
+ }
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ var w;
+ if (scale.range().length < 2) {
+ w = 0;
+ } else if (scale.range().length === 2) {
+ w = scale.range()[1];
+ } else {
+ w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]);
+ }
+ axisLabel
+ .attr('text-anchor', 'middle')
+ .attr('y', xLabelMargin)
+ .attr('x', w/2);
+ if (showMaxMin) {
+ //if (showMaxMin && !isOrdinal) {
+ var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
+ //.data(scale.domain())
+ .data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]);
+ axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
+ axisMaxMin.exit().remove();
+ axisMaxMin
+ .attr('transform', function(d,i) {
+ return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)'
+ })
+ .select('text')
+ .attr('dy', '.71em')
+ .attr('y', axis.tickPadding())
+ .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
+ .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
+ .text(function(d,i) {
+ var v = fmt(d);
+ return ('' + v).match('NaN') ? '' : v;
+ });
+ axisMaxMin.watchTransition(renderWatch, 'min-max bottom')
+ .attr('transform', function(d,i) {
+ return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)'
+ });
+ }
+ if (staggerLabels)
+ xTicks
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + (i % 2 == 0 ? '0' : '12') + ')'
+ });
+
+ break;
+ case 'right':
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ axisLabel
+ .style('text-anchor', rotateYLabel ? 'middle' : 'begin')
+ .attr('transform', rotateYLabel ? 'rotate(90)' : '')
+ .attr('y', rotateYLabel ? (-Math.max(margin.right,width) + 12) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
+ .attr('x', rotateYLabel ? (scale.range()[0] / 2) : axis.tickPadding());
+ if (showMaxMin) {
+ var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
+ .data(scale.domain());
+ axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text')
+ .style('opacity', 0);
+ axisMaxMin.exit().remove();
+ axisMaxMin
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + nv.utils.NaNtoZero(scale(d)) + ')'
+ })
+ .select('text')
+ .attr('dy', '.32em')
+ .attr('y', 0)
+ .attr('x', axis.tickPadding())
+ .style('text-anchor', 'start')
+ .text(function(d,i) {
+ var v = fmt(d);
+ return ('' + v).match('NaN') ? '' : v;
+ });
+ axisMaxMin.watchTransition(renderWatch, 'min-max right')
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')'
+ })
+ .select('text')
+ .style('opacity', 1);
+ }
+ break;
+ case 'left':
+ /*
+ //For dynamically placing the label. Can be used with dynamically-sized chart axis margins
+ var yTicks = g.selectAll('g').select("text");
+ yTicks.each(function(d,i){
+ var labelPadding = this.getBoundingClientRect().width + axis.tickPadding() + 16;
+ if(labelPadding > width) width = labelPadding;
+ });
+ */
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ axisLabel
+ .style('text-anchor', rotateYLabel ? 'middle' : 'end')
+ .attr('transform', rotateYLabel ? 'rotate(-90)' : '')
+ .attr('y', rotateYLabel ? (-Math.max(margin.left,width) + 25 - (axisLabelDistance || 0)) : -10)
+ .attr('x', rotateYLabel ? (-scale.range()[0] / 2) : -axis.tickPadding());
+ if (showMaxMin) {
+ var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
+ .data(scale.domain());
+ axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text')
+ .style('opacity', 0);
+ axisMaxMin.exit().remove();
+ axisMaxMin
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + nv.utils.NaNtoZero(scale0(d)) + ')'
+ })
+ .select('text')
+ .attr('dy', '.32em')
+ .attr('y', 0)
+ .attr('x', -axis.tickPadding())
+ .attr('text-anchor', 'end')
+ .text(function(d,i) {
+ var v = fmt(d);
+ return ('' + v).match('NaN') ? '' : v;
+ });
+ axisMaxMin.watchTransition(renderWatch, 'min-max right')
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')'
+ })
+ .select('text')
+ .style('opacity', 1);
+ }
+ break;
+ }
+ axisLabel.text(function(d) { return d });
+
+ if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) {
+ //check if max and min overlap other values, if so, hide the values that overlap
+ g.selectAll('g') // the g's wrapping each tick
+ .each(function(d,i) {
+ d3.select(this).select('text').attr('opacity', 1);
+ if (scale(d) < scale.range()[1] + 10 || scale(d) > scale.range()[0] - 10) { // 10 is assuming text height is 16... if d is 0, leave it!
+ if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
+ d3.select(this).attr('opacity', 0);
+
+ d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!!
+ }
+ });
+
+ //if Max and Min = 0 only show min, Issue #281
+ if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0) {
+ wrap.selectAll('g.nv-axisMaxMin').style('opacity', function (d, i) {
+ return !i ? 1 : 0
+ });
+ }
+ }
+
+ if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
+ var maxMinRange = [];
+ wrap.selectAll('g.nv-axisMaxMin')
+ .each(function(d,i) {
+ try {
+ if (i) // i== 1, max position
+ maxMinRange.push(scale(d) - this.getBoundingClientRect().width - 4) //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
+ else // i==0, min position
+ maxMinRange.push(scale(d) + this.getBoundingClientRect().width + 4)
+ }catch (err) {
+ if (i) // i== 1, max position
+ maxMinRange.push(scale(d) - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
+ else // i==0, min position
+ maxMinRange.push(scale(d) + 4);
+ }
+ });
+ // the g's wrapping each tick
+ g.selectAll('g').each(function(d,i) {
+ if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) {
+ if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
+ d3.select(this).remove();
+ else
+ d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
+ }
+ });
+ }
+
+ //highlight zero line ... Maybe should not be an option and should just be in CSS?
+ if (highlightZero) {
+ g.selectAll('.tick')
+ .filter(function (d) {
+ return !parseFloat(Math.round(this.__data__ * 100000) / 1000000) && (this.__data__ !== undefined)
+ }) //this is because sometimes the 0 tick is a very small fraction, TODO: think of cleaner technique
+ .classed('zero', true);
+ }
+ //store old scales for use in transitions on update
+ scale0 = scale.copy();
+
+ });
+
+ renderWatch.renderEnd('axis immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.axis = axis;
+ chart.dispatch = dispatch;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ axisLabelDistance: {get: function(){return axisLabelDistance;}, set: function(_){axisLabelDistance=_;}},
+ staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
+ rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
+ rotateYLabel: {get: function(){return rotateYLabel;}, set: function(_){rotateYLabel=_;}},
+ highlightZero: {get: function(){return highlightZero;}, set: function(_){highlightZero=_;}},
+ showMaxMin: {get: function(){return showMaxMin;}, set: function(_){showMaxMin=_;}},
+ axisLabel: {get: function(){return axisLabelText;}, set: function(_){axisLabelText=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}},
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration=_;
+ renderWatch.reset(duration);
+ }},
+ scale: {get: function(){return scale;}, set: function(_){
+ scale = _;
+ axis.scale(scale);
+ isOrdinal = typeof scale.rangeBands === 'function';
+ nv.utils.inheritOptionsD3(chart, scale, ['domain', 'range', 'rangeBand', 'rangeBands']);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+ nv.utils.inheritOptionsD3(chart, axis, ['orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat']);
+ nv.utils.inheritOptionsD3(chart, scale, ['domain', 'range', 'rangeBand', 'rangeBands']);
+
+ return chart;
+};
+
+// Chart design based on the recommendations of Stephen Few. Implementation
+// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
+// http://projects.instantcognition.com/protovis/bulletchart/
+
+nv.models.bullet = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , orient = 'left' // TODO top & bottom
+ , reverse = false
+ , ranges = function(d) { return d.ranges }
+ , markers = function(d) { return d.markers ? d.markers : [0] }
+ , measures = function(d) { return d.measures }
+ , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] }
+ , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : [] }
+ , measureLabels = function(d) { return d.measureLabels ? d.measureLabels : [] }
+ , forceX = [0] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
+ , width = 380
+ , height = 30
+ , tickFormat = null
+ , color = nv.utils.getColor(['#1f77b4'])
+ , dispatch = d3.dispatch('elementMouseover', 'elementMouseout')
+ ;
+
+ function chart(selection) {
+ selection.each(function(d, i) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
+ markerz = markers.call(this, d, i).slice().sort(d3.descending),
+ measurez = measures.call(this, d, i).slice().sort(d3.descending),
+ rangeLabelz = rangeLabels.call(this, d, i).slice(),
+ markerLabelz = markerLabels.call(this, d, i).slice(),
+ measureLabelz = measureLabels.call(this, d, i).slice();
+
+ // Setup Scales
+ // Compute the new x-scale.
+ var x1 = d3.scale.linear()
+ .domain( d3.extent(d3.merge([forceX, rangez])) )
+ .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
+
+ // Retrieve the old x-scale, if this is an update.
+ var x0 = this.__chart__ || d3.scale.linear()
+ .domain([0, Infinity])
+ .range(x1.range());
+
+ // Stash the new scale.
+ this.__chart__ = x1;
+
+ var rangeMin = d3.min(rangez), //rangez[2]
+ rangeMax = d3.max(rangez), //rangez[0]
+ rangeAvg = rangez[1];
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-bullet').data([d]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bullet');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('rect').attr('class', 'nv-range nv-rangeMax');
+ gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg');
+ gEnter.append('rect').attr('class', 'nv-range nv-rangeMin');
+ gEnter.append('rect').attr('class', 'nv-measure');
+ gEnter.append('path').attr('class', 'nv-markerTriangle');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
+ w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
+ var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
+ xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
+
+ g.select('rect.nv-rangeMax')
+ .attr('height', availableHeight)
+ .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin))
+ .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin))
+ .datum(rangeMax > 0 ? rangeMax : rangeMin)
+
+ g.select('rect.nv-rangeAvg')
+ .attr('height', availableHeight)
+ .attr('width', w1(rangeAvg))
+ .attr('x', xp1(rangeAvg))
+ .datum(rangeAvg)
+
+ g.select('rect.nv-rangeMin')
+ .attr('height', availableHeight)
+ .attr('width', w1(rangeMax))
+ .attr('x', xp1(rangeMax))
+ .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax))
+ .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax))
+ .datum(rangeMax > 0 ? rangeMin : rangeMax)
+
+ g.select('rect.nv-measure')
+ .style('fill', color)
+ .attr('height', availableHeight / 3)
+ .attr('y', availableHeight / 3)
+ .attr('width', measurez < 0 ?
+ x1(0) - x1(measurez[0])
+ : x1(measurez[0]) - x1(0))
+ .attr('x', xp1(measurez))
+ .on('mouseover', function() {
+ dispatch.elementMouseover({
+ value: measurez[0],
+ label: measureLabelz[0] || 'Current',
+ pos: [x1(measurez[0]), availableHeight/2]
+ })
+ })
+ .on('mouseout', function() {
+ dispatch.elementMouseout({
+ value: measurez[0],
+ label: measureLabelz[0] || 'Current'
+ })
+ });
+
+ var h3 = availableHeight / 6;
+ if (markerz[0]) {
+ g.selectAll('path.nv-markerTriangle')
+ .attr('transform', function(d) { return 'translate(' + x1(markerz[0]) + ',' + (availableHeight / 2) + ')' })
+ .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
+ .on('mouseover', function() {
+ dispatch.elementMouseover({
+ value: markerz[0],
+ label: markerLabelz[0] || 'Previous',
+ pos: [x1(markerz[0]), availableHeight/2]
+ })
+ })
+ .on('mouseout', function() {
+ dispatch.elementMouseout({
+ value: markerz[0],
+ label: markerLabelz[0] || 'Previous'
+ })
+ });
+ } else {
+ g.selectAll('path.nv-markerTriangle').remove();
+ }
+
+ wrap.selectAll('.nv-range')
+ .on('mouseover', function(d,i) {
+ var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
+
+ dispatch.elementMouseover({
+ value: d,
+ label: label,
+ pos: [x1(d), availableHeight/2]
+ })
+ })
+ .on('mouseout', function(d,i) {
+ var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
+
+ dispatch.elementMouseout({
+ value: d,
+ label: label
+ })
+ });
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ ranges: {get: function(){return ranges;}, set: function(_){ranges=_;}}, // ranges (bad, satisfactory, good)
+ markers: {get: function(){return markers;}, set: function(_){markers=_;}}, // markers (previous, goal)
+ measures: {get: function(){return measures;}, set: function(_){measures=_;}}, // measures (actual, forecast)
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ orient: {get: function(){return orient;}, set: function(_){ // left, right, top, bottom
+ orient = _;
+ reverse = orient == 'right' || orient == 'bottom';
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+ return chart;
+};
+
+
+
+// Chart design based on the recommendations of Stephen Few. Implementation
+// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
+// http://projects.instantcognition.com/protovis/bulletchart/
+nv.models.bulletChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var bullet = nv.models.bullet()
+ ;
+
+ var orient = 'left' // TODO top & bottom
+ , reverse = false
+ , margin = {top: 5, right: 40, bottom: 20, left: 120}
+ , ranges = function(d) { return d.ranges }
+ , markers = function(d) { return d.markers ? d.markers : [0] }
+ , measures = function(d) { return d.measures }
+ , width = null
+ , height = 55
+ , tickFormat = null
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + x + ' ' +
+ '
' + y + '
'
+ }
+ , noData = 'No Data Available.'
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ) + margin.left,
+ top = e.pos[1] + ( offsetElement.offsetTop || 0) + margin.top,
+ content = tooltip(e.key, e.label, e.value, e, chart);
+
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
+ };
+
+ function chart(selection) {
+ selection.each(function(d, i) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom,
+ that = this;
+
+ chart.update = function() { chart(selection) };
+ chart.container = this;
+
+ // Display No Data message if there's nothing to show.
+ if (!d || !ranges.call(this, d, i)) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', 18 + margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
+ markerz = markers.call(this, d, i).slice().sort(d3.descending),
+ measurez = measures.call(this, d, i).slice().sort(d3.descending);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-bulletChart').data([d]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bulletChart');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-bulletWrap');
+ gEnter.append('g').attr('class', 'nv-titles');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ // Compute the new x-scale.
+ var x1 = d3.scale.linear()
+ .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) // TODO: need to allow forceX and forceY, and xDomain, yDomain
+ .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
+
+ // Retrieve the old x-scale, if this is an update.
+ var x0 = this.__chart__ || d3.scale.linear()
+ .domain([0, Infinity])
+ .range(x1.range());
+
+ // Stash the new scale.
+ this.__chart__ = x1;
+
+ var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
+ w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
+
+ var title = gEnter.select('.nv-titles').append('g')
+ .attr('text-anchor', 'end')
+ .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')');
+ title.append('text')
+ .attr('class', 'nv-title')
+ .text(function(d) { return d.title; });
+
+ title.append('text')
+ .attr('class', 'nv-subtitle')
+ .attr('dy', '1em')
+ .text(function(d) { return d.subtitle; });
+
+ bullet
+ .width(availableWidth)
+ .height(availableHeight)
+
+ var bulletWrap = g.select('.nv-bulletWrap');
+ d3.transition(bulletWrap).call(bullet);
+
+ // Compute the tick format.
+ var format = tickFormat || x1.tickFormat( availableWidth / 100 );
+
+ // Update the tick groups.
+ var tick = g.selectAll('g.nv-tick')
+ .data(x1.ticks( availableWidth / 50 ), function(d) {
+ return this.textContent || format(d);
+ });
+
+ // Initialize the ticks with the old scale, x0.
+ var tickEnter = tick.enter().append('g')
+ .attr('class', 'nv-tick')
+ .attr('transform', function(d) { return 'translate(' + x0(d) + ',0)' })
+ .style('opacity', 1e-6);
+
+ tickEnter.append('line')
+ .attr('y1', availableHeight)
+ .attr('y2', availableHeight * 7 / 6);
+
+ tickEnter.append('text')
+ .attr('text-anchor', 'middle')
+ .attr('dy', '1em')
+ .attr('y', availableHeight * 7 / 6)
+ .text(format);
+
+ // Transition the updating ticks to the new scale, x1.
+ var tickUpdate = d3.transition(tick)
+ .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
+ .style('opacity', 1);
+
+ tickUpdate.select('line')
+ .attr('y1', availableHeight)
+ .attr('y2', availableHeight * 7 / 6);
+
+ tickUpdate.select('text')
+ .attr('y', availableHeight * 7 / 6);
+
+ // Transition the exiting ticks to the new scale, x1.
+ d3.transition(tick.exit())
+ .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
+ .style('opacity', 1e-6)
+ .remove();
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ dispatch.on('tooltipShow', function(e) {
+ e.key = d.title;
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ });
+
+ d3.timer.flush();
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ bullet.dispatch.on('elementMouseover.tooltip', function(e) {
+ dispatch.tooltipShow(e);
+ });
+
+ bullet.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.bullet = bullet;
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ ranges: {get: function(){return ranges;}, set: function(_){ranges=_;}}, // ranges (bad, satisfactory, good)
+ markers: {get: function(){return markers;}, set: function(_){markers=_;}}, // markers (previous, goal)
+ measures: {get: function(){return measures;}, set: function(_){measures=_;}}, // measures (actual, forecast)
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ orient: {get: function(){return orient;}, set: function(_){ // left, right, top, bottom
+ orient = _;
+ reverse = orient == 'right' || orient == 'bottom';
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, bullet);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+
+
+nv.models.cumulativeLineChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var lines = nv.models.line()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , controls = nv.models.legend()
+ , interactiveLayer = nv.interactiveGuideline()
+ ;
+
+ var margin = {top: 30, right: 30, bottom: 50, left: 60}
+ , color = nv.utils.defaultColor()
+ , width = null
+ , height = null
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , tooltips = true
+ , showControls = true
+ , useInteractiveGuideline = false
+ , rescaleY = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' ' +
+ '
' + y + ' at ' + x + '
'
+ }
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , id = lines.id()
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = 'No Data Available.'
+ , average = function(d) { return d.average }
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
+ , transitionDuration = 250
+ , duration = 250
+ , noErrorCheck = false //if set to TRUE, will bypass an error check in the indexify function.
+ ;
+
+ state.index = 0;
+ state.rescaleY = rescaleY;
+
+ xAxis
+ .orient('bottom')
+ .tickPadding(7)
+ ;
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ ;
+
+ controls.updateState(false);
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var dx = d3.scale.linear()
+ , index = {i: 0, x: 0}
+ , renderWatch = nv.utils.renderWatch(dispatch, duration)
+ ;
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
+ y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, null, null, offsetElement);
+ };
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled }),
+ index: index.i,
+ rescaleY: rescaleY
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.index !== undefined)
+ index.i = state.index;
+ if (state.rescaleY !== undefined)
+ rescaleY = state.rescaleY;
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(lines);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+ container.classed('nv-chart-' + id, true);
+ var that = this;
+
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ chart.update = function() {
+ if (duration === 0)
+ container.call(chart);
+ else
+ container.transition().duration(duration).call(chart)
+ };
+ chart.container = this;
+
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ var indexDrag = d3.behavior.drag()
+ .on('dragstart', dragStart)
+ .on('drag', dragMove)
+ .on('dragend', dragEnd);
+
+
+ function dragStart(d,i) {
+ d3.select(chart.container)
+ .style('cursor', 'ew-resize');
+ }
+
+ function dragMove(d,i) {
+ index.x = d3.event.x;
+ index.i = Math.round(dx.invert(index.x));
+ updateZero();
+ }
+
+ function dragEnd(d,i) {
+ d3.select(chart.container)
+ .style('cursor', 'auto');
+
+ // update state and send stateChange with new index
+ state.index = index.i;
+ dispatch.stateChange(state);
+ }
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ x = lines.xScale();
+ y = lines.yScale();
+
+ if (!rescaleY) {
+ var seriesDomains = data
+ .filter(function(series) { return !series.disabled })
+ .map(function(series,i) {
+ var initialDomain = d3.extent(series.values, lines.y());
+
+ //account for series being disabled when losing 95% or more
+ if (initialDomain[0] < -.95) initialDomain[0] = -.95;
+
+ return [
+ (initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]),
+ (initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0])
+ ];
+ });
+
+ var completeDomain = [
+ d3.min(seriesDomains, function(d) { return d[0] }),
+ d3.max(seriesDomains, function(d) { return d[1] })
+ ];
+
+ lines.yDomain(completeDomain);
+ } else {
+ lines.yDomain(null);
+ }
+
+ dx.domain([0, data[0].values.length - 1]) //Assumes all series have same length
+ .range([0, availableWidth])
+ .clamp(true);
+
+ var data = indexify(index.i, data);
+
+ // Setup containers and skeleton of chart
+ var interactivePointerEvents = (useInteractiveGuideline) ? "none" : "all";
+ var wrap = container.selectAll('g.nv-wrap.nv-cumulativeLine').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-cumulativeLine').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-interactive');
+ gEnter.append('g').attr('class', 'nv-x nv-axis').style("pointer-events","none");
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-background');
+ gEnter.append('g').attr('class', 'nv-linesWrap').style("pointer-events",interactivePointerEvents);
+ gEnter.append('g').attr('class', 'nv-avgLinesWrap').style("pointer-events","none");
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-controlsWrap');
+
+ // Legend
+ if (showLegend) {
+ legend.width(availableWidth);
+
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ }
+
+ // Controls
+ if (showControls) {
+ var controlsData = [
+ { key: 'Re-scale y-axis', disabled: !rescaleY }
+ ];
+
+ controls
+ .width(140)
+ .color(['#444', '#444', '#444'])
+ .rightAlign(false)
+ .margin({top: 5, right: 0, bottom: 5, left: 20})
+ ;
+
+ g.select('.nv-controlsWrap')
+ .datum(controlsData)
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ .call(controls);
+ }
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
+
+ // Show error if series goes below 100%
+ var tempDisabled = data.filter(function(d) { return d.tempDisabled });
+
+ wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates
+ if (tempDisabled.length) {
+ wrap.append('text').attr('class', 'tempDisabled')
+ .attr('x', availableWidth / 2)
+ .attr('y', '-.71em')
+ .style('text-anchor', 'end')
+ .text(tempDisabled.map(function(d) { return d.key }).join(', ') + ' values cannot be calculated for this time period.');
+ }
+
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left:margin.left,top:margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
+
+ gEnter.select('.nv-background')
+ .append('rect');
+
+ g.select('.nv-background rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
+
+ lines
+ //.x(function(d) { return d.x })
+ .y(function(d) { return d.display.y })
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && !data[i].tempDisabled; }));
+
+ var linesWrap = g.select('.nv-linesWrap')
+ .datum(data.filter(function(d) { return !d.disabled && !d.tempDisabled }));
+
+ linesWrap.call(lines);
+
+ //Store a series index number in the data array.
+ data.forEach(function(d,i) {
+ d.seriesIndex = i;
+ });
+
+ var avgLineData = data.filter(function(d) {
+ return !d.disabled && !!average(d);
+ });
+
+ var avgLines = g.select(".nv-avgLinesWrap").selectAll("line")
+ .data(avgLineData, function(d) { return d.key; });
+
+ var getAvgLineY = function(d) {
+ //If average lines go off the svg element, clamp them to the svg bounds.
+ var yVal = y(average(d));
+ if (yVal < 0) return 0;
+ if (yVal > availableHeight) return availableHeight;
+ return yVal;
+ };
+
+ avgLines.enter()
+ .append('line')
+ .style('stroke-width',2)
+ .style('stroke-dasharray','10,10')
+ .style('stroke',function (d,i) {
+ return lines.color()(d,d.seriesIndex);
+ })
+ .attr('x1',0)
+ .attr('x2',availableWidth)
+ .attr('y1', getAvgLineY)
+ .attr('y2', getAvgLineY);
+
+ avgLines
+ .style('stroke-opacity',function(d){
+ //If average lines go offscreen, make them transparent
+ var yVal = y(average(d));
+ if (yVal < 0 || yVal > availableHeight) return 0;
+ return 1;
+ })
+ .attr('x1',0)
+ .attr('x2',availableWidth)
+ .attr('y1', getAvgLineY)
+ .attr('y2', getAvgLineY);
+
+ avgLines.exit().remove();
+
+ //Create index line
+ var indexLine = linesWrap.selectAll('.nv-indexLine')
+ .data([index]);
+ indexLine.enter().append('rect').attr('class', 'nv-indexLine')
+ .attr('width', 3)
+ .attr('x', -2)
+ .attr('fill', 'red')
+ .attr('fill-opacity', .5)
+ .style("pointer-events","all")
+ .call(indexDrag);
+
+ indexLine
+ .attr('transform', function(d) { return 'translate(' + dx(d.i) + ',0)' })
+ .attr('height', availableHeight);
+
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ .ticks( nv.utils.calcTicksX(availableWidth/70, data) )
+ .tickSize(-availableHeight, 0);
+
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ g.select('.nv-x.nv-axis')
+ .call(xAxis);
+ }
+
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-y.nv-axis')
+ .call(yAxis);
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ function updateZero() {
+ indexLine
+ .data([index]);
+
+ //When dragging the index line, turn off line transitions.
+ // Then turn them back on when done dragging.
+ var oldDuration = chart.duration();
+ chart.duration(0);
+ chart.update();
+ chart.duration(oldDuration);
+ }
+
+ g.select('.nv-background rect')
+ .on('click', function() {
+ index.x = d3.mouse(this)[0];
+ index.i = Math.round(dx.invert(index.x));
+
+ // update state and send stateChange with new index
+ state.index = index.i;
+ dispatch.stateChange(state);
+
+ updateZero();
+ });
+
+ lines.dispatch.on('elementClick', function(e) {
+ index.i = e.pointIndex;
+ index.x = dx(index.i);
+
+ // update state and send stateChange with new index
+ state.index = index.i;
+ dispatch.stateChange(state);
+
+ updateZero();
+ });
+
+ controls.dispatch.on('legendClick', function(d,i) {
+ d.disabled = !d.disabled;
+ rescaleY = !d.disabled;
+
+ state.rescaleY = rescaleY;
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ lines.clearHighlights();
+ var singlePoint, pointIndex, pointXLocation, allData = [];
+
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ lines.highlightPoint(i, pointIndex, true);
+ var point = series.values[pointIndex];
+ if (typeof point === 'undefined') return;
+ if (typeof singlePoint === 'undefined') singlePoint = point;
+ if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ allData.push({
+ key: series.key,
+ value: chart.y()(point, pointIndex),
+ color: color(series,series.seriesIndex)
+ });
+ });
+
+ //Highlight the tooltip entry based on which point the mouse is closest to.
+ if (allData.length > 2) {
+ var yValue = chart.yScale().invert(e.mouseY);
+ var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
+ var threshold = 0.03 * domainExtent;
+ var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold);
+ if (indexToHighlight !== null)
+ allData[indexToHighlight].highlight = true;
+ }
+
+ var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex), pointIndex);
+ interactiveLayer.tooltip
+ .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
+ .chartContainer(that.parentNode)
+ .enabled(tooltips)
+ .valueFormatter(function(d,i) {
+ return yAxis.tickFormat()(d);
+ })
+ .data(
+ {
+ value: xValue,
+ series: allData
+ }
+ )();
+
+ interactiveLayer.renderGuideLine(pointXLocation);
+ });
+
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ dispatch.tooltipHide();
+ lines.clearHighlights();
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ if (typeof e.index !== 'undefined') {
+ index.i = e.index;
+ index.x = dx(index.i);
+
+ state.index = e.index;
+
+ indexLine
+ .data([index]);
+ }
+
+ if (typeof e.rescaleY !== 'undefined') {
+ rescaleY = e.rescaleY;
+ }
+
+ chart.update();
+ });
+
+ });
+
+ renderWatch.renderEnd('cumulativeLineChart immediate');
+
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ lines.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
+
+ var indexifyYGetter = null;
+ /* Normalize the data according to an index point. */
+ function indexify(idx, data) {
+ if (!indexifyYGetter) indexifyYGetter = lines.y();
+ return data.map(function(line, i) {
+ if (!line.values) {
+ return line;
+ }
+ var indexValue = line.values[idx];
+ if (indexValue == null) {
+ return line;
+ }
+ var v = indexifyYGetter(indexValue, idx);
+
+ //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
+ if (v < -.95 && !noErrorCheck) {
+ //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100)
+
+ line.tempDisabled = true;
+ return line;
+ }
+
+ line.tempDisabled = false;
+
+ line.values = line.values.map(function(point, pointIndex) {
+ point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / (1 + v) };
+ return point;
+ });
+
+ return line;
+ })
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.lines = lines;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.interactiveLayer = interactiveLayer;
+ chart.state = state;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ rescaleY: {get: function(){return rescaleY;}, set: function(_){rescaleY=_;}},
+ showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ average: {get: function(){return average;}, set: function(_){average=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ noErrorCheck: {get: function(){return noErrorCheck;}, set: function(_){noErrorCheck=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = _;
+ if (_ === true) {
+ chart.interactive(false);
+ chart.useVoronoi(false);
+ }
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ lines.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ renderWatch.reset(duration);
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, lines);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};//TODO: consider deprecating by adding necessary features to multiBar model
+nv.models.discreteBar = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , x = d3.scale.ordinal()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
+ , color = nv.utils.defaultColor()
+ , showValues = false
+ , valueFormat = d3.format(',.2f')
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout','renderEnd')
+ , rectClass = 'discreteBar'
+ , duration = 250
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var x0, y0;
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ //add series index to each data point for reference
+ data.forEach(function(series, i) {
+ series.values.forEach(function(point) {
+ point.series = i;
+ });
+ });
+
+ // Setup Scales
+ // remap and flatten the data for use in calculating the scales' domains
+ var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
+ data.map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i), y0: d.y0 }
+ })
+ });
+
+ x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
+ .rangeBands(xRange || [0, availableWidth], .1);
+ y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y }).concat(forceY)));
+
+ // If showValues, pad the Y axis range to account for label height
+ if (showValues) y.range(yRange || [availableHeight - (y.domain()[0] < 0 ? 12 : 0), y.domain()[1] > 0 ? 12 : 0]);
+ else y.range(yRange || [availableHeight, 0]);
+
+ //store old scales if they exist
+ x0 = x0 || x;
+ y0 = y0 || y.copy().range([y(0),y(0)]);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-discretebar').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discretebar');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-groups');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ //TODO: by definition, the discrete bar should not have multiple groups, will modify/remove later
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d) { return d.key });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
+ groups.exit()
+ .watchTransition(renderWatch, 'discreteBar: exit groups')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6)
+ .remove();
+ groups
+ .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
+ .classed('hover', function(d) { return d.hover });
+ groups
+ .watchTransition(renderWatch, 'discreteBar: groups')
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', .75);
+
+ var bars = groups.selectAll('g.nv-bar')
+ .data(function(d) { return d.values });
+ bars.exit().remove();
+
+ var barsEnter = bars.enter().append('g')
+ .attr('transform', function(d,i,j) {
+ return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05 ) + ', ' + y(0) + ')'
+ })
+ .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ })
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ })
+ .on('click', function(d,i) {
+ dispatch.elementClick({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ d3.event.stopPropagation();
+ })
+ .on('dblclick', function(d,i) {
+ dispatch.elementDblClick({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ d3.event.stopPropagation();
+ });
+
+ barsEnter.append('rect')
+ .attr('height', 0)
+ .attr('width', x.rangeBand() * .9 / data.length )
+
+ if (showValues) {
+ barsEnter.append('text')
+ .attr('text-anchor', 'middle')
+ ;
+
+ bars.select('text')
+ .text(function(d,i) { return valueFormat(getY(d,i)) })
+ .watchTransition(renderWatch, 'discreteBar: bars text')
+ .attr('x', x.rangeBand() * .9 / 2)
+ .attr('y', function(d,i) { return getY(d,i) < 0 ? y(getY(d,i)) - y(0) + 12 : -4 })
+
+ ;
+ } else {
+ bars.selectAll('text').remove();
+ }
+
+ bars
+ .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive' })
+ .style('fill', function(d,i) { return d.color || color(d,i) })
+ .style('stroke', function(d,i) { return d.color || color(d,i) })
+ .select('rect')
+ .attr('class', rectClass)
+ .watchTransition(renderWatch, 'discreteBar: bars rect')
+ .attr('width', x.rangeBand() * .9 / data.length);
+ bars.watchTransition(renderWatch, 'discreteBar: bars')
+ //.delay(function(d,i) { return i * 1200 / data[0].values.length })
+ .attr('transform', function(d,i) {
+ var left = x(getX(d,i)) + x.rangeBand() * .05,
+ top = getY(d,i) < 0 ?
+ y(0) :
+ y(0) - y(getY(d,i)) < 1 ?
+ y(0) - 1 : //make 1 px positive bars show up above y=0
+ y(getY(d,i));
+
+ return 'translate(' + left + ', ' + top + ')'
+ })
+ .select('rect')
+ .attr('height', function(d,i) {
+ return Math.max(Math.abs(y(getY(d,i)) - y((yDomain && yDomain[0]) || 0)) || 1)
+ });
+
+
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
+
+ });
+
+ renderWatch.renderEnd('discreteBar immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ showValues: {get: function(){return showValues;}, set: function(_){showValues=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.models.discreteBarChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var discretebar = nv.models.discreteBar()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ ;
+
+ var margin = {top: 15, right: 10, bottom: 50, left: 60}
+ , width = null
+ , height = null
+ , color = nv.utils.getColor()
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , staggerLabels = false
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + x + ' ' +
+ '
' + y + '
'
+ }
+ , x
+ , y
+ , noData = "No Data Available."
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'beforeUpdate','renderEnd')
+ , duration = 250
+ ;
+
+ xAxis
+ .orient('bottom')
+ .highlightZero(false)
+ .showMaxMin(false)
+ .tickFormat(function(d) { return d })
+ ;
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ .tickFormat(d3.format(',.1f'))
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(discretebar.x()(e.point, e.pointIndex)),
+ y = yAxis.tickFormat()(discretebar.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
+ };
+
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(discretebar);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ chart.update = function() {
+ dispatch.beforeUpdate();
+ container.transition().duration(duration).call(chart);
+ };
+ chart.container = this;
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ x = discretebar.xScale();
+ y = discretebar.yScale().clamp(true);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-discreteBarWithAxes').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discreteBarWithAxes').append('g');
+ var defsEnter = gEnter.append('defs');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis')
+ .append('g').attr('class', 'nv-zeroLine')
+ .append('line');
+
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+
+ g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
+
+ // Main Chart Component(s)
+ discretebar
+ .width(availableWidth)
+ .height(availableHeight);
+
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }))
+
+ barsWrap.transition().call(discretebar);
+
+
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-x-label-clip-' + discretebar.id())
+ .append('rect');
+
+ g.select('#nv-x-label-clip-' + discretebar.id() + ' rect')
+ .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1))
+ .attr('height', 16)
+ .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 ));
+
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
+
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + (y.range()[0] + ((discretebar.showValues() && y.domain()[0] < 0) ? 16 : 0)) + ')');
+ g.select('.nv-x.nv-axis').call(xAxis);
+
+ var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
+ if (staggerLabels) {
+ xTicks
+ .selectAll('text')
+ .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' })
+ }
+ }
+
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-y.nv-axis').call(yAxis);
+ }
+
+ // Zero line
+ g.select(".nv-zeroLine line")
+ .attr("x1",0)
+ .attr("x2",availableWidth)
+ .attr("y1", y(0))
+ .attr("y2", y(0))
+ ;
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ });
+
+ renderWatch.renderEnd('discreteBar chart immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ discretebar.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ discretebar.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.discretebar = discretebar;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ discretebar.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ discretebar.color(color);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, discretebar);
+ nv.utils.initOptions(chart);
+
+ return chart;
+}
+
+nv.models.distribution = function() {
+ "use strict";
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 400 //technically width or height depending on x or y....
+ , size = 8
+ , axis = 'x' // 'x' or 'y'... horizontal or vertical
+ , getData = function(d) { return d[axis] } // defaults d.x or d.y
+ , color = nv.utils.defaultColor()
+ , scale = d3.scale.linear()
+ , domain
+ , duration = 250
+ , dispatch = d3.dispatch('renderEnd')
+ ;
+
+ //============================================================
+
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var scale0;
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ //============================================================
+
+
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom),
+ naxis = axis == 'x' ? 'y' : 'x',
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ //------------------------------------------------------------
+ // Setup Scales
+
+ scale0 = scale0 || scale;
+
+ //------------------------------------------------------------
+
+
+ //------------------------------------------------------------
+ // Setup containers and skeleton of chart
+
+ var wrap = container.selectAll('g.nv-distribution').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-distribution');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
+
+ //------------------------------------------------------------
+
+
+ var distWrap = g.selectAll('g.nv-dist')
+ .data(function(d) { return d }, function(d) { return d.key });
+
+ distWrap.enter().append('g');
+ distWrap
+ .attr('class', function(d,i) { return 'nv-dist nv-series-' + i })
+ .style('stroke', function(d,i) { return color(d, i) });
+
+ var dist = distWrap.selectAll('line.nv-dist' + axis)
+ .data(function(d) { return d.values })
+ dist.enter().append('line')
+ .attr(axis + '1', function(d,i) { return scale0(getData(d,i)) })
+ .attr(axis + '2', function(d,i) { return scale0(getData(d,i)) })
+ renderWatch.transition(distWrap.exit().selectAll('line.nv-dist' + axis), 'dist exit')
+ // .transition()
+ .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
+ .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
+ .style('stroke-opacity', 0)
+ .remove();
+ dist
+ .attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i })
+ .attr(naxis + '1', 0)
+ .attr(naxis + '2', size);
+ renderWatch.transition(dist, 'dist')
+ // .transition()
+ .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
+ .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
+
+
+ scale0 = scale.copy();
+
+ });
+ renderWatch.renderEnd('distribution immediate');
+ return chart;
+ }
+
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+ chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+
+ chart.margin = function(_) {
+ if (!arguments.length) return margin;
+ margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
+ margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
+ margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
+ margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
+ return chart;
+ };
+
+ chart.width = function(_) {
+ if (!arguments.length) return width;
+ width = _;
+ return chart;
+ };
+
+ chart.axis = function(_) {
+ if (!arguments.length) return axis;
+ axis = _;
+ return chart;
+ };
+
+ chart.size = function(_) {
+ if (!arguments.length) return size;
+ size = _;
+ return chart;
+ };
+
+ chart.getData = function(_) {
+ if (!arguments.length) return getData;
+ getData = d3.functor(_);
+ return chart;
+ };
+
+ chart.scale = function(_) {
+ if (!arguments.length) return scale;
+ scale = _;
+ return chart;
+ };
+
+ chart.color = function(_) {
+ if (!arguments.length) return color;
+ color = nv.utils.getColor(_);
+ return chart;
+ };
+
+ chart.duration = function(_) {
+ if (!arguments.length) return duration;
+ duration = _;
+ renderWatch.reset(duration);
+ return chart;
+ };
+ //============================================================
+
+
+ return chart;
+}
+//TODO: consider deprecating and using multibar with single series for this
+nv.models.historicalBar = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = null
+ , height = null
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , forceX = []
+ , forceY = [0]
+ , padData = false
+ , clipEdge = true
+ , color = nv.utils.defaultColor()
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
+ , interactive = true
+ ;
+
+ var renderWatch = nv.utils.renderWatch(dispatch, 0);
+
+ function chart(selection) {
+ selection.each(function(data) {
+ renderWatch.reset();
+
+ var container = d3.select(this);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right;
+ var availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ nv.utils.initSVG(container);
+
+ // Setup Scales
+ x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
+
+ if (padData)
+ x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range(xRange || [0, availableWidth]);
+
+ y.domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) ))
+ .range(yRange || [availableHeight, 0]);
+
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
+
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id);
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-bars');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ container
+ .on('click', function(d,i) {
+ dispatch.chartClick({
+ data: d,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
+ });
+
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-chart-clip-path-' + id)
+ .append('rect');
+
+ wrap.select('#nv-chart-clip-path-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
+
+ g.attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
+
+ var bars = wrap.select('.nv-bars').selectAll('.nv-bar')
+ .data(function(d) { return d }, function(d,i) {return getX(d,i)});
+ bars.exit().remove();
+
+ var barsEnter = bars.enter().append('rect')
+ .attr('x', 0 )
+ .attr('y', function(d,i) { return nv.utils.NaNtoZero(y(Math.max(0, getY(d,i)))) })
+ .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.abs(y(getY(d,i)) - y(0))) })
+ .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; })
+ .on('mouseover', function(d,i) {
+ if (!interactive) return;
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ point: d,
+ series: data[0],
+ pos: [x(getX(d,i)), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: 0,
+ e: d3.event
+ });
+
+ })
+ .on('mouseout', function(d,i) {
+ if (!interactive) return;
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ point: d,
+ series: data[0],
+ pointIndex: i,
+ seriesIndex: 0,
+ e: d3.event
+ });
+ })
+ .on('click', function(d,i) {
+ if (!interactive) return;
+ dispatch.elementClick({
+ //label: d[label],
+ value: getY(d,i),
+ data: d,
+ index: i,
+ pos: [x(getX(d,i)), y(getY(d,i))],
+ e: d3.event,
+ id: id
+ });
+ d3.event.stopPropagation();
+ })
+ .on('dblclick', function(d,i) {
+ if (!interactive) return;
+ dispatch.elementDblClick({
+ //label: d[label],
+ value: getY(d,i),
+ data: d,
+ index: i,
+ pos: [x(getX(d,i)), y(getY(d,i))],
+ e: d3.event,
+ id: id
+ });
+ d3.event.stopPropagation();
+ });
+
+ bars
+ .attr('fill', function(d,i) { return color(d, i); })
+ .attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i })
+ .watchTransition(renderWatch, 'bars')
+ .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; })
+ //TODO: better width calculations that don't assume always uniform data spacing;w
+ .attr('width', (availableWidth / data[0].values.length) * .9 );
+
+ bars.watchTransition(renderWatch, 'bars')
+ .attr('y', function(d,i) {
+ var rval = getY(d,i) < 0 ?
+ y(0) :
+ y(0) - y(getY(d,i)) < 1 ?
+ y(0) - 1 :
+ y(getY(d,i));
+ return nv.utils.NaNtoZero(rval);
+ })
+ .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(0)),1)) });
+
+ });
+
+ renderWatch.renderEnd('historicalBar immediate');
+ return chart;
+ }
+
+ //Create methods to allow outside functions to highlight a specific bar.
+ chart.highlightPoint = function(pointIndex, isHoverOver) {
+ d3.select(".nv-historicalBar-" + id)
+ .select(".nv-bars .nv-bar-0-" + pointIndex)
+ .classed("hover", isHoverOver)
+ ;
+ };
+
+ chart.clearHighlights = function() {
+ d3.select(".nv-historicalBar-" + id)
+ .select(".nv-bars .nv-bar.hover")
+ .classed("hover", false)
+ ;
+ };
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ padData: {get: function(){return padData;}, set: function(_){padData=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.models.historicalBarChart = function(bar_model) {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var bars = bar_model || nv.models.historicalBar()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , interactiveLayer = nv.interactiveGuideline()
+ ;
+
+
+ var margin = {top: 30, right: 90, bottom: 50, left: 90}
+ , color = nv.utils.defaultColor()
+ , width = null
+ , height = null
+ , showLegend = false
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , useInteractiveGuideline = false
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' ' +
+ '
' + y + ' at ' + x + '
'
+ }
+ , x
+ , y
+ , state = {}
+ , defaultState = null
+ , noData = 'No Data Available.'
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
+ , transitionDuration = 250
+ ;
+
+ xAxis
+ .orient('bottom')
+ .tickPadding(7)
+ ;
+ yAxis
+ .orient( (rightAlignYAxis) ? 'right' : 'left')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+
+ // New addition to calculate position if SVG is scaled with viewBox, may move TODO: consider implementing everywhere else
+ if (offsetElement) {
+ var svg = d3.select(offsetElement).select('svg');
+ var viewBox = (svg.node()) ? svg.attr('viewBox') : null;
+ if (viewBox) {
+ viewBox = viewBox.split(' ');
+ var ratio = parseInt(svg.style('width')) / viewBox[2];
+ e.pos[0] = e.pos[0] * ratio;
+ e.pos[1] = e.pos[1] * ratio;
+ }
+ }
+
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(bars.x()(e.point, e.pointIndex)),
+ y = yAxis.tickFormat()(bars.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, null, null, offsetElement);
+ };
+ var renderWatch = nv.utils.renderWatch(dispatch, 0);
+
+ function chart(selection) {
+ selection.each(function(data) {
+ renderWatch.reset();
+ renderWatch.models(bars);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+
+ chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
+ chart.container = this;
+
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ x = bars.xScale();
+ y = bars.yScale();
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-historicalBarChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBarChart').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-interactive');
+
+ // Legend
+ if (showLegend) {
+ legend.width(availableWidth);
+
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ }
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
+
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left:margin.left, top:margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
+ bars
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }));
+ barsWrap.transition().call(bars);
+
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ .tickSize(-availableHeight, 0);
+
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ g.select('.nv-x.nv-axis')
+ .transition()
+ .call(xAxis);
+ }
+
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-y.nv-axis')
+ .transition()
+ .call(yAxis);
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ bars.clearHighlights();
+
+ var singlePoint, pointIndex, pointXLocation, allData = [];
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ bars.highlightPoint(pointIndex,true);
+ var point = series.values[pointIndex];
+ if (typeof point === 'undefined') return;
+ if (typeof singlePoint === 'undefined') singlePoint = point;
+ if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ allData.push({
+ key: series.key,
+ value: chart.y()(point, pointIndex),
+ color: color(series,series.seriesIndex),
+ data: series.values[pointIndex]
+ });
+ });
+
+ var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
+ interactiveLayer.tooltip
+ .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
+ .chartContainer(that.parentNode)
+ .enabled(tooltips)
+ .valueFormatter(function(d,i) {
+ return yAxis.tickFormat()(d);
+ })
+ .data(
+ {
+ value: xValue,
+ series: allData
+ }
+ )();
+
+ interactiveLayer.renderGuideLine(pointXLocation);
+
+ });
+
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ dispatch.tooltipHide();
+ bars.clearHighlights();
+ });
+
+ legend.dispatch.on('legendClick', function(d,i) {
+ d.disabled = !d.disabled;
+
+ if (!data.filter(function(d) { return !d.disabled }).length) {
+ data.map(function(d) {
+ d.disabled = false;
+ wrap.selectAll('.nv-series').classed('disabled', false);
+ return d;
+ });
+ }
+
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
+ selection.transition().call(chart);
+ });
+
+ legend.dispatch.on('legendDblclick', function(d) {
+ //Double clicking should always enable current series, and disabled all others.
+ data.forEach(function(d) {
+ d.disabled = true;
+ });
+ d.disabled = false;
+
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ chart.update();
+ });
+ });
+
+ renderWatch.renderEnd('historicalBarChart immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ bars.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ bars.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.bars = bars;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.interactiveLayer = interactiveLayer;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ bars.color(color);
+ }},
+ duration: {get: function(){return transitionDuration;}, set: function(_){
+ transitionDuration=_;
+ renderWatch.reset(transitionDuration);
+ yAxis.duration(transitionDuration);
+ xAxis.duration(transitionDuration);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = _;
+ if (_ === true) {
+ chart.interactive(false);
+ }
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, bars);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+
+// ohlcChart is just a historical chart with oclc bars and some tweaks
+nv.models.ohlcBarChart = function() {
+ var chart = nv.models.historicalBarChart(nv.models.ohlcBar());
+
+ // special default tooltip since we show multiple values per x
+ chart.useInteractiveGuideline(true);
+ chart.interactiveLayer.tooltip.contentGenerator(function(data) {
+ // we assume only one series exists for this chart
+ var d = data.series[0].data;
+ // match line colors as defined in nv.d3.css
+ var color = d.open < d.close ? "2ca02c" : "d62728";
+ return '' +
+ '
' + data.value + ' ' +
+ '
' +
+ 'open: ' + chart.yAxis.tickFormat()(d.open) + ' ' +
+ 'close: ' + chart.yAxis.tickFormat()(d.close) + ' ' +
+ 'high ' + chart.yAxis.tickFormat()(d.high) + ' ' +
+ 'low: ' + chart.yAxis.tickFormat()(d.low) + ' ' +
+ '
';
+ });
+ return chart;
+};nv.models.legend = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 5, right: 0, bottom: 5, left: 0}
+ , width = 400
+ , height = 20
+ , getKey = function(d) { return d.key }
+ , color = nv.utils.defaultColor()
+ , align = true
+ , rightAlign = true
+ , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch.
+ , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time)
+ , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
+ ;
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-legend').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g');
+ var g = wrap.select('g');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ var series = g.selectAll('.nv-series')
+ .data(function(d) { return d });
+ var seriesEnter = series.enter().append('g').attr('class', 'nv-series')
+ .on('mouseover', function(d,i) {
+ dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects
+ })
+ .on('mouseout', function(d,i) {
+ dispatch.legendMouseout(d,i);
+ })
+ .on('click', function(d,i) {
+ dispatch.legendClick(d,i);
+ if (updateState) {
+ if (radioButtonMode) {
+ //Radio button mode: set every series to disabled,
+ // and enable the clicked series.
+ data.forEach(function(series) { series.disabled = true});
+ d.disabled = false;
+ }
+ else {
+ d.disabled = !d.disabled;
+ if (data.every(function(series) { return series.disabled})) {
+ //the default behavior of NVD3 legends is, if every single series
+ // is disabled, turn all series' back on.
+ data.forEach(function(series) { series.disabled = false});
+ }
+ }
+ dispatch.stateChange({
+ disabled: data.map(function(d) { return !!d.disabled })
+ });
+ }
+ })
+ .on('dblclick', function(d,i) {
+ dispatch.legendDblclick(d,i);
+ if (updateState) {
+ //the default behavior of NVD3 legends, when double clicking one,
+ // is to set all other series' to false, and make the double clicked series enabled.
+ data.forEach(function(series) {
+ series.disabled = true;
+ });
+ d.disabled = false;
+ dispatch.stateChange({
+ disabled: data.map(function(d) { return !!d.disabled })
+ });
+ }
+ });
+ seriesEnter.append('circle')
+ .style('stroke-width', 2)
+ .attr('class','nv-legend-symbol')
+ .attr('r', 5);
+ seriesEnter.append('text')
+ .attr('text-anchor', 'start')
+ .attr('class','nv-legend-text')
+ .attr('dy', '.32em')
+ .attr('dx', '8');
+ series.classed('nv-disabled', function(d) { return d.disabled });
+ series.exit().remove();
+ series.select('circle')
+ .style('fill', function(d,i) { return d.color || color(d,i)})
+ .style('stroke', function(d,i) { return d.color || color(d, i) });
+ series.select('text').text(getKey);
+
+ //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
+ // NEW ALIGNING CODE, TODO: clean up
+ if (align) {
+
+ var seriesWidths = [];
+ series.each(function(d,i) {
+ var legendText = d3.select(this).select('text');
+ var nodeTextLength;
+ try {
+ nodeTextLength = legendText.node().getComputedTextLength();
+ // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead
+ if(nodeTextLength <= 0) throw Error();
+ }
+ catch(e) {
+ nodeTextLength = nv.utils.calcApproxTextWidth(legendText);
+ }
+
+ seriesWidths.push(nodeTextLength + 28); // 28 is ~ the width of the circle plus some padding
+ });
+
+ var seriesPerRow = 0;
+ var legendWidth = 0;
+ var columnWidths = [];
+
+ while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) {
+ columnWidths[seriesPerRow] = seriesWidths[seriesPerRow];
+ legendWidth += seriesWidths[seriesPerRow++];
+ }
+ if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row
+
+ while ( legendWidth > availableWidth && seriesPerRow > 1 ) {
+ columnWidths = [];
+ seriesPerRow--;
+
+ for (var k = 0; k < seriesWidths.length; k++) {
+ if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) )
+ columnWidths[k % seriesPerRow] = seriesWidths[k];
+ }
+
+ legendWidth = columnWidths.reduce(function(prev, cur, index, array) {
+ return prev + cur;
+ });
+ }
+
+ var xPositions = [];
+ for (var i = 0, curX = 0; i < seriesPerRow; i++) {
+ xPositions[i] = curX;
+ curX += columnWidths[i];
+ }
+
+ series
+ .attr('transform', function(d, i) {
+ return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * 20) + ')';
+ });
+
+ //position legend as far right as possible within the total width
+ if (rightAlign) {
+ g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')');
+ }
+ else {
+ g.attr('transform', 'translate(0' + ',' + margin.top + ')');
+ }
+
+ height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * 20);
+
+ } else {
+
+ var ypos = 5,
+ newxpos = 5,
+ maxwidth = 0,
+ xpos;
+ series
+ .attr('transform', function(d, i) {
+ var length = d3.select(this).select('text').node().getComputedTextLength() + 28;
+ xpos = newxpos;
+
+ if (width < margin.left + margin.right + xpos + length) {
+ newxpos = xpos = 5;
+ ypos += 20;
+ }
+
+ newxpos += length;
+ if (newxpos > maxwidth) maxwidth = newxpos;
+
+ return 'translate(' + xpos + ',' + ypos + ')';
+ });
+
+ //position legend as far right as possible within the total width
+ g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')');
+
+ height = margin.top + margin.bottom + ypos + 15;
+ }
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ key: {get: function(){return getKey;}, set: function(_){getKey=_;}},
+ align: {get: function(){return align;}, set: function(_){align=_;}},
+ rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}},
+ updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}},
+ radioButtonMode: {get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.models.line = function() {
+ "use strict";
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var scatter = nv.models.scatter()
+ ;
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , color = nv.utils.defaultColor() // a function that returns a color
+ , getX = function(d) { return d.x } // accessor to get the x value from a data point
+ , getY = function(d) { return d.y } // accessor to get the y value from a data point
+ , defined = function(d,i) { return !isNaN(getY(d,i)) && getY(d,i) !== null } // allows a line to be not continuous when it is not defined
+ , isArea = function(d) { return d.area } // decides if a line is an area or just a line
+ , clipEdge = false // if true, masks lines within x and y scale
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , interpolate = "linear" // controls the line interpolation
+ , duration = 250
+ , dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
+ ;
+
+ scatter
+ .pointSize(16) // default size
+ .pointDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor
+ ;
+
+ //============================================================
+
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var x0, y0 //used to store previous scales
+ , renderWatch = nv.utils.renderWatch(dispatch, duration)
+ ;
+
+ //============================================================
+
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(scatter);
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ // Setup Scales
+ x = scatter.xScale();
+ y = scatter.yScale();
+
+ x0 = x0 || x;
+ y0 = y0 || y;
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-line').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-line');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-groups');
+ gEnter.append('g').attr('class', 'nv-scatterWrap');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ scatter
+ .width(availableWidth)
+ .height(availableHeight);
+
+ var scatterWrap = wrap.select('.nv-scatterWrap');
+ scatterWrap.call(scatter);
+
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-edge-clip-' + scatter.id())
+ .append('rect');
+
+ wrap.select('#nv-edge-clip-' + scatter.id() + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', (availableHeight > 0) ? availableHeight : 0);
+
+ g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
+ scatterWrap
+ .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
+
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d) { return d.key });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
+
+ groups.exit().remove();
+
+ groups
+ .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
+ .classed('hover', function(d) { return d.hover })
+ .style('fill', function(d,i){ return color(d, i) })
+ .style('stroke', function(d,i){ return color(d, i)});
+ groups.watchTransition(renderWatch, 'line: groups')
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', .5);
+
+ var areaPaths = groups.selectAll('path.nv-area')
+ .data(function(d) { return isArea(d) ? [d] : [] }); // this is done differently than lines because I need to check if series is an area
+ areaPaths.enter().append('path')
+ .attr('class', 'nv-area')
+ .attr('d', function(d) {
+ return d3.svg.area()
+ .interpolate(interpolate)
+ .defined(defined)
+ .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
+ .y0(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
+ .y1(function(d,i) { return y0( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
+ //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
+ .apply(this, [d.values])
+ });
+ groups.exit().selectAll('path.nv-area')
+ .remove();
+
+ areaPaths.watchTransition(renderWatch, 'line: areaPaths')
+ .attr('d', function(d) {
+ return d3.svg.area()
+ .interpolate(interpolate)
+ .defined(defined)
+ .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
+ .y0(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
+ .y1(function(d,i) { return y( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
+ //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
+ .apply(this, [d.values])
+ });
+
+ var linePaths = groups.selectAll('path.nv-line')
+ .data(function(d) { return [d.values] });
+ linePaths.enter().append('path')
+ .attr('class', 'nv-line')
+ .attr('d',
+ d3.svg.line()
+ .interpolate(interpolate)
+ .defined(defined)
+ .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
+ .y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
+ );
+
+ linePaths.watchTransition(renderWatch, 'line: linePaths')
+ .attr('d',
+ d3.svg.line()
+ .interpolate(interpolate)
+ .defined(defined)
+ .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
+ .y(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
+ );
+
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
+ });
+ renderWatch.renderEnd('line immediate');
+ return chart;
+ }
+
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.scatter = scatter;
+ // Pass through events
+ scatter.dispatch.on('elementClick', function(){ dispatch.elementClick.apply(this, arguments); })
+ scatter.dispatch.on('elementMouseover', function(){ dispatch.elementMouseover.apply(this, arguments); })
+ scatter.dispatch.on('elementMouseout', function(){ dispatch.elementMouseout.apply(this, arguments); })
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ defined: {get: function(){return defined;}, set: function(_){defined=_;}},
+ interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ scatter.duration(duration);
+ }},
+ isArea: {get: function(){return isArea;}, set: function(_){
+ isArea = d3.functor(_);
+ }},
+ x: {get: function(){return getX;}, set: function(_){
+ getX = _;
+ scatter.x(_);
+ }},
+ y: {get: function(){return getY;}, set: function(_){
+ getY = _;
+ scatter.y(_);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ scatter.color(color);
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, scatter);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+nv.models.lineChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var lines = nv.models.line()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , interactiveLayer = nv.interactiveGuideline()
+ ;
+
+ var margin = {top: 30, right: 20, bottom: 50, left: 60}
+ , color = nv.utils.defaultColor()
+ , width = null
+ , height = null
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , useInteractiveGuideline = false
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' ' +
+ '
' + y + ' at ' + x + '
'
+ }
+ , x
+ , y
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = 'No Data Available.'
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
+ , duration = 250
+ ;
+
+ xAxis
+ .orient('bottom')
+ .tickPadding(7)
+ ;
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
+ y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, null, null, offsetElement);
+ };
+
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled })
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(lines);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+
+ chart.update = function() {
+ if (duration === 0)
+ container.call(chart);
+ else
+ container.transition().duration(duration).call(chart)
+ };
+ chart.container = this;
+
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+
+ // Setup Scales
+ x = lines.xScale();
+ y = lines.yScale();
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-lineChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineChart').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append("rect").style("opacity",0);
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-linesWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-interactive');
+
+ g.select("rect")
+ .attr("width",availableWidth)
+ .attr("height",(availableHeight > 0) ? availableHeight : 0);
+
+ // Legend
+ if (showLegend) {
+ legend.width(availableWidth);
+
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ }
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
+
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left:margin.left, top:margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
+
+ lines
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+
+
+ var linesWrap = g.select('.nv-linesWrap')
+ .datum(data.filter(function(d) { return !d.disabled }));
+
+ linesWrap.call(lines);
+
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
+
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ g.select('.nv-x.nv-axis')
+ .call(xAxis);
+ }
+
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-y.nv-axis')
+ .call(yAxis);
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ lines.clearHighlights();
+ var singlePoint, pointIndex, pointXLocation, allData = [];
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ lines.highlightPoint(i, pointIndex, true);
+ var point = series.values[pointIndex];
+ if (typeof point === 'undefined') return;
+ if (typeof singlePoint === 'undefined') singlePoint = point;
+ if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ allData.push({
+ key: series.key,
+ value: chart.y()(point, pointIndex),
+ color: color(series,series.seriesIndex)
+ });
+ });
+ //Highlight the tooltip entry based on which point the mouse is closest to.
+ if (allData.length > 2) {
+ var yValue = chart.yScale().invert(e.mouseY);
+ var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
+ var threshold = 0.03 * domainExtent;
+ var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold);
+ if (indexToHighlight !== null)
+ allData[indexToHighlight].highlight = true;
+ }
+
+ var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
+ interactiveLayer.tooltip
+ .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
+ .chartContainer(that.parentNode)
+ .enabled(tooltips)
+ .valueFormatter(function(d,i) {
+ return yAxis.tickFormat()(d);
+ })
+ .data(
+ {
+ value: xValue,
+ series: allData
+ }
+ )();
+
+ interactiveLayer.renderGuideLine(pointXLocation);
+
+ });
+
+ interactiveLayer.dispatch.on('elementClick', function(e) {
+ var pointXLocation, allData = [];
+
+ data.filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ }).forEach(function(series) {
+ var pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ var point = series.values[pointIndex];
+ if (typeof point === 'undefined') return;
+ if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ var yPos = chart.yScale()(chart.y()(point,pointIndex));
+ allData.push({
+ point: point,
+ pointIndex: pointIndex,
+ pos: [pointXLocation, yPos],
+ seriesIndex: series.seriesIndex,
+ series: series
+ });
+ });
+
+ lines.dispatch.elementClick(allData);
+ });
+
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ dispatch.tooltipHide();
+ lines.clearHighlights();
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ chart.update();
+ });
+
+ });
+
+ renderWatch.renderEnd('lineChart immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ lines.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.lines = lines;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.interactiveLayer = interactiveLayer;
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ lines.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ lines.color(color);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( rightAlignYAxis ? 'right' : 'left');
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = _;
+ if (useInteractiveGuideline) {
+ lines.interactive(false);
+ lines.useVoronoi(false);
+ }
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, lines);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+nv.models.linePlusBarChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var lines = nv.models.line()
+ , lines2 = nv.models.line()
+ , bars = nv.models.historicalBar()
+ , bars2 = nv.models.historicalBar()
+ , xAxis = nv.models.axis()
+ , x2Axis = nv.models.axis()
+ , y1Axis = nv.models.axis()
+ , y2Axis = nv.models.axis()
+ , y3Axis = nv.models.axis()
+ , y4Axis = nv.models.axis()
+ , legend = nv.models.legend()
+ , brush = d3.svg.brush()
+ ;
+
+ var margin = {top: 30, right: 30, bottom: 30, left: 60}
+ , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
+ , width = null
+ , height = null
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , color = nv.utils.defaultColor()
+ , showLegend = true
+ , focusEnable = true
+ , focusShowAxisY = false
+ , focusShowAxisX = true
+ , focusHeight = 50
+ , extent
+ , brushExtent = null
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' ' +
+ '
' + y + ' at ' + x + '
';
+ }
+ , x
+ , x2
+ , y1
+ , y2
+ , y3
+ , y4
+ , noData = "No Data Available."
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush', 'stateChange', 'changeState')
+ , transitionDuration = 0
+ , state = nv.utils.state()
+ , defaultState = null
+ , legendLeftAxisHint = ' (left axis)'
+ , legendRightAxisHint = ' (right axis)'
+ ;
+
+ lines
+ .clipEdge(true)
+ ;
+ lines2
+ .interactive(false)
+ ;
+ xAxis
+ .orient('bottom')
+ .tickPadding(5)
+ ;
+ y1Axis
+ .orient('left')
+ ;
+ y2Axis
+ .orient('right')
+ ;
+ x2Axis
+ .orient('bottom')
+ .tickPadding(5)
+ ;
+ y3Axis
+ .orient('left')
+ ;
+ y4Axis
+ .orient('right')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+ if (extent) {
+ e.pointIndex += Math.ceil(extent[0]);
+ }
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
+ y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
+ };
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled })
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight1 = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom - (focusEnable ? focusHeight : 0) ,
+ availableHeight2 = focusHeight - margin2.top - margin2.bottom;
+
+ chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
+ chart.container = this;
+
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight1 / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
+ var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
+
+ x = bars.xScale();
+ x2 = x2Axis.scale();
+ y1 = bars.yScale();
+ y2 = lines.yScale();
+ y3 = bars2.yScale();
+ y4 = lines2.yScale();
+
+ var series1 = data
+ .filter(function(d) { return !d.disabled && d.bar })
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i) }
+ })
+ });
+
+ var series2 = data
+ .filter(function(d) { return !d.disabled && !d.bar })
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i) }
+ })
+ });
+
+ x.range([0, availableWidth]);
+
+ x2 .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
+ .range([0, availableWidth]);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+
+ // this is the main chart
+ var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
+ focusEnter.append('g').attr('class', 'nv-x nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y1 nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y2 nv-axis');
+ focusEnter.append('g').attr('class', 'nv-barsWrap');
+ focusEnter.append('g').attr('class', 'nv-linesWrap');
+
+ // context chart is where you can focus in
+ var contextEnter = gEnter.append('g').attr('class', 'nv-context');
+ contextEnter.append('g').attr('class', 'nv-x nv-axis');
+ contextEnter.append('g').attr('class', 'nv-y1 nv-axis');
+ contextEnter.append('g').attr('class', 'nv-y2 nv-axis');
+ contextEnter.append('g').attr('class', 'nv-barsWrap');
+ contextEnter.append('g').attr('class', 'nv-linesWrap');
+ contextEnter.append('g').attr('class', 'nv-brushBackground');
+ contextEnter.append('g').attr('class', 'nv-x nv-brush');
+
+ //============================================================
+ // Legend
+ //------------------------------------------------------------
+
+ if (showLegend) {
+ legend.width( availableWidth / 2 );
+
+ g.select('.nv-legendWrap')
+ .datum(data.map(function(series) {
+ series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
+ series.key = series.originalKey + (series.bar ? legendLeftAxisHint : legendRightAxisHint);
+ return series;
+ }))
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight1 = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom - focusHeight;
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
+ }
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ //============================================================
+ // Context chart (focus chart) components
+ //------------------------------------------------------------
+
+ // hide or show the focus context chart
+ g.select('.nv-context').style('display', focusEnable ? 'initial' : 'none');
+
+ bars2
+ .width(availableWidth)
+ .height(availableHeight2)
+ .color(data.map(function (d, i) {
+ return d.color || color(d, i);
+ }).filter(function (d, i) {
+ return !data[i].disabled && data[i].bar
+ }));
+ lines2
+ .width(availableWidth)
+ .height(availableHeight2)
+ .color(data.map(function (d, i) {
+ return d.color || color(d, i);
+ }).filter(function (d, i) {
+ return !data[i].disabled && !data[i].bar
+ }));
+
+ var bars2Wrap = g.select('.nv-context .nv-barsWrap')
+ .datum(dataBars.length ? dataBars : [
+ {values: []}
+ ]);
+ var lines2Wrap = g.select('.nv-context .nv-linesWrap')
+ .datum(!dataLines[0].disabled ? dataLines : [
+ {values: []}
+ ]);
+
+ g.select('.nv-context')
+ .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')');
+
+ bars2Wrap.transition().call(bars2);
+ lines2Wrap.transition().call(lines2);
+
+ // context (focus chart) axis controls
+ if (focusShowAxisX) {
+ x2Axis
+ .ticks(nv.utils.calcTicksX(availableWidth / 100, data))
+ .tickSize(-availableHeight2, 0);
+ g.select('.nv-context .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y3.range()[0] + ')');
+ g.select('.nv-context .nv-x.nv-axis').transition()
+ .call(x2Axis);
+ }
+
+ if (focusShowAxisY) {
+ y3Axis
+ .scale(y3)
+ .ticks( availableHeight2 / 36 )
+ .tickSize( -availableWidth, 0);
+ y4Axis
+ .scale(y4)
+ .ticks( availableHeight2 / 36 )
+ .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
+
+ g.select('.nv-context .nv-y3.nv-axis')
+ .style('opacity', dataBars.length ? 1 : 0)
+ .attr('transform', 'translate(0,' + x2.range()[0] + ')');
+ g.select('.nv-context .nv-y2.nv-axis')
+ .style('opacity', dataLines.length ? 1 : 0)
+ .attr('transform', 'translate(' + x2.range()[1] + ',0)');
+
+ g.select('.nv-context .nv-y1.nv-axis').transition()
+ .call(y3Axis);
+ g.select('.nv-context .nv-y2.nv-axis').transition()
+ .call(y4Axis);
+ }
+
+ // Setup Brush
+ brush.x(x2).on('brush', onBrush);
+
+ if (brushExtent) brush.extent(brushExtent);
+
+ var brushBG = g.select('.nv-brushBackground').selectAll('g')
+ .data([brushExtent || brush.extent()]);
+
+ var brushBGenter = brushBG.enter()
+ .append('g');
+
+ brushBGenter.append('rect')
+ .attr('class', 'left')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight2);
+
+ brushBGenter.append('rect')
+ .attr('class', 'right')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight2);
+
+ var gBrush = g.select('.nv-x.nv-brush')
+ .call(brush);
+ gBrush.selectAll('rect')
+ //.attr('y', -5)
+ .attr('height', availableHeight2);
+ gBrush.selectAll('.resize').append('path').attr('d', resizePath);
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+ state.disabled = e.disabled;
+ }
+ chart.update();
+ });
+
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
+
+ // Taken from crossfilter (http://square.github.com/crossfilter/)
+ function resizePath(d) {
+ var e = +(d == 'e'),
+ x = e ? 1 : -1,
+ y = availableHeight2 / 3;
+ return 'M' + (.5 * x) + ',' + y
+ + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
+ + 'V' + (2 * y - 6)
+ + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
+ + 'Z'
+ + 'M' + (2.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8)
+ + 'M' + (4.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8);
+ }
+
+
+ function updateBrushBG() {
+ if (!brush.empty()) brush.extent(brushExtent);
+ brushBG
+ .data([brush.empty() ? x2.domain() : brushExtent])
+ .each(function(d,i) {
+ var leftWidth = x2(d[0]) - x2.range()[0],
+ rightWidth = x2.range()[1] - x2(d[1]);
+ d3.select(this).select('.left')
+ .attr('width', leftWidth < 0 ? 0 : leftWidth);
+
+ d3.select(this).select('.right')
+ .attr('x', x2(d[1]))
+ .attr('width', rightWidth < 0 ? 0 : rightWidth);
+ });
+ }
+
+ function onBrush() {
+ brushExtent = brush.empty() ? null : brush.extent();
+ extent = brush.empty() ? x2.domain() : brush.extent();
+ dispatch.brush({extent: extent, brush: brush});
+ updateBrushBG();
+
+ // Prepare Main (Focus) Bars and Lines
+ bars
+ .width(availableWidth)
+ .height(availableHeight1)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
+
+ lines
+ .width(availableWidth)
+ .height(availableHeight1)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
+
+ var focusBarsWrap = g.select('.nv-focus .nv-barsWrap')
+ .datum(!dataBars.length ? [{values:[]}] :
+ dataBars
+ .map(function(d,i) {
+ return {
+ key: d.key,
+ values: d.values.filter(function(d,i) {
+ return bars.x()(d,i) >= extent[0] && bars.x()(d,i) <= extent[1];
+ })
+ }
+ })
+ );
+
+ var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
+ .datum(dataLines[0].disabled ? [{values:[]}] :
+ dataLines
+ .map(function(d,i) {
+ return {
+ key: d.key,
+ values: d.values.filter(function(d,i) {
+ return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
+ })
+ }
+ })
+ );
+
+ // Update Main (Focus) X Axis
+ if (dataBars.length) {
+ x = bars.xScale();
+ } else {
+ x = lines.xScale();
+ }
+
+ xAxis
+ .scale(x)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight1, 0);
+
+ xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]);
+
+ g.select('.nv-x.nv-axis').transition().duration(transitionDuration)
+ .call(xAxis);
+
+ // Update Main (Focus) Bars and Lines
+ focusBarsWrap.transition().duration(transitionDuration).call(bars);
+ focusLinesWrap.transition().duration(transitionDuration).call(lines);
+
+ // Setup and Update Main (Focus) Y Axes
+ g.select('.nv-focus .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y1.range()[0] + ')');
+
+ y1Axis
+ .scale(y1)
+ .ticks( nv.utils.calcTicksY(availableHeight1/36, data) )
+ .tickSize(-availableWidth, 0);
+ y2Axis
+ .scale(y2)
+ .ticks( nv.utils.calcTicksY(availableHeight1/36, data) )
+ .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
+
+ g.select('.nv-focus .nv-y1.nv-axis')
+ .style('opacity', dataBars.length ? 1 : 0);
+ g.select('.nv-focus .nv-y2.nv-axis')
+ .style('opacity', dataLines.length && !dataLines[0].disabled ? 1 : 0)
+ .attr('transform', 'translate(' + x.range()[1] + ',0)');
+
+ g.select('.nv-focus .nv-y1.nv-axis').transition().duration(transitionDuration)
+ .call(y1Axis);
+ g.select('.nv-focus .nv-y2.nv-axis').transition().duration(transitionDuration)
+ .call(y2Axis);
+ }
+
+ onBrush();
+
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ lines.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ bars.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ bars.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.legend = legend;
+ chart.lines = lines;
+ chart.lines2 = lines2;
+ chart.bars = bars;
+ chart.bars2 = bars2;
+ chart.xAxis = xAxis;
+ chart.x2Axis = x2Axis;
+ chart.y1Axis = y1Axis;
+ chart.y2Axis = y2Axis;
+ chart.y3Axis = y3Axis;
+ chart.y4Axis = y4Axis;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
+ focusHeight: {get: function(){return focusHeight;}, set: function(_){focusHeight=_;}},
+ focusShowAxisX: {get: function(){return focusShowAxisX;}, set: function(_){focusShowAxisX=_;}},
+ focusShowAxisY: {get: function(){return focusShowAxisY;}, set: function(_){focusShowAxisY=_;}},
+ legendLeftAxisHint: {get: function(){return legendLeftAxisHint;}, set: function(_){legendLeftAxisHint=_;}},
+ legendRightAxisHint: {get: function(){return legendRightAxisHint;}, set: function(_){legendRightAxisHint=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return transitionDuration;}, set: function(_){
+ transitionDuration = _;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ }},
+ x: {get: function(){return getX;}, set: function(_){
+ getX = _;
+ lines.x(_);
+ lines2.x(_);
+ bars.x(_);
+ bars2.x(_);
+ }},
+ y: {get: function(){return getY;}, set: function(_){
+ getY = _;
+ lines.y(_);
+ lines2.y(_);
+ bars.y(_);
+ bars2.y(_);
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, lines);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+nv.models.lineWithFocusChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var lines = nv.models.line()
+ , lines2 = nv.models.line()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , x2Axis = nv.models.axis()
+ , y2Axis = nv.models.axis()
+ , legend = nv.models.legend()
+ , brush = d3.svg.brush()
+ ;
+
+ var margin = {top: 30, right: 30, bottom: 30, left: 60}
+ , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
+ , color = nv.utils.defaultColor()
+ , width = null
+ , height = null
+ , height2 = 100
+ , x
+ , y
+ , x2
+ , y2
+ , showLegend = true
+ , brushExtent = null
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' ' +
+ '
' + y + ' at ' + x + '
'
+ }
+ , noData = "No Data Available."
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush', 'stateChange', 'changeState')
+ , transitionDuration = 250
+ , state = nv.utils.state()
+ , defaultState = null
+ ;
+
+ lines
+ .clipEdge(true)
+ ;
+ lines2
+ .interactive(false)
+ ;
+ xAxis
+ .orient('bottom')
+ .tickPadding(5)
+ ;
+ yAxis
+ .orient('left')
+ ;
+ x2Axis
+ .orient('bottom')
+ .tickPadding(5)
+ ;
+ y2Axis
+ .orient('left')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
+ y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, null, null, offsetElement);
+ };
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled })
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight1 = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom - height2,
+ availableHeight2 = height2 - margin2.top - margin2.bottom;
+
+ chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
+ chart.container = this;
+
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight1 / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ x = lines.xScale();
+ y = lines.yScale();
+ x2 = lines2.xScale();
+ y2 = lines2.yScale();
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-lineWithFocusChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineWithFocusChart').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+
+ var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
+ focusEnter.append('g').attr('class', 'nv-x nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y nv-axis');
+ focusEnter.append('g').attr('class', 'nv-linesWrap');
+
+ var contextEnter = gEnter.append('g').attr('class', 'nv-context');
+ contextEnter.append('g').attr('class', 'nv-x nv-axis');
+ contextEnter.append('g').attr('class', 'nv-y nv-axis');
+ contextEnter.append('g').attr('class', 'nv-linesWrap');
+ contextEnter.append('g').attr('class', 'nv-brushBackground');
+ contextEnter.append('g').attr('class', 'nv-x nv-brush');
+
+ // Legend
+ if (showLegend) {
+ legend.width(availableWidth);
+
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight1 = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom - height2;
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ }
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ // Main Chart Component(s)
+ lines
+ .width(availableWidth)
+ .height(availableHeight1)
+ .color(
+ data
+ .map(function(d,i) {
+ return d.color || color(d, i);
+ })
+ .filter(function(d,i) {
+ return !data[i].disabled;
+ })
+ );
+
+ lines2
+ .defined(lines.defined())
+ .width(availableWidth)
+ .height(availableHeight2)
+ .color(
+ data
+ .map(function(d,i) {
+ return d.color || color(d, i);
+ })
+ .filter(function(d,i) {
+ return !data[i].disabled;
+ })
+ );
+
+ g.select('.nv-context')
+ .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')')
+
+ var contextLinesWrap = g.select('.nv-context .nv-linesWrap')
+ .datum(data.filter(function(d) { return !d.disabled }))
+
+ d3.transition(contextLinesWrap).call(lines2);
+
+ // Setup Main (Focus) Axes
+ xAxis
+ .scale(x)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight1, 0);
+
+ yAxis
+ .scale(y)
+ .ticks( nv.utils.calcTicksY(availableHeight1/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-focus .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + availableHeight1 + ')');
+
+ // Setup Brush
+ brush
+ .x(x2)
+ .on('brush', function() {
+ //When brushing, turn off transitions because chart needs to change immediately.
+ var oldTransition = chart.duration();
+ chart.duration(0);
+ onBrush();
+ chart.duration(oldTransition);
+ });
+
+ if (brushExtent) brush.extent(brushExtent);
+
+ var brushBG = g.select('.nv-brushBackground').selectAll('g')
+ .data([brushExtent || brush.extent()])
+
+ var brushBGenter = brushBG.enter()
+ .append('g');
+
+ brushBGenter.append('rect')
+ .attr('class', 'left')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight2);
+
+ brushBGenter.append('rect')
+ .attr('class', 'right')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight2);
+
+ var gBrush = g.select('.nv-x.nv-brush')
+ .call(brush);
+ gBrush.selectAll('rect')
+ //.attr('y', -5)
+ .attr('height', availableHeight2);
+ gBrush.selectAll('.resize').append('path').attr('d', resizePath);
+
+ onBrush();
+
+ // Setup Secondary (Context) Axes
+ x2Axis
+ .scale(x2)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight2, 0);
+
+ g.select('.nv-context .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y2.range()[0] + ')');
+ d3.transition(g.select('.nv-context .nv-x.nv-axis'))
+ .call(x2Axis);
+
+ y2Axis
+ .scale(y2)
+ .ticks( nv.utils.calcTicksY(availableHeight2/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ d3.transition(g.select('.nv-context .nv-y.nv-axis'))
+ .call(y2Axis);
+
+ g.select('.nv-context .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y2.range()[0] + ')');
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+ }
+ chart.update();
+ });
+
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
+
+ // Taken from crossfilter (http://square.github.com/crossfilter/)
+ function resizePath(d) {
+ var e = +(d == 'e'),
+ x = e ? 1 : -1,
+ y = availableHeight2 / 3;
+ return 'M' + (.5 * x) + ',' + y
+ + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
+ + 'V' + (2 * y - 6)
+ + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
+ + 'Z'
+ + 'M' + (2.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8)
+ + 'M' + (4.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8);
+ }
+
+
+ function updateBrushBG() {
+ if (!brush.empty()) brush.extent(brushExtent);
+ brushBG
+ .data([brush.empty() ? x2.domain() : brushExtent])
+ .each(function(d,i) {
+ var leftWidth = x2(d[0]) - x.range()[0],
+ rightWidth = x.range()[1] - x2(d[1]);
+ d3.select(this).select('.left')
+ .attr('width', leftWidth < 0 ? 0 : leftWidth);
+
+ d3.select(this).select('.right')
+ .attr('x', x2(d[1]))
+ .attr('width', rightWidth < 0 ? 0 : rightWidth);
+ });
+ }
+
+
+ function onBrush() {
+ brushExtent = brush.empty() ? null : brush.extent();
+ var extent = brush.empty() ? x2.domain() : brush.extent();
+
+ //The brush extent cannot be less than one. If it is, don't update the line chart.
+ if (Math.abs(extent[0] - extent[1]) <= 1) {
+ return;
+ }
+
+ dispatch.brush({extent: extent, brush: brush});
+
+
+ updateBrushBG();
+
+ // Update Main (Focus)
+ var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
+ .datum(
+ data
+ .filter(function(d) { return !d.disabled })
+ .map(function(d,i) {
+ return {
+ key: d.key,
+ area: d.area,
+ values: d.values.filter(function(d,i) {
+ return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
+ })
+ }
+ })
+ );
+ focusLinesWrap.transition().duration(transitionDuration).call(lines);
+
+
+ // Update Main (Focus) Axes
+ g.select('.nv-focus .nv-x.nv-axis').transition().duration(transitionDuration)
+ .call(xAxis);
+ g.select('.nv-focus .nv-y.nv-axis').transition().duration(transitionDuration)
+ .call(yAxis);
+ }
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ lines.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.legend = legend;
+ chart.lines = lines;
+ chart.lines2 = lines2;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.x2Axis = x2Axis;
+ chart.y2Axis = y2Axis;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ focusHeight: {get: function(){return height2;}, set: function(_){height2=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ // line color is handled above?
+ }},
+ interpolate: {get: function(){return lines.interpolate();}, set: function(_){
+ lines.interpolate(_);
+ lines2.interpolate(_);
+ }},
+ xTickFormat: {get: function(){return xAxis.xTickFormat();}, set: function(_){
+ xAxis.xTickFormat(_);
+ x2Axis.xTickFormat(_);
+ }},
+ yTickFormat: {get: function(){return yAxis.yTickFormat();}, set: function(_){
+ yAxis.yTickFormat(_);
+ y2Axis.yTickFormat(_);
+ }},
+ duration: {get: function(){return transitionDuration;}, set: function(_){
+ transitionDuration=_;
+ yAxis.duration(transitionDuration);
+ xAxis.duration(transitionDuration);
+ }},
+ x: {get: function(){return lines.x();}, set: function(_){
+ lines.x(_);
+ lines2.x(_);
+ }},
+ y: {get: function(){return lines.y();}, set: function(_){
+ lines.y(_);
+ lines2.y(_);
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, lines);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.models.multiBar = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , x = d3.scale.ordinal()
+ , y = d3.scale.linear()
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
+ , clipEdge = true
+ , stacked = false
+ , stackOffset = 'zero' // options include 'silhouette', 'wiggle', 'expand', 'zero', or a custom function
+ , color = nv.utils.defaultColor()
+ , hideable = false
+ , barColor = null // adding the ability to set the color for each rather than the whole group
+ , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
+ , duration = 500
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , groupSpacing = 0.1
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var x0, y0 //used to store previous scales
+ , renderWatch = nv.utils.renderWatch(dispatch, duration)
+ ;
+
+ var last_datalength = 0;
+
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ // This function defines the requirements for render complete
+ var endFn = function(d, i) {
+ if (d.series === data.length - 1 && i === data[0].values.length - 1)
+ return true;
+ return false;
+ };
+
+ if(hideable && data.length) hideable = [{
+ values: data[0].values.map(function(d) {
+ return {
+ x: d.x,
+ y: 0,
+ series: d.series,
+ size: 0.01
+ };}
+ )}];
+
+ if (stacked)
+ data = d3.layout.stack()
+ .offset(stackOffset)
+ .values(function(d){ return d.values })
+ .y(getY)
+ (!data.length && hideable ? hideable : data);
+
+
+ //add series index to each data point for reference
+ data.forEach(function(series, i) {
+ series.values.forEach(function(point) {
+ point.series = i;
+ });
+ });
+
+ // HACK for negative value stacking
+ if (stacked)
+ data[0].values.map(function(d,i) {
+ var posBase = 0, negBase = 0;
+ data.map(function(d) {
+ var f = d.values[i]
+ f.size = Math.abs(f.y);
+ if (f.y<0) {
+ f.y1 = negBase;
+ negBase = negBase - f.size;
+ } else
+ {
+ f.y1 = f.size + posBase;
+ posBase = posBase + f.size;
+ }
+ });
+ });
+
+ // Setup Scales
+ // remap and flatten the data for use in calculating the scales' domains
+ var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
+ data.map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
+ })
+ });
+
+ x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
+ .rangeBands(xRange || [0, availableWidth], groupSpacing);
+
+ y.domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 : d.y1 + d.y ) : d.y }).concat(forceY)))
+ .range(yRange || [availableHeight, 0]);
+
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
+
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
+
+ x0 = x0 || x;
+ y0 = y0 || y;
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-multibar').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibar');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g')
+
+ gEnter.append('g').attr('class', 'nv-groups');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-edge-clip-' + id)
+ .append('rect');
+ wrap.select('#nv-edge-clip-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
+
+ g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
+
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d,i) { return i });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
+
+ var exitTransition = renderWatch
+ .transition(groups.exit().selectAll('rect.nv-bar'), 'multibarExit', Math.min(100, duration))
+ .attr('y', function(d) { return (stacked ? y0(d.y0) : y0(0)) || 0 })
+ .attr('height', 0)
+ .remove();
+ if (exitTransition.delay)
+ exitTransition.delay(function(d,i) {
+ var delay = i * (duration / (last_datalength + 1)) - i;
+ return delay;
+ });
+ groups
+ .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
+ .classed('hover', function(d) { return d.hover })
+ .style('fill', function(d,i){ return color(d, i) })
+ .style('stroke', function(d,i){ return color(d, i) });
+ groups
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', 0.75);
+
+ var bars = groups.selectAll('rect.nv-bar')
+ .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values });
+ bars.exit().remove();
+
+ var barsEnter = bars.enter().append('rect')
+ .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
+ .attr('x', function(d,i,j) {
+ return stacked ? 0 : (j * x.rangeBand() / data.length )
+ })
+ .attr('y', function(d) { return y0(stacked ? d.y0 : 0) || 0 })
+ .attr('height', 0)
+ .attr('width', x.rangeBand() / (stacked ? 1 : data.length) )
+ .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
+ ;
+ bars
+ .style('fill', function(d,i,j){ return color(d, j, i); })
+ .style('stroke', function(d,i,j){ return color(d, j, i); })
+ .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ })
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ })
+ .on('click', function(d,i) {
+ dispatch.elementClick({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ d3.event.stopPropagation();
+ })
+ .on('dblclick', function(d,i) {
+ dispatch.elementDblClick({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ d3.event.stopPropagation();
+ });
+ bars
+ .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
+ .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
+
+ if (barColor) {
+ if (!disabled) disabled = data.map(function() { return true });
+ bars
+ .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
+ .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
+ }
+
+ var barSelection =
+ bars.watchTransition(renderWatch, 'multibar', Math.min(250, duration))
+ .delay(function(d,i) {
+ return i * duration / data[0].values.length;
+ });
+ if (stacked)
+ barSelection
+ .attr('y', function(d,i) {
+ return y((stacked ? d.y1 : 0));
+ })
+ .attr('height', function(d,i) {
+ return Math.max(Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0))),1);
+ })
+ .attr('x', function(d,i) {
+ return stacked ? 0 : (d.series * x.rangeBand() / data.length )
+ })
+ .attr('width', x.rangeBand() / (stacked ? 1 : data.length) );
+ else
+ barSelection
+ .attr('x', function(d,i) {
+ return d.series * x.rangeBand() / data.length
+ })
+ .attr('width', x.rangeBand() / data.length)
+ .attr('y', function(d,i) {
+ return getY(d,i) < 0 ?
+ y(0) :
+ y(0) - y(getY(d,i)) < 1 ?
+ y(0) - 1 :
+ y(getY(d,i)) || 0;
+ })
+ .attr('height', function(d,i) {
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0;
+ });
+
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
+
+ // keep track of the last data value length for transition calculations
+ if (data[0] && data[0].values) {
+ last_datalength = data[0].values.length;
+ }
+
+ });
+
+ renderWatch.renderEnd('multibar immediate');
+
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ stacked: {get: function(){return stacked;}, set: function(_){stacked=_;}},
+ stackOffset: {get: function(){return stackOffset;}, set: function(_){stackOffset=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ disabled: {get: function(){return disabled;}, set: function(_){disabled=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ hideable: {get: function(){return hideable;}, set: function(_){hideable=_;}},
+ groupSpacing:{get: function(){return groupSpacing;}, set: function(_){groupSpacing=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ barColor: {get: function(){return barColor;}, set: function(_){
+ barColor = nv.utils.getColor(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.models.multiBarChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var multibar = nv.models.multiBar()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , controls = nv.models.legend()
+ ;
+
+ var margin = {top: 30, right: 20, bottom: 50, left: 60}
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor()
+ , showControls = true
+ , controlLabels = {}
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , reduceXTicks = true // if false a tick will show for every data point
+ , staggerLabels = false
+ , rotateLabels = 0
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' ' +
+ '
' + y + ' on ' + x + '
'
+ }
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = "No Data Available."
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
+ , controlWidth = function() { return showControls ? 180 : 0 }
+ , duration = 250
+ ;
+
+ state.stacked = false // DEPRECATED Maintained for backward compatibility
+
+ multibar
+ .stacked(false)
+ ;
+ xAxis
+ .orient('bottom')
+ .tickPadding(7)
+ .highlightZero(true)
+ .showMaxMin(false)
+ .tickFormat(function(d) { return d })
+ ;
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ .tickFormat(d3.format(',.1f'))
+ ;
+
+ controls.updateState(false);
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var renderWatch = nv.utils.renderWatch(dispatch);
+ var stacked = false;
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
+ y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
+ };
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled }),
+ stacked: stacked
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.stacked !== undefined)
+ stacked = state.stacked;
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(multibar);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ chart.update = function() {
+ if (duration === 0)
+ container.call(chart);
+ else
+ container.transition()
+ .duration(duration)
+ .call(chart);
+ };
+ chart.container = this;
+
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ x = multibar.xScale();
+ y = multibar.yScale();
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-multiBarWithLegend').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarWithLegend').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-controlsWrap');
+
+ // Legend
+ if (showLegend) {
+ legend.width(availableWidth - controlWidth());
+
+ if (multibar.barColor())
+ data.forEach(function(series,i) {
+ series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
+ });
+
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
+ }
+
+ // Controls
+ if (showControls) {
+ var controlsData = [
+ { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
+ { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
+ ];
+
+ controls.width(controlWidth()).color(['#444', '#444', '#444']);
+ g.select('.nv-controlsWrap')
+ .datum(controlsData)
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ .call(controls);
+ }
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
+
+ // Main Chart Component(s)
+ multibar
+ .disabled(data.map(function(series) { return series.disabled }))
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+
+
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }));
+
+ barsWrap.call(multibar);
+
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
+
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ g.select('.nv-x.nv-axis')
+ .call(xAxis);
+
+ var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');
+
+ xTicks
+ .selectAll('line, text')
+ .style('opacity', 1)
+
+ if (staggerLabels) {
+ var getTranslate = function(x,y) {
+ return "translate(" + x + "," + y + ")";
+ };
+
+ var staggerUp = 5, staggerDown = 17; //pixels to stagger by
+ // Issue #140
+ xTicks
+ .selectAll("text")
+ .attr('transform', function(d,i,j) {
+ return getTranslate(0, (j % 2 == 0 ? staggerUp : staggerDown));
+ });
+
+ var totalInBetweenTicks = d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;
+ g.selectAll(".nv-x.nv-axis .nv-axisMaxMin text")
+ .attr("transform", function(d,i) {
+ return getTranslate(0, (i === 0 || totalInBetweenTicks % 2 !== 0) ? staggerDown : staggerUp);
+ });
+ }
+
+ if (reduceXTicks)
+ xTicks
+ .filter(function(d,i) {
+ return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0;
+ })
+ .selectAll('text, line')
+ .style('opacity', 0);
+
+ if(rotateLabels)
+ xTicks
+ .selectAll('.tick text')
+ .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
+ .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');
+
+ g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
+ .style('opacity', 1);
+ }
+
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-y.nv-axis')
+ .call(yAxis);
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ controls.dispatch.on('legendClick', function(d,i) {
+ if (!d.disabled) return;
+ controlsData = controlsData.map(function(s) {
+ s.disabled = true;
+ return s;
+ });
+ d.disabled = false;
+
+ switch (d.key) {
+ case 'Grouped':
+ multibar.stacked(false);
+ break;
+ case 'Stacked':
+ multibar.stacked(true);
+ break;
+ }
+
+ state.stacked = multibar.stacked();
+ dispatch.stateChange(state);
+
+ chart.update();
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode)
+ });
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ if (typeof e.stacked !== 'undefined') {
+ multibar.stacked(e.stacked);
+ state.stacked = e.stacked;
+ stacked = e.stacked;
+ }
+
+ chart.update();
+ });
+ });
+
+ renderWatch.renderEnd('multibarchart immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ multibar.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ multibar.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.multibar = multibar;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.state = state;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
+ controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ reduceXTicks: {get: function(){return reduceXTicks;}, set: function(_){reduceXTicks=_;}},
+ rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
+ staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ multibar.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ renderWatch.reset(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( rightAlignYAxis ? 'right' : 'left');
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, multibar);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.models.multiBarHorizontal = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , x = d3.scale.ordinal()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , getYerr = function(d) { return d.yErr }
+ , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
+ , color = nv.utils.defaultColor()
+ , barColor = null // adding the ability to set the color for each rather than the whole group
+ , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
+ , stacked = false
+ , showValues = false
+ , showBarLabels = false
+ , valuePadding = 60
+ , valueFormat = d3.format(',.2f')
+ , delay = 1200
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , duration = 250
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout','renderEnd')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var x0, y0; //used to store previous scales
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ if (stacked)
+ data = d3.layout.stack()
+ .offset('zero')
+ .values(function(d){ return d.values })
+ .y(getY)
+ (data);
+
+ //add series index to each data point for reference
+ data.forEach(function(series, i) {
+ series.values.forEach(function(point) {
+ point.series = i;
+ });
+ });
+
+ // HACK for negative value stacking
+ if (stacked)
+ data[0].values.map(function(d,i) {
+ var posBase = 0, negBase = 0;
+ data.map(function(d) {
+ var f = d.values[i]
+ f.size = Math.abs(f.y);
+ if (f.y<0) {
+ f.y1 = negBase - f.size;
+ negBase = negBase - f.size;
+ } else
+ {
+ f.y1 = posBase;
+ posBase = posBase + f.size;
+ }
+ });
+ });
+
+ // Setup Scales
+ // remap and flatten the data for use in calculating the scales' domains
+ var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
+ data.map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
+ })
+ });
+
+ x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
+ .rangeBands(xRange || [0, availableHeight], .1);
+
+ y.domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 + d.y : d.y1 ) : d.y }).concat(forceY)))
+
+ if (showValues && !stacked)
+ y.range(yRange || [(y.domain()[0] < 0 ? valuePadding : 0), availableWidth - (y.domain()[1] > 0 ? valuePadding : 0) ]);
+ else
+ y.range(yRange || [0, availableWidth]);
+
+ x0 = x0 || x;
+ y0 = y0 || d3.scale.linear().domain(y.domain()).range([y(0),y(0)]);
+
+ // Setup containers and skeleton of chart
+ var wrap = d3.select(this).selectAll('g.nv-wrap.nv-multibarHorizontal').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibarHorizontal');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-groups');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d,i) { return i });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
+ groups.exit().watchTransition(renderWatch, 'multibarhorizontal: exit groups')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6)
+ .remove();
+ groups
+ .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
+ .classed('hover', function(d) { return d.hover })
+ .style('fill', function(d,i){ return color(d, i) })
+ .style('stroke', function(d,i){ return color(d, i) });
+ groups.watchTransition(renderWatch, 'multibarhorizontal: groups')
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', .75);
+
+ var bars = groups.selectAll('g.nv-bar')
+ .data(function(d) { return d.values });
+ bars.exit().remove();
+
+ var barsEnter = bars.enter().append('g')
+ .attr('transform', function(d,i,j) {
+ return 'translate(' + y0(stacked ? d.y0 : 0) + ',' + (stacked ? 0 : (j * x.rangeBand() / data.length ) + x(getX(d,i))) + ')'
+ });
+
+ barsEnter.append('rect')
+ .attr('width', 0)
+ .attr('height', x.rangeBand() / (stacked ? 1 : data.length) )
+
+ bars
+ .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [ y(getY(d,i) + (stacked ? d.y0 : 0)), x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length) ],
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ })
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ })
+ .on('click', function(d,i) {
+ dispatch.elementClick({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ d3.event.stopPropagation();
+ })
+ .on('dblclick', function(d,i) {
+ dispatch.elementDblClick({
+ value: getY(d,i),
+ point: d,
+ series: data[d.series],
+ pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
+ pointIndex: i,
+ seriesIndex: d.series,
+ e: d3.event
+ });
+ d3.event.stopPropagation();
+ });
+
+ if (getYerr(data[0],0)) {
+ barsEnter.append('polyline');
+
+ bars.select('polyline')
+ .attr('fill', 'none')
+ .attr('points', function(d,i) {
+ var xerr = getYerr(d,i)
+ , mid = 0.8 * x.rangeBand() / ((stacked ? 1 : data.length) * 2);
+ xerr = xerr.length ? xerr : [-Math.abs(xerr), Math.abs(xerr)];
+ xerr = xerr.map(function(e) { return y(e) - y(0); });
+ var a = [[xerr[0],-mid], [xerr[0],mid], [xerr[0],0], [xerr[1],0], [xerr[1],-mid], [xerr[1],mid]];
+ return a.map(function (path) { return path.join(',') }).join(' ');
+ })
+ .attr('transform', function(d,i) {
+ var mid = x.rangeBand() / ((stacked ? 1 : data.length) * 2);
+ return 'translate(' + (getY(d,i) < 0 ? 0 : y(getY(d,i)) - y(0)) + ', ' + mid + ')'
+ });
+ }
+
+ barsEnter.append('text');
+
+ if (showValues && !stacked) {
+ bars.select('text')
+ .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' })
+ .attr('y', x.rangeBand() / (data.length * 2))
+ .attr('dy', '.32em')
+ .html(function(d,i) {
+ var t = valueFormat(getY(d,i))
+ , yerr = getYerr(d,i);
+ if (yerr === undefined)
+ return t;
+ if (!yerr.length)
+ return t + '±' + valueFormat(Math.abs(yerr));
+ return t + '+' + valueFormat(Math.abs(yerr[1])) + '-' + valueFormat(Math.abs(yerr[0]));
+ });
+ bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
+ .select('text')
+ .attr('x', function(d,i) { return getY(d,i) < 0 ? -4 : y(getY(d,i)) - y(0) + 4 })
+ } else {
+ bars.selectAll('text').text('');
+ }
+
+ if (showBarLabels && !stacked) {
+ barsEnter.append('text').classed('nv-bar-label',true);
+ bars.select('text.nv-bar-label')
+ .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'start' : 'end' })
+ .attr('y', x.rangeBand() / (data.length * 2))
+ .attr('dy', '.32em')
+ .text(function(d,i) { return getX(d,i) });
+ bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
+ .select('text.nv-bar-label')
+ .attr('x', function(d,i) { return getY(d,i) < 0 ? y(0) - y(getY(d,i)) + 4 : -4 });
+ }
+ else {
+ bars.selectAll('text.nv-bar-label').text('');
+ }
+
+ bars
+ .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
+
+ if (barColor) {
+ if (!disabled) disabled = data.map(function() { return true });
+ bars
+ .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
+ .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
+ }
+
+ if (stacked)
+ bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
+ .attr('transform', function(d,i) {
+ return 'translate(' + y(d.y1) + ',' + x(getX(d,i)) + ')'
+ })
+ .select('rect')
+ .attr('width', function(d,i) {
+ return Math.abs(y(getY(d,i) + d.y0) - y(d.y0))
+ })
+ .attr('height', x.rangeBand() );
+ else
+ bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
+ .attr('transform', function(d,i) {
+ //TODO: stacked must be all positive or all negative, not both?
+ return 'translate(' +
+ (getY(d,i) < 0 ? y(getY(d,i)) : y(0))
+ + ',' +
+ (d.series * x.rangeBand() / data.length
+ +
+ x(getX(d,i)) )
+ + ')'
+ })
+ .select('rect')
+ .attr('height', x.rangeBand() / data.length )
+ .attr('width', function(d,i) {
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)),1)
+ });
+
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
+
+ });
+
+ renderWatch.renderEnd('multibarHorizontal immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ yErr: {get: function(){return getYerr;}, set: function(_){getYerr=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ stacked: {get: function(){return stacked;}, set: function(_){stacked=_;}},
+ showValues: {get: function(){return showValues;}, set: function(_){showValues=_;}},
+ // this shows the group name, seems pointless?
+ //showBarLabels: {get: function(){return showBarLabels;}, set: function(_){showBarLabels=_;}},
+ disabled: {get: function(){return disabled;}, set: function(_){disabled=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
+ valuePadding: {get: function(){return valuePadding;}, set: function(_){valuePadding=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ barColor: {get: function(){return color;}, set: function(_){
+ barColor = nv.utils.getColor(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+nv.models.multiBarHorizontalChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var multibar = nv.models.multiBarHorizontal()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend().height(30)
+ , controls = nv.models.legend().height(30)
+ ;
+
+ var margin = {top: 30, right: 20, bottom: 50, left: 60}
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor()
+ , showControls = true
+ , controlLabels = {}
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , stacked = false
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' - ' + x + ' ' +
+ '
' + y + '
'
+ }
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = 'No Data Available.'
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState','renderEnd')
+ , controlWidth = function() { return showControls ? 180 : 0 }
+ , duration = 250
+ ;
+
+ state.stacked = false; // DEPRECATED Maintained for backward compatibility
+
+ multibar
+ .stacked(stacked)
+ ;
+ xAxis
+ .orient('left')
+ .tickPadding(5)
+ .highlightZero(false)
+ .showMaxMin(false)
+ .tickFormat(function(d) { return d })
+ ;
+ yAxis
+ .orient('bottom')
+ .tickFormat(d3.format(',.1f'))
+ ;
+
+ controls.updateState(false);
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
+ y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
+ };
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled }),
+ stacked: stacked
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.stacked !== undefined)
+ stacked = state.stacked;
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
+
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(multibar);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ chart.update = function() { container.transition().duration(duration).call(chart) };
+ chart.container = this;
+
+ stacked = multibar.stacked();
+
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ x = multibar.xScale();
+ y = multibar.yScale();
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-multiBarHorizontalChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarHorizontalChart').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis')
+ .append('g').attr('class', 'nv-zeroLine')
+ .append('line');
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-controlsWrap');
+
+ // Legend
+ if (showLegend) {
+ legend.width(availableWidth - controlWidth());
+
+ if (multibar.barColor())
+ data.forEach(function(series,i) {
+ series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
+ });
+
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
+ }
+
+ // Controls
+ if (showControls) {
+ var controlsData = [
+ { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
+ { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
+ ];
+
+ controls.width(controlWidth()).color(['#444', '#444', '#444']);
+ g.select('.nv-controlsWrap')
+ .datum(controlsData)
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ .call(controls);
+ }
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ // Main Chart Component(s)
+ multibar
+ .disabled(data.map(function(series) { return series.disabled }))
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }));
+
+ barsWrap.transition().call(multibar);
+
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ .ticks( nv.utils.calcTicksY(availableHeight/24, data) )
+ .tickSize(-availableWidth, 0);
+
+ g.select('.nv-x.nv-axis').call(xAxis);
+
+ var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
+
+ xTicks
+ .selectAll('line, text');
+ }
+
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize( -availableHeight, 0);
+
+ g.select('.nv-y.nv-axis')
+ .attr('transform', 'translate(0,' + availableHeight + ')');
+ g.select('.nv-y.nv-axis').call(yAxis);
+ }
+
+ // Zero line
+ g.select(".nv-zeroLine line")
+ .attr("x1", y(0))
+ .attr("x2", y(0))
+ .attr("y1", 0)
+ .attr("y2", -availableHeight)
+ ;
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ controls.dispatch.on('legendClick', function(d,i) {
+ if (!d.disabled) return;
+ controlsData = controlsData.map(function(s) {
+ s.disabled = true;
+ return s;
+ });
+ d.disabled = false;
+
+ switch (d.key) {
+ case 'Grouped':
+ multibar.stacked(false);
+ break;
+ case 'Stacked':
+ multibar.stacked(true);
+ break;
+ }
+
+ state.stacked = multibar.stacked();
+ dispatch.stateChange(state);
+ stacked = multibar.stacked();
+
+ chart.update();
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ if (typeof e.stacked !== 'undefined') {
+ multibar.stacked(e.stacked);
+ state.stacked = e.stacked;
+ stacked = e.stacked;
+ }
+
+ chart.update();
+ });
+ });
+ renderWatch.renderEnd('multibar horizontal chart immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ multibar.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ multibar.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.multibar = multibar;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.state = state;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
+ controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ multibar.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, multibar);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+nv.models.multiChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 30, right: 20, bottom: 50, left: 60},
+ color = nv.utils.defaultColor(),
+ width = null,
+ height = null,
+ showLegend = true,
+ tooltips = true,
+ tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' ' +
+ '
' + y + ' at ' + x + '
'
+ },
+ x,
+ y,
+ noData = 'No Data Available.',
+ yDomain1,
+ yDomain2,
+ getX = function(d) { return d.x },
+ getY = function(d) { return d.y},
+ interpolate = 'monotone'
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var x = d3.scale.linear(),
+ yScale1 = d3.scale.linear(),
+ yScale2 = d3.scale.linear(),
+
+ lines1 = nv.models.line().yScale(yScale1),
+ lines2 = nv.models.line().yScale(yScale2),
+
+ bars1 = nv.models.multiBar().stacked(false).yScale(yScale1),
+ bars2 = nv.models.multiBar().stacked(false).yScale(yScale2),
+
+ stack1 = nv.models.stackedArea().yScale(yScale1),
+ stack2 = nv.models.stackedArea().yScale(yScale2),
+
+ xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5),
+ yAxis1 = nv.models.axis().scale(yScale1).orient('left'),
+ yAxis2 = nv.models.axis().scale(yScale2).orient('right'),
+
+ legend = nv.models.legend().height(30),
+ dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(lines1.x()(e.point, e.pointIndex)),
+ y = ((e.series.yAxis == 2) ? yAxis2 : yAxis1).tickFormat()(lines1.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, undefined, undefined, offsetElement.offsetParent);
+ };
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+
+ chart.update = function() { container.transition().call(chart); };
+ chart.container = this;
+
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ var dataLines1 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 1});
+ var dataLines2 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 2});
+ var dataBars1 = data.filter(function(d) {return d.type == 'bar' && d.yAxis == 1});
+ var dataBars2 = data.filter(function(d) {return d.type == 'bar' && d.yAxis == 2});
+ var dataStack1 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 1});
+ var dataStack2 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 2});
+
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1})
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: d.x, y: d.y }
+ })
+ });
+
+ var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2})
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: d.x, y: d.y }
+ })
+ });
+
+ x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
+ .range([0, availableWidth]);
+
+ var wrap = container.selectAll('g.wrap.multiChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g');
+
+ gEnter.append('g').attr('class', 'x axis');
+ gEnter.append('g').attr('class', 'y1 axis');
+ gEnter.append('g').attr('class', 'y2 axis');
+ gEnter.append('g').attr('class', 'lines1Wrap');
+ gEnter.append('g').attr('class', 'lines2Wrap');
+ gEnter.append('g').attr('class', 'bars1Wrap');
+ gEnter.append('g').attr('class', 'bars2Wrap');
+ gEnter.append('g').attr('class', 'stack1Wrap');
+ gEnter.append('g').attr('class', 'stack2Wrap');
+ gEnter.append('g').attr('class', 'legendWrap');
+
+ var g = wrap.select('g');
+
+ var color_array = data.map(function(d,i) {
+ return data[i].color || color(d, i);
+ });
+
+ if (showLegend) {
+ legend.color(color_array);
+ legend.width( availableWidth / 2 );
+
+ g.select('.legendWrap')
+ .datum(data.map(function(series) {
+ series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
+ series.key = series.originalKey + (series.yAxis == 1 ? '' : ' (right axis)');
+ return series;
+ }))
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ g.select('.legendWrap')
+ .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
+ }
+
+ lines1
+ .width(availableWidth)
+ .height(availableHeight)
+ .interpolate(interpolate)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'}));
+ lines2
+ .width(availableWidth)
+ .height(availableHeight)
+ .interpolate(interpolate)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'}));
+ bars1
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'}));
+ bars2
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'}));
+ stack1
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
+ stack2
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
+
+ g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ var lines1Wrap = g.select('.lines1Wrap')
+ .datum(
+ dataLines1.filter(function(d){return !d.disabled})
+ );
+ var bars1Wrap = g.select('.bars1Wrap')
+ .datum(
+ dataBars1.filter(function(d){return !d.disabled})
+ );
+ var stack1Wrap = g.select('.stack1Wrap')
+ .datum(
+ dataStack1.filter(function(d){return !d.disabled})
+ );
+
+ var lines2Wrap = g.select('.lines2Wrap')
+ .datum(
+ dataLines2.filter(function(d){return !d.disabled})
+ );
+ var bars2Wrap = g.select('.bars2Wrap')
+ .datum(
+ dataBars2.filter(function(d){return !d.disabled})
+ );
+ var stack2Wrap = g.select('.stack2Wrap')
+ .datum(
+ dataStack2.filter(function(d){return !d.disabled})
+ );
+
+ var extraValue1 = dataStack1.length ? dataStack1.map(function(a){return a.values}).reduce(function(a,b){
+ return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
+ }).concat([{x:0, y:0}]) : []
+ var extraValue2 = dataStack2.length ? dataStack2.map(function(a){return a.values}).reduce(function(a,b){
+ return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
+ }).concat([{x:0, y:0}]) : []
+
+ yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } ))
+ .range([0, availableHeight])
+
+ yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
+ .range([0, availableHeight])
+
+ lines1.yDomain(yScale1.domain())
+ bars1.yDomain(yScale1.domain())
+ stack1.yDomain(yScale1.domain())
+
+ lines2.yDomain(yScale2.domain())
+ bars2.yDomain(yScale2.domain())
+ stack2.yDomain(yScale2.domain())
+
+ if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);}
+ if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);}
+
+ if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);}
+ if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);}
+
+ if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);}
+ if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);}
+
+ xAxis
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
+
+ g.select('.x.axis')
+ .attr('transform', 'translate(0,' + availableHeight + ')');
+ d3.transition(g.select('.x.axis'))
+ .call(xAxis);
+
+ yAxis1
+ .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+
+ d3.transition(g.select('.y1.axis'))
+ .call(yAxis1);
+
+ yAxis2
+ .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ d3.transition(g.select('.y2.axis'))
+ .call(yAxis2);
+
+ g.select('.y1.axis')
+ .classed('nv-disabled', series1.length ? false : true)
+ .attr('transform', 'translate(' + x.range()[0] + ',0)');
+
+ g.select('.y2.axis')
+ .classed('nv-disabled', series2.length ? false : true)
+ .attr('transform', 'translate(' + x.range()[1] + ',0)');
+
+ legend.dispatch.on('stateChange', function(newState) {
+ chart.update();
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ lines1.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines1.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ lines2.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines2.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ bars1.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ bars1.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ bars2.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ bars2.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ stack1.dispatch.on('tooltipShow', function(e) {
+ //disable tooltips when value ~= 0
+ //// TODO: consider removing points from voronoi that have 0 value instead of this hack
+ if (!Math.round(stack1.y()(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
+ setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
+ return false;
+ }
+
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
+ dispatch.tooltipShow(e);
+ });
+
+ stack1.dispatch.on('tooltipHide', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ stack2.dispatch.on('tooltipShow', function(e) {
+ //disable tooltips when value ~= 0
+ //// TODO: consider removing points from voronoi that have 0 value instead of this hack
+ if (!Math.round(stack2.y()(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
+ setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
+ return false;
+ }
+
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
+ dispatch.tooltipShow(e);
+ });
+
+ stack2.dispatch.on('tooltipHide', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ lines1.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines1.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ lines2.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ lines2.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Global getters and setters
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.lines1 = lines1;
+ chart.lines2 = lines2;
+ chart.bars1 = bars1;
+ chart.bars2 = bars2;
+ chart.stack1 = stack1;
+ chart.stack2 = stack2;
+ chart.xAxis = xAxis;
+ chart.yAxis1 = yAxis1;
+ chart.yAxis2 = yAxis2;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ yDomain1: {get: function(){return yDomain1;}, set: function(_){yDomain1=_;}},
+ yDomain2: {get: function(){return yDomain2;}, set: function(_){yDomain2=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ x: {get: function(){return getX;}, set: function(_){
+ getX = _;
+ lines1.x(_);
+ bars1.x(_);
+ }},
+ y: {get: function(){return getY;}, set: function(_){
+ getY = _;
+ lines1.y(_);
+ bars1.y(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+
+nv.models.ohlcBar = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = null
+ , height = null
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , getOpen = function(d) { return d.open }
+ , getClose = function(d) { return d.close }
+ , getHigh = function(d) { return d.high }
+ , getLow = function(d) { return d.low }
+ , forceX = []
+ , forceY = []
+ , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
+ , clipEdge = true
+ , color = nv.utils.defaultColor()
+ , interactive = false
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right;
+ var availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ nv.utils.initSVG(container);
+
+ // Setup Scales
+ x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
+
+ if (padData)
+ x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range(xRange || [0, availableWidth]);
+
+ y.domain(yDomain || [
+ d3.min(data[0].values.map(getLow).concat(forceY)),
+ d3.max(data[0].values.map(getHigh).concat(forceY))
+ ]
+ ).range(yRange || [availableHeight, 0]);
+
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
+
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
+
+ // Setup containers and skeleton of chart
+ var wrap = d3.select(this).selectAll('g.nv-wrap.nv-ohlcBar').data([data[0].values]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-ohlcBar');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-ticks');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ container
+ .on('click', function(d,i) {
+ dispatch.chartClick({
+ data: d,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
+ });
+
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-chart-clip-path-' + id)
+ .append('rect');
+
+ wrap.select('#nv-chart-clip-path-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
+
+ g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
+
+ var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
+ .data(function(d) { return d });
+ ticks.exit().remove();
+
+ var ticksEnter = ticks.enter().append('path')
+ .attr('class', function(d,i,j) { return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i })
+ .attr('d', function(d,i) {
+ var w = (availableWidth / data[0].values.length) * .9;
+ return 'm0,0l0,'
+ + (y(getOpen(d,i))
+ - y(getHigh(d,i)))
+ + 'l'
+ + (-w/2)
+ + ',0l'
+ + (w/2)
+ + ',0l0,'
+ + (y(getLow(d,i)) - y(getOpen(d,i)))
+ + 'l0,'
+ + (y(getClose(d,i))
+ - y(getLow(d,i)))
+ + 'l'
+ + (w/2)
+ + ',0l'
+ + (-w/2)
+ + ',0z';
+ })
+ .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
+ .attr('fill', function(d,i) { return color[0]; })
+ .attr('stroke', function(d,i) { return color[0]; })
+ .attr('x', 0 )
+ .attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
+ .attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) });
+
+ // the bar colors are controlled by CSS currently
+ ticks.attr('class', function(d,i,j) {
+ return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i;
+ });
+
+ d3.transition(ticks)
+ .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
+ .attr('d', function(d,i) {
+ var w = (availableWidth / data[0].values.length) * .9;
+ return 'm0,0l0,'
+ + (y(getOpen(d,i))
+ - y(getHigh(d,i)))
+ + 'l'
+ + (-w/2)
+ + ',0l'
+ + (w/2)
+ + ',0l0,'
+ + (y(getLow(d,i))
+ - y(getOpen(d,i)))
+ + 'l0,'
+ + (y(getClose(d,i))
+ - y(getLow(d,i)))
+ + 'l'
+ + (w/2)
+ + ',0l'
+ + (-w/2)
+ + ',0z';
+ });
+ });
+
+ return chart;
+ }
+
+
+ //Create methods to allow outside functions to highlight a specific bar.
+ chart.highlightPoint = function(pointIndex, isHoverOver) {
+ chart.clearHighlights();
+ d3.select(".nv-ohlcBar .nv-tick-0-" + pointIndex)
+ .classed("hover", isHoverOver)
+ ;
+ };
+
+ chart.clearHighlights = function() {
+ d3.select(".nv-ohlcBar .nv-tick.hover")
+ .classed("hover", false)
+ ;
+ };
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ padData: {get: function(){return padData;}, set: function(_){padData=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
+
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ open: {get: function(){return getOpen();}, set: function(_){getOpen=_;}},
+ close: {get: function(){return getClose();}, set: function(_){getClose=_;}},
+ high: {get: function(){return getHigh;}, set: function(_){getHigh=_;}},
+ low: {get: function(){return getLow;}, set: function(_){getLow=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top != undefined ? _.top : margin.top;
+ margin.right = _.right != undefined ? _.right : margin.right;
+ margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom;
+ margin.left = _.left != undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+ return chart;
+};
+
+// Code adapted from Jason Davies' "Parallel Coordinates"
+// http://bl.ocks.org/jasondavies/1341281
+
+nv.models.parallelCoordinates = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 30, right: 10, bottom: 10, left: 10}
+ , width = null
+ , height = null
+ , x = d3.scale.ordinal()
+ , y = {}
+ , dimensions = []
+ , color = nv.utils.defaultColor()
+ , filters = []
+ , active = []
+ , dispatch = d3.dispatch('brush')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right;
+ var availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ nv.utils.initSVG(container);
+
+ active = data; //set all active before first brush call
+
+ //This is a placeholder until this chart is made resizeable
+ chart.update = function() { };
+
+ // Setup Scales
+ x.rangePoints([0, availableWidth], 1).domain(dimensions);
+
+ // Extract the list of dimensions and create a scale for each.
+ dimensions.forEach(function(d) {
+ y[d] = d3.scale.linear()
+ .domain(d3.extent(data, function(p) { return +p[d]; }))
+ .range([availableHeight, 0]);
+
+ y[d].brush = d3.svg.brush().y(y[d]).on('brush', brush);
+
+ return d != 'name';
+ });
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-parallelCoordinates').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-parallelCoordinates');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-parallelCoordinatesWrap');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ var line = d3.svg.line(),
+ axis = d3.svg.axis().orient('left'),
+ background,
+ foreground;
+
+ // Add grey background lines for context.
+ background = gEnter.append('g')
+ .attr('class', 'background')
+ .selectAll('path')
+ .data(data)
+ .enter().append('path')
+ .attr('d', path)
+ ;
+
+ // Add blue foreground lines for focus.
+ foreground = gEnter.append('g')
+ .attr('class', 'foreground')
+ .selectAll('path')
+ .data(data)
+ .enter().append('path')
+ .attr('d', path)
+ .attr('stroke', color)
+ ;
+
+ // Add a group element for each dimension.
+ var dimension = g.selectAll('.dimension')
+ .data(dimensions)
+ .enter().append('g')
+ .attr('class', 'dimension')
+ .attr('transform', function(d) { return 'translate(' + x(d) + ',0)'; });
+
+ // Add an axis and title.
+ dimension.append('g')
+ .attr('class', 'axis')
+ .each(function(d) { d3.select(this).call(axis.scale(y[d])); })
+ .append('text')
+ .attr('text-anchor', 'middle')
+ .attr('y', -9)
+ .text(String);
+
+ // Add and store a brush for each axis.
+ dimension.append('g')
+ .attr('class', 'brush')
+ .each(function(d) { d3.select(this).call(y[d].brush); })
+ .selectAll('rect')
+ .attr('x', -8)
+ .attr('width', 16);
+
+ // Returns the path for a given data point.
+ function path(d) {
+ return line(dimensions.map(function(p) { return [x(p), y[p](d[p])]; }));
+ }
+
+ // Handles a brush event, toggling the display of foreground lines.
+ function brush() {
+ var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }),
+ extents = actives.map(function(p) { return y[p].brush.extent(); });
+
+ filters = []; //erase current filters
+ actives.forEach(function(d,i) {
+ filters[i] = {
+ dimension: d,
+ extent: extents[i]
+ }
+ });
+
+ active = []; //erase current active list
+ foreground.style('display', function(d) {
+ var isActive = actives.every(function(p, i) {
+ return extents[i][0] <= d[p] && d[p] <= extents[i][1];
+ });
+ if (isActive) active.push(d);
+ return isActive ? null : 'none';
+ });
+
+ dispatch.brush({
+ filters: filters,
+ active: active
+ });
+ }
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ dimensions: {get: function(){return dimensions;}, set: function(_){dimensions=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
+ margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
+ margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
+ margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+ return chart;
+};
+nv.models.pie = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 500
+ , height = 500
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , color = nv.utils.defaultColor()
+ , valueFormat = d3.format(',.2f')
+ , labelFormat = d3.format('%')
+ , showLabels = true
+ , pieLabelsOutside = true
+ , donutLabelsOutside = false
+ , labelType = "key"
+ , labelThreshold = .02 //if slice percentage is under this, don't show label
+ , donut = false
+ , title = false
+ , growOnHover = true
+ , titleOffset = 0
+ , labelSunbeamLayout = false
+ , startAngle = false
+ , padAngle = false
+ , endAngle = false
+ , cornerRadius = 0
+ , donutRatio = 0.5
+ , duration = 250
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
+ ;
+
+
+ //============================================================
+ // chart function
+ //------------------------------------------------------------
+
+ var renderWatch = nv.utils.renderWatch(dispatch);
+
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right
+ ,availableHeight = height - margin.top - margin.bottom
+ ,radius = Math.min(availableWidth, availableHeight) / 2
+ ,arcRadius = radius-(radius / 5)
+ ,container = d3.select(this)
+ ;
+ nv.utils.initSVG(container);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('.nv-wrap.nv-pie').data(data);
+ var wrapEnter = wrap.enter().append('g').attr('class','nvd3 nv-wrap nv-pie nv-chart-' + id);
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+ var g_pie = gEnter.append('g').attr('class', 'nv-pie');
+ gEnter.append('g').attr('class', 'nv-pieLabels');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ g.select('.nv-pie').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
+ g.select('.nv-pieLabels').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
+
+ //
+ container.on('click', function(d,i) {
+ dispatch.chartClick({
+ data: d,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
+ });
+
+
+ var arc = d3.svg.arc().outerRadius(arcRadius);
+ var arcOver = d3.svg.arc().outerRadius(arcRadius + 5);
+
+ if (startAngle) {
+ arc.startAngle(startAngle);
+ arcOver.startAngle(startAngle);
+ }
+ if (endAngle) {
+ arc.endAngle(endAngle);
+ arcOver.endAngle(endAngle);
+ }
+ if (donut) {
+ arc.innerRadius(radius * donutRatio);
+ arcOver.innerRadius(radius * donutRatio);
+ }
+
+ // Setup the Pie chart and choose the data element
+ var pie = d3.layout.pie()
+ .sort(null)
+ .value(function(d) { return d.disabled ? 0 : getY(d) });
+
+ // padAngle added in d3 3.5
+ if (pie.padAngle && padAngle) {
+ pie.padAngle(padAngle);
+ }
+
+ if (arc.cornerRadius && cornerRadius) {
+ arc.cornerRadius(cornerRadius);
+ arcOver.cornerRadius(cornerRadius);
+ }
+
+ // if title is specified and donut, put it in the middle
+ if (donut && title) {
+ var title_g = g_pie.append('g').attr('class', 'nv-pie');
+
+ title_g.append("text")
+ .style("text-anchor", "middle")
+ .attr('class', 'nv-pie-title')
+ .text(function (d) {
+ return title;
+ })
+ .attr("dy", "0.35em") // trick to vertically center text
+ .attr('transform', function(d, i) {
+ return 'translate(0, '+ titleOffset + ')';
+ });
+ }
+
+ var slices = wrap.select('.nv-pie').selectAll('.nv-slice').data(pie);
+ var pieLabels = wrap.select('.nv-pieLabels').selectAll('.nv-label').data(pie);
+
+ slices.exit().remove();
+ pieLabels.exit().remove();
+
+ var ae = slices.enter().append('g')
+ ae.attr('class', 'nv-slice')
+ ae.on('mouseover', function(d,i){
+ d3.select(this).classed('hover', true);
+ if (growOnHover) {
+ d3.select(this).select("path").transition()
+ .duration(70)
+ .attr("d", arcOver);
+ }
+ dispatch.elementMouseover({
+ label: getX(d.data),
+ value: getY(d.data),
+ point: d.data,
+ pointIndex: i,
+ pos: [d3.event.pageX, d3.event.pageY],
+ id: id,
+ color: d3.select(this).style("fill")
+ });
+ });
+ ae.on('mouseout', function(d,i){
+ d3.select(this).classed('hover', false);
+ if (growOnHover) {
+ d3.select(this).select("path").transition()
+ .duration(50)
+ .attr("d", arc);
+ }
+ dispatch.elementMouseout({
+ label: getX(d.data),
+ value: getY(d.data),
+ point: d.data,
+ index: i,
+ id: id
+ });
+ });
+
+ slices.attr('fill', function(d,i) { return color(d, i); })
+ slices.attr('stroke', function(d,i) { return color(d, i); });
+
+ var paths = ae.append('path').each(function(d) {
+ this._current = d;
+ });
+
+ paths.on('click', function(d,i) {
+ dispatch.elementClick({
+ label: getX(d.data),
+ value: getY(d.data),
+ point: d.data,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
+ d3.event.stopPropagation();
+ });
+ paths.on('dblclick', function(d,i) {
+ dispatch.elementDblClick({
+ label: getX(d.data),
+ value: getY(d.data),
+ point: d.data,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
+ d3.event.stopPropagation();
+ });
+ slices.select('path')
+ .transition()
+ .attr('d', arc)
+ .attrTween('d', arcTween);
+
+ if (showLabels) {
+ // This does the normal label
+ var labelsArc = d3.svg.arc().innerRadius(0);
+
+ if (pieLabelsOutside){
+ var labelsArc = arc;
+ }
+
+ if (donutLabelsOutside) {
+ labelsArc = d3.svg.arc().outerRadius(arc.outerRadius());
+ }
+
+ pieLabels.enter().append("g").classed("nv-label",true).each(function(d,i) {
+ var group = d3.select(this);
+
+ group.attr('transform', function(d) {
+ if (labelSunbeamLayout) {
+ d.outerRadius = arcRadius + 10; // Set Outer Coordinate
+ d.innerRadius = arcRadius + 15; // Set Inner Coordinate
+ var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
+ if ((d.startAngle+d.endAngle)/2 < Math.PI) {
+ rotateAngle -= 90;
+ } else {
+ rotateAngle += 90;
+ }
+ return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
+ } else {
+ d.outerRadius = radius + 10; // Set Outer Coordinate
+ d.innerRadius = radius + 15; // Set Inner Coordinate
+ return 'translate(' + labelsArc.centroid(d) + ')'
+ }
+ });
+
+ group.append('rect')
+ .style('stroke', '#fff')
+ .style('fill', '#fff')
+ .attr("rx", 3)
+ .attr("ry", 3);
+
+ group.append('text')
+ .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
+ .style('fill', '#000')
+
+ });
+
+ var labelLocationHash = {};
+ var avgHeight = 14;
+ var avgWidth = 140;
+ var createHashKey = function(coordinates) {
+ return Math.floor(coordinates[0]/avgWidth) * avgWidth + ',' + Math.floor(coordinates[1]/avgHeight) * avgHeight;
+ };
+
+ pieLabels.watchTransition(renderWatch,'pie labels').attr('transform', function(d) {
+ if (labelSunbeamLayout) {
+ d.outerRadius = arcRadius + 10; // Set Outer Coordinate
+ d.innerRadius = arcRadius + 15; // Set Inner Coordinate
+ var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
+ if ((d.startAngle+d.endAngle)/2 < Math.PI) {
+ rotateAngle -= 90;
+ } else {
+ rotateAngle += 90;
+ }
+ return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
+ } else {
+ d.outerRadius = radius + 10; // Set Outer Coordinate
+ d.innerRadius = radius + 15; // Set Inner Coordinate
+
+ /*
+ Overlapping pie labels are not good. What this attempts to do is, prevent overlapping.
+ Each label location is hashed, and if a hash collision occurs, we assume an overlap.
+ Adjust the label's y-position to remove the overlap.
+ */
+ var center = labelsArc.centroid(d);
+ if(d.value){
+ var hashKey = createHashKey(center);
+ if (labelLocationHash[hashKey]) {
+ center[1] -= avgHeight;
+ }
+ labelLocationHash[createHashKey(center)] = true;
+ }
+ return 'translate(' + center + ')'
+ }
+ });
+
+ pieLabels.select(".nv-label text")
+ .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
+ .text(function(d, i) {
+ var percent = (d.endAngle - d.startAngle) / (2 * Math.PI);
+ var labelTypes = {
+ "key" : getX(d.data),
+ "value": getY(d.data),
+ "percent": labelFormat(percent)
+ };
+ return (d.value && percent > labelThreshold) ? labelTypes[labelType] : '';
+ })
+ ;
+ }
+
+
+ // Computes the angle of an arc, converting from radians to degrees.
+ function angle(d) {
+ var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
+ return a > 90 ? a - 180 : a;
+ }
+
+ function arcTween(a) {
+ a.endAngle = isNaN(a.endAngle) ? 0 : a.endAngle;
+ a.startAngle = isNaN(a.startAngle) ? 0 : a.startAngle;
+ if (!donut) a.innerRadius = 0;
+ var i = d3.interpolate(this._current, a);
+ this._current = i(0);
+ return function(t) {
+ return arc(i(t));
+ };
+ }
+ });
+
+ renderWatch.renderEnd('pie immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=_;}},
+ title: {get: function(){return title;}, set: function(_){title=_;}},
+ titleOffset: {get: function(){return titleOffset;}, set: function(_){titleOffset=_;}},
+ labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_;}},
+ labelFormat: {get: function(){return labelFormat;}, set: function(_){labelFormat=_;}},
+ valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ endAngle: {get: function(){return endAngle;}, set: function(_){endAngle=_;}},
+ startAngle: {get: function(){return startAngle;}, set: function(_){startAngle=_;}},
+ padAngle: {get: function(){return padAngle;}, set: function(_){padAngle=_;}},
+ cornerRadius: {get: function(){return cornerRadius;}, set: function(_){cornerRadius=_;}},
+ donutRatio: {get: function(){return donutRatio;}, set: function(_){donutRatio=_;}},
+ pieLabelsOutside: {get: function(){return pieLabelsOutside;}, set: function(_){pieLabelsOutside=_;}},
+ donutLabelsOutside: {get: function(){return donutLabelsOutside;}, set: function(_){donutLabelsOutside=_;}},
+ labelSunbeamLayout: {get: function(){return labelSunbeamLayout;}, set: function(_){labelSunbeamLayout=_;}},
+ donut: {get: function(){return donut;}, set: function(_){donut=_;}},
+ growOnHover: {get: function(){return growOnHover;}, set: function(_){growOnHover=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
+ margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
+ margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
+ margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
+ }},
+ y: {get: function(){return getY;}, set: function(_){
+ getY=d3.functor(_);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color=nv.utils.getColor(_);
+ }},
+ labelType: {get: function(){return labelType;}, set: function(_){
+ labelType= _ || 'key';
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+ return chart;
+};
+nv.models.pieChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var pie = nv.models.pie();
+ var legend = nv.models.legend();
+
+ var margin = {top: 30, right: 20, bottom: 20, left: 20}
+ , width = null
+ , height = null
+ , showLegend = true
+ , color = nv.utils.defaultColor()
+ , tooltips = true
+ , tooltip = function(key, y, e, graph) {
+ return '
' + key + ' '
+ + '
' + y + '
';
+ }
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = "No Data Available."
+ , duration = 250
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState','renderEnd')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var showTooltip = function(e, offsetElement) {
+ var tooltipLabel = pie.x()(e.point);
+ var left = e.pos[0] + ( (offsetElement && offsetElement.offsetLeft) || 0 ),
+ top = e.pos[1] + ( (offsetElement && offsetElement.offsetTop) || 0),
+ y = pie.valueFormat()(pie.y()(e.point)),
+ content = tooltip(tooltipLabel, y, e, chart)
+ ;
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
+ };
+
+ var renderWatch = nv.utils.renderWatch(dispatch);
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled })
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined) {
+ data.forEach(function (series, i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ }
+ };
+
+ //============================================================
+ // Chart function
+ //------------------------------------------------------------
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(pie);
+
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ var that = this;
+ var availableWidth = (width || parseInt(container.style('width'), 10) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height'), 10) || 400)
+ - margin.top - margin.bottom
+ ;
+
+ chart.update = function() { container.transition().call(chart); };
+ chart.container = this;
+
+ state.setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-pieChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-pieChart').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-pieWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+
+ // Legend
+ if (showLegend) {
+ legend.width( availableWidth ).key(pie.x());
+
+ wrap.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')');
+ }
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ // Main Chart Component(s)
+ pie.width(availableWidth).height(availableHeight);
+ var pieWrap = g.select('.nv-pieWrap').datum([data]);
+ d3.transition(pieWrap).call(pie);
+
+ // Event Handling/Dispatching (in chart's scope)
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState) {
+ state[key] = newState[key];
+ }
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ pie.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+ state.disabled = e.disabled;
+ }
+ chart.update();
+ });
+
+ });
+
+ renderWatch.renderEnd('pieChart immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ pie.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.legend = legend;
+ chart.dispatch = dispatch;
+ chart.pie = pie;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ // use Object get/set functionality to map between vars and chart functions
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ // options that require extra logic in the setter
+ color: {get: function(){return color;}, set: function(_){
+ color = _;
+ legend.color(color);
+ pie.color(color);
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }}
+ });
+ nv.utils.inheritOptions(chart, pie);
+ nv.utils.initOptions(chart);
+ return chart;
+};
+
+nv.models.scatter = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor() // chooses color
+ , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , z = d3.scale.linear() //linear because d3.svg.shape.size is treated as area
+ , getX = function(d) { return d.x } // accessor to get the x value
+ , getY = function(d) { return d.y } // accessor to get the y value
+ , getSize = function(d) { return d.size || 1} // accessor to get the point size
+ , getShape = function(d) { return d.shape || 'circle' } // accessor to get point shape
+ , forceX = [] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
+ , forceY = [] // List of numbers to Force into the Y scale
+ , forceSize = [] // List of numbers to Force into the Size scale
+ , interactive = true // If true, plots a voronoi overlay for advanced point intersection
+ , pointActive = function(d) { return !d.notActive } // any points that return false will be filtered out
+ , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
+ , padDataOuter = .1 //outerPadding to imitate ordinal scale outer padding
+ , clipEdge = false // if true, masks points within x and y scale
+ , clipVoronoi = true // if true, masks each point with a circle... can turn off to slightly increase performance
+ , clipRadius = function() { return 25 } // function to get the radius for voronoi point clips
+ , xDomain = null // Override x domain (skips the calculation from data)
+ , yDomain = null // Override y domain
+ , xRange = null // Override x range
+ , yRange = null // Override y range
+ , sizeDomain = null // Override point size domain
+ , sizeRange = null
+ , singlePoint = false
+ , dispatch = d3.dispatch('elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
+ , useVoronoi = true
+ , duration = 250
+ ;
+
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var x0, y0, z0 // used to store previous scales
+ , timeoutID
+ , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
+ , renderWatch = nv.utils.renderWatch(dispatch, duration)
+ ;
+
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var container = d3.select(this);
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right;
+ var availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ nv.utils.initSVG(container);
+
+ //add series index to each data point for reference
+ data.forEach(function(series, i) {
+ series.values.forEach(function(point) {
+ point.series = i;
+ });
+ });
+
+ // Setup Scales
+ // remap and flatten the data for use in calculating the scales' domains
+ var seriesData = (xDomain && yDomain && sizeDomain) ? [] : // if we know xDomain and yDomain and sizeDomain, no need to calculate.... if Size is constant remember to set sizeDomain to speed up performance
+ d3.merge(
+ data.map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) }
+ })
+ })
+ );
+
+ x .domain(xDomain || d3.extent(seriesData.map(function(d) { return d.x; }).concat(forceX)))
+
+ if (padData && data[0])
+ x.range(xRange || [(availableWidth * padDataOuter + availableWidth) / (2 *data[0].values.length), availableWidth - availableWidth * (1 + padDataOuter) / (2 * data[0].values.length) ]);
+ //x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range(xRange || [0, availableWidth]);
+
+ y .domain(yDomain || d3.extent(seriesData.map(function(d) { return d.y }).concat(forceY)))
+ .range(yRange || [availableHeight, 0]);
+
+ z .domain(sizeDomain || d3.extent(seriesData.map(function(d) { return d.size }).concat(forceSize)))
+ .range(sizeRange || [16, 256]);
+
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ if (x.domain()[0] === x.domain()[1] || y.domain()[0] === y.domain()[1]) singlePoint = true;
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
+
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] - y.domain()[0] * 0.01, y.domain()[1] + y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
+
+ if ( isNaN(x.domain()[0])) {
+ x.domain([-1,1]);
+ }
+
+ if ( isNaN(y.domain()[0])) {
+ y.domain([-1,1]);
+ }
+
+ x0 = x0 || x;
+ y0 = y0 || y;
+ z0 = z0 || z;
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id + (singlePoint ? ' nv-single-point' : ''));
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-groups');
+ gEnter.append('g').attr('class', 'nv-point-paths');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-edge-clip-' + id)
+ .append('rect');
+
+ wrap.select('#nv-edge-clip-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', (availableHeight > 0) ? availableHeight : 0);
+
+ g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
+
+ function updateInteractiveLayer() {
+
+ if (!interactive) return false;
+
+ var eventElements;
+
+ var vertices = d3.merge(data.map(function(group, groupIndex) {
+ return group.values
+ .map(function(point, pointIndex) {
+ // *Adding noise to make duplicates very unlikely
+ // *Injecting series and point index for reference
+ /* *Adding a 'jitter' to the points, because there's an issue in d3.geom.voronoi.
+ */
+ var pX = getX(point,pointIndex);
+ var pY = getY(point,pointIndex);
+
+ return [x(pX)+ Math.random() * 1e-7,
+ y(pY)+ Math.random() * 1e-7,
+ groupIndex,
+ pointIndex, point]; //temp hack to add noise untill I think of a better way so there are no duplicates
+ })
+ .filter(function(pointArray, pointIndex) {
+ return pointActive(pointArray[4], pointIndex); // Issue #237.. move filter to after map, so pointIndex is correct!
+ })
+ })
+ );
+
+ //inject series and point index for reference into voronoi
+ if (useVoronoi === true) {
+
+ if(vertices.length < 3) {
+ // Issue #283 - Adding 2 dummy points to the voronoi b/c voronoi requires min 3 points to work
+ vertices.push([x.range()[0] - 20, y.range()[0] - 20, null, null]);
+ vertices.push([x.range()[1] + 20, y.range()[1] + 20, null, null]);
+ vertices.push([x.range()[0] - 20, y.range()[0] + 20, null, null]);
+ vertices.push([x.range()[1] + 20, y.range()[1] - 20, null, null]);
+ }
+
+ // keep voronoi sections from going more than 10 outside of graph
+ // to avoid overlap with other things like legend etc
+ var bounds = d3.geom.polygon([
+ [-10,-10],
+ [-10,height + 10],
+ [width + 10,height + 10],
+ [width + 10,-10]
+ ]);
+
+ var voronoi = d3.geom.voronoi(vertices).map(function(d, i) {
+ return {
+ 'data': bounds.clip(d),
+ 'series': vertices[i][2],
+ 'point': vertices[i][3]
+ }
+ });
+
+ // nuke all voronoi paths on reload and recreate them
+ wrap.select('.nv-point-paths').selectAll('path').remove();
+ var pointPaths = wrap.select('.nv-point-paths').selectAll('path').data(voronoi);
+ pointPaths
+ .enter().append("svg:path")
+ .attr("d", function(d) {
+ if (!d || !d.data || d.data.length === 0)
+ return 'M 0 0';
+ else
+ return "M" + d.data.join(",") + "Z";
+ })
+ .attr("id", function(d,i) {
+ return "nv-path-"+i; })
+ .attr("clip-path", function(d,i) { return "url(#nv-clip-"+i+")"; })
+ ;
+ // chain these to above to see the voronoi elements (good for debugging)
+ //.style("fill", d3.rgb(230, 230, 230))
+ //.style('fill-opacity', 0.4)
+ //.style('stroke-opacity', 1)
+ //.style("stroke", d3.rgb(200,200,200));
+
+ if (clipVoronoi) {
+ // voronoi sections are already set to clip,
+ // just create the circles with the IDs they expect
+ var clips = wrap.append("svg:g").attr("id", "nv-point-clips");
+ clips.selectAll("clipPath")
+ .data(vertices)
+ .enter().append("svg:clipPath")
+ .attr("id", function(d, i) { return "nv-clip-"+i;})
+ .append("svg:circle")
+ .attr('cx', function(d) { return d[0]; })
+ .attr('cy', function(d) { return d[1]; })
+ .attr('r', clipRadius);
+ }
+
+ var mouseEventCallback = function(d,mDispatch) {
+ if (needsUpdate) return 0;
+ var series = data[d.series];
+ if (typeof series === 'undefined') return;
+ var point = series.values[d.point];
+
+ mDispatch({
+ point: point,
+ series: series,
+ pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: d.point
+ });
+ };
+
+ pointPaths
+ .on('click', function(d) {
+ mouseEventCallback(d, dispatch.elementClick);
+ })
+ .on('dblclick', function(d) {
+ mouseEventCallback(d, dispatch.elementDblClick);
+ })
+ .on('mouseover', function(d) {
+ mouseEventCallback(d, dispatch.elementMouseover);
+ })
+ .on('mouseout', function(d, i) {
+ mouseEventCallback(d, dispatch.elementMouseout);
+ });
+
+ } else {
+ /*
+ // bring data in form needed for click handlers
+ var dataWithPoints = vertices.map(function(d, i) {
+ return {
+ 'data': d,
+ 'series': vertices[i][2],
+ 'point': vertices[i][3]
+ }
+ });
+ */
+
+ // add event handlers to points instead voronoi paths
+ wrap.select('.nv-groups').selectAll('.nv-group')
+ .selectAll('.nv-point')
+ //.data(dataWithPoints)
+ //.style('pointer-events', 'auto') // recativate events, disabled by css
+ .on('click', function(d,i) {
+ //nv.log('test', d, i);
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
+
+ dispatch.elementClick({
+ point: point,
+ series: series,
+ pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: i
+ });
+ })
+ .on('mouseover', function(d,i) {
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
+
+ dispatch.elementMouseover({
+ point: point,
+ series: series,
+ pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: i
+ });
+ })
+ .on('mouseout', function(d,i) {
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
+
+ dispatch.elementMouseout({
+ point: point,
+ series: series,
+ seriesIndex: d.series,
+ pointIndex: i
+ });
+ });
+ }
+
+ needsUpdate = false;
+ }
+
+ needsUpdate = true;
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d) { return d.key });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
+ groups.exit()
+ .remove();
+ groups
+ .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
+ .classed('hover', function(d) { return d.hover });
+ groups.watchTransition(renderWatch, 'scatter: groups')
+ .style('fill', function(d,i) { return color(d, i) })
+ .style('stroke', function(d,i) { return color(d, i) })
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', .5);
+
+ // create the points
+ var points = groups.selectAll('path.nv-point')
+ .data(function(d) { return d.values });
+ points.enter().append('path')
+ .style('fill', function (d,i) { return d.color })
+ .style('stroke', function (d,i) { return d.color })
+ .attr('transform', function(d,i) {
+ return 'translate(' + x0(getX(d,i)) + ',' + y0(getY(d,i)) + ')'
+ })
+ .attr('d',
+ nv.utils.symbol()
+ .type(getShape)
+ .size(function(d,i) { return z(getSize(d,i)) })
+ );
+ points.exit().remove();
+ groups.exit().selectAll('path.nv-point')
+ .watchTransition(renderWatch, 'scatter exit')
+ .attr('transform', function(d,i) {
+ return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
+ })
+ .remove();
+ points.each(function(d,i) {
+ d3.select(this)
+ .classed('nv-point', true)
+ .classed('nv-point-' + i, true)
+ .classed('hover',false)
+ ;
+ });
+ points
+ .watchTransition(renderWatch, 'scatter points')
+ .attr('transform', function(d,i) {
+ //nv.log(d,i,getX(d,i), x(getX(d,i)));
+ return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
+ })
+ .attr('d',
+ nv.utils.symbol()
+ .type(getShape)
+ .size(function(d,i) { return z(getSize(d,i)) })
+ );
+
+ // Delay updating the invisible interactive layer for smoother animation
+ clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer
+ timeoutID = setTimeout(updateInteractiveLayer, 300);
+ //updateInteractiveLayer();
+
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
+ z0 = z.copy();
+
+ });
+ renderWatch.renderEnd('scatter immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ // utility function calls provided by this chart
+ chart._calls = new function() {
+ this.clearHighlights = function () {
+ d3.selectAll(".nv-chart-" + id + " .nv-point.hover").classed("hover", false);
+ return null;
+ };
+ this.highlightPoint = function (seriesIndex, pointIndex, isHoverOver) {
+ d3.select(".nv-chart-" + id + " .nv-series-" + seriesIndex + " .nv-point-" + pointIndex)
+ .classed("hover", isHoverOver);
+ };
+ };
+
+ // trigger calls from events too
+ dispatch.on('elementMouseover.point', function(d) {
+ if (interactive) chart._calls.highlightPoint(d.seriesIndex,d.pointIndex,true);
+ });
+
+ dispatch.on('elementMouseout.point', function(d) {
+ if (interactive) chart._calls.highlightPoint(d.seriesIndex,d.pointIndex,false);
+ });
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ pointScale: {get: function(){return z;}, set: function(_){z=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ pointDomain: {get: function(){return sizeDomain;}, set: function(_){sizeDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ pointRange: {get: function(){return sizeRange;}, set: function(_){sizeRange=_;}},
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ forcePoint: {get: function(){return forceSize;}, set: function(_){forceSize=_;}},
+ interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
+ pointActive: {get: function(){return pointActive;}, set: function(_){pointActive=_;}},
+ padDataOuter: {get: function(){return padDataOuter;}, set: function(_){padDataOuter=_;}},
+ padData: {get: function(){return padData;}, set: function(_){padData=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ clipVoronoi: {get: function(){return clipVoronoi;}, set: function(_){clipVoronoi=_;}},
+ clipRadius: {get: function(){return clipRadius;}, set: function(_){clipRadius=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+
+
+ // simple functor options
+ x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
+ y: {get: function(){return getY;}, set: function(_){getY = d3.functor(_);}},
+ pointSize: {get: function(){return getSize;}, set: function(_){getSize = d3.functor(_);}},
+ pointShape: {get: function(){return getShape;}, set: function(_){getShape = d3.functor(_);}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ useVoronoi: {get: function(){return useVoronoi;}, set: function(_){
+ useVoronoi = _;
+ if (useVoronoi === false) {
+ clipVoronoi = false;
+ }
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+ return chart;
+};
+
+nv.models.scatterChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var scatter = nv.models.scatter()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , distX = nv.models.distribution()
+ , distY = nv.models.distribution()
+ ;
+
+ var margin = {top: 30, right: 20, bottom: 50, left: 75}
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor()
+ , x = scatter.xScale()
+ , y = scatter.yScale()
+ , showDistX = false
+ , showDistY = false
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , tooltips = true
+ , tooltipX = function(key, x, y) { return '
' + x + ' ' }
+ , tooltipY = function(key, x, y) { return '
' + y + ' ' }
+ , tooltip = function(key, x, y, date) { return '
' + key + ' '
+ + '
' + date + '
' }
+ , state = nv.utils.state()
+ , defaultState = null
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
+ , noData = "No Data Available."
+ , duration = 250
+ ;
+
+ scatter
+ .xScale(x)
+ .yScale(y)
+ ;
+ xAxis
+ .orient('bottom')
+ .tickPadding(10)
+ ;
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ .tickPadding(10)
+ ;
+ distX
+ .axis('x')
+ ;
+ distY
+ .axis('y')
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var x0, y0
+ , renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ var showTooltip = function(e, offsetElement) {
+ //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ leftX = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ topX = y.range()[0] + margin.top + ( offsetElement.offsetTop || 0),
+ leftY = x.range()[0] + margin.left + ( offsetElement.offsetLeft || 0 ),
+ topY = e.pos[1] + ( offsetElement.offsetTop || 0),
+ xVal = xAxis.tickFormat()(scatter.x()(e.point, e.pointIndex)),
+ yVal = yAxis.tickFormat()(scatter.y()(e.point, e.pointIndex));
+
+ if( tooltipX != null )
+ nv.tooltip.show([leftX, topX], tooltipX(e.series.key, xVal, yVal, e, chart), 'n', 1, offsetElement, 'x-nvtooltip');
+ if( tooltipY != null )
+ nv.tooltip.show([leftY, topY], tooltipY(e.series.key, xVal, yVal, e, chart), 'e', 1, offsetElement, 'y-nvtooltip');
+ if( tooltip != null )
+ nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e.point.tooltip, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
+ };
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled })
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(scatter);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+ if (showDistX) renderWatch.models(distX);
+ if (showDistY) renderWatch.models(distY);
+
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ chart.update = function() {
+ if (duration === 0)
+ container.call(chart);
+ else
+ container.transition().duration(duration).call(chart);
+ };
+ chart.container = this;
+
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ renderWatch.renderEnd('scatter immediate');
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ x = scatter.xScale();
+ y = scatter.yScale();
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ // background for pointer events
+ gEnter.append('rect').attr('class', 'nvd3 nv-background').style("pointer-events","none");
+
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-scatterWrap');
+ gEnter.append('g').attr('class', 'nv-regressionLinesWrap');
+ gEnter.append('g').attr('class', 'nv-distWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
+
+ // Legend
+ if (showLegend) {
+ legend.width( availableWidth / 2 );
+
+ wrap.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + (availableWidth / 2) + ',' + (-margin.top) +')');
+ }
+
+ // Main Chart Component(s)
+ scatter
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+
+ wrap.select('.nv-scatterWrap')
+ .datum(data.filter(function(d) { return !d.disabled }))
+ .call(scatter);
+
+
+ wrap.select('.nv-regressionLinesWrap')
+ .attr('clip-path', 'url(#nv-edge-clip-' + scatter.id() + ')');
+
+ var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines')
+ .data(function (d) {
+ return d;
+ });
+
+ regWrap.enter().append('g').attr('class', 'nv-regLines');
+
+ var regLine = regWrap.selectAll('.nv-regLine')
+ .data(function (d) {
+ return [d]
+ });
+
+ regLine.enter()
+ .append('line').attr('class', 'nv-regLine')
+ .style('stroke-opacity', 0);
+
+ // don't add lines unless we have slope and intercept to use
+ regLine.filter(function(d) {
+ return d.intercept && d.slope;
+ })
+ .watchTransition(renderWatch, 'scatterPlusLineChart: regline')
+ .attr('x1', x.range()[0])
+ .attr('x2', x.range()[1])
+ .attr('y1', function (d, i) {
+ return y(x.domain()[0] * d.slope + d.intercept)
+ })
+ .attr('y2', function (d, i) {
+ return y(x.domain()[1] * d.slope + d.intercept)
+ })
+ .style('stroke', function (d, i, j) {
+ return color(d, j)
+ })
+ .style('stroke-opacity', function (d, i) {
+ return (d.disabled || typeof d.slope === 'undefined' || typeof d.intercept === 'undefined') ? 0 : 1
+ });
+
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ .ticks( xAxis.ticks() ? xAxis.ticks() : nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize( -availableHeight , 0);
+
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')')
+ .call(xAxis);
+ }
+
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ .ticks( yAxis.ticks() ? yAxis.ticks() : nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-y.nv-axis')
+ .call(yAxis);
+ }
+
+
+ if (showDistX) {
+ distX
+ .getData(scatter.x())
+ .scale(x)
+ .width(availableWidth)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+ gEnter.select('.nv-distWrap').append('g')
+ .attr('class', 'nv-distributionX');
+ g.select('.nv-distributionX')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')')
+ .datum(data.filter(function(d) { return !d.disabled }))
+ .call(distX);
+ }
+
+ if (showDistY) {
+ distY
+ .getData(scatter.y())
+ .scale(y)
+ .width(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+ gEnter.select('.nv-distWrap').append('g')
+ .attr('class', 'nv-distributionY');
+ g.select('.nv-distributionY')
+ .attr('transform', 'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)')
+ .datum(data.filter(function(d) { return !d.disabled }))
+ .call(distY);
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+
+ scatter.dispatch.on('elementMouseover.tooltip', function(e) {
+ d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
+ .attr('y1', e.pos[1] - availableHeight);
+ d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
+ .attr('x2', e.pos[0] + distX.size());
+
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ chart.update();
+ });
+
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
+
+ });
+
+ renderWatch.renderEnd('scatter with line immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ scatter.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+
+ d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
+ .attr('y1', 0);
+ d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
+ .attr('x2', distY.size());
+ });
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.scatter = scatter;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.distX = distX;
+ chart.distY = distY;
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showDistX: {get: function(){return showDistX;}, set: function(_){showDistX=_;}},
+ showDistY: {get: function(){return showDistY;}, set: function(_){showDistY=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ tooltipXContent: {get: function(){return tooltipX;}, set: function(_){tooltipX=_;}},
+ tooltipYContent: {get: function(){return tooltipY;}, set: function(_){tooltipY=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ duration: {get: function(){return duration;}, set: function(_){duration=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ distX.color(color);
+ distY.color(color);
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, scatter);
+ nv.utils.initOptions(chart);
+ return chart;
+};
+
+nv.models.sparkline = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 2, right: 0, bottom: 2, left: 0}
+ , width = 400
+ , height = 32
+ , animate = true
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , color = nv.utils.getColor(['#000'])
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ ;
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ // Setup Scales
+ x .domain(xDomain || d3.extent(data, getX ))
+ .range(xRange || [0, availableWidth]);
+
+ y .domain(yDomain || d3.extent(data, getY ))
+ .range(yRange || [availableHeight, 0]);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-sparkline').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparkline');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
+
+ var paths = wrap.selectAll('path')
+ .data(function(d) { return [d] });
+ paths.enter().append('path');
+ paths.exit().remove();
+ paths
+ .style('stroke', function(d,i) { return d.color || color(d, i) })
+ .attr('d', d3.svg.line()
+ .x(function(d,i) { return x(getX(d,i)) })
+ .y(function(d,i) { return y(getY(d,i)) })
+ );
+
+ // TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent)
+ var points = wrap.selectAll('circle.nv-point')
+ .data(function(data) {
+ var yValues = data.map(function(d, i) { return getY(d,i); });
+ function pointIndex(index) {
+ if (index != -1) {
+ var result = data[index];
+ result.pointIndex = index;
+ return result;
+ } else {
+ return null;
+ }
+ }
+ var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])),
+ minPoint = pointIndex(yValues.indexOf(y.domain()[0])),
+ currentPoint = pointIndex(yValues.length - 1);
+ return [minPoint, maxPoint, currentPoint].filter(function (d) {return d != null;});
+ });
+ points.enter().append('circle');
+ points.exit().remove();
+ points
+ .attr('cx', function(d,i) { return x(getX(d,d.pointIndex)) })
+ .attr('cy', function(d,i) { return y(getY(d,d.pointIndex)) })
+ .attr('r', 2)
+ .attr('class', function(d,i) {
+ return getX(d, d.pointIndex) == x.domain()[1] ? 'nv-point nv-currentValue' :
+ getY(d, d.pointIndex) == y.domain()[0] ? 'nv-point nv-minValue' : 'nv-point nv-maxValue'
+ });
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ animate: {get: function(){return animate;}, set: function(_){animate=_;}},
+
+ //functor options
+ x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
+ y: {get: function(){return getY;}, set: function(_){getY=d3.functor(_);}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+ return chart;
+};
+
+nv.models.sparklinePlus = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var sparkline = nv.models.sparkline();
+
+ var margin = {top: 15, right: 100, bottom: 10, left: 50}
+ , width = null
+ , height = null
+ , x
+ , y
+ , index = []
+ , paused = false
+ , xTickFormat = d3.format(',r')
+ , yTickFormat = d3.format(',.2f')
+ , showValue = true
+ , alignValue = true
+ , rightAlignValue = false
+ , noData = "No Data Available."
+ ;
+
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ chart.update = function() { chart(selection) };
+ chart.container = this;
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ var currentValue = sparkline.y()(data[data.length-1], data.length-1);
+
+ // Setup Scales
+ x = sparkline.xScale();
+ y = sparkline.yScale();
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-sparklineplus').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparklineplus');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-sparklineWrap');
+ gEnter.append('g').attr('class', 'nv-valueWrap');
+ gEnter.append('g').attr('class', 'nv-hoverArea');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ // Main Chart Component(s)
+ var sparklineWrap = g.select('.nv-sparklineWrap');
+
+ sparkline.width(availableWidth).height(availableHeight);
+ sparklineWrap.call(sparkline);
+
+ var valueWrap = g.select('.nv-valueWrap');
+ var value = valueWrap.selectAll('.nv-currentValue')
+ .data([currentValue]);
+
+ value.enter().append('text').attr('class', 'nv-currentValue')
+ .attr('dx', rightAlignValue ? -8 : 8)
+ .attr('dy', '.9em')
+ .style('text-anchor', rightAlignValue ? 'end' : 'start');
+
+ value
+ .attr('x', availableWidth + (rightAlignValue ? margin.right : 0))
+ .attr('y', alignValue ? function(d) { return y(d) } : 0)
+ .style('fill', sparkline.color()(data[data.length-1], data.length-1))
+ .text(yTickFormat(currentValue));
+
+ gEnter.select('.nv-hoverArea').append('rect')
+ .on('mousemove', sparklineHover)
+ .on('click', function() { paused = !paused })
+ .on('mouseout', function() { index = []; updateValueLine(); });
+
+ g.select('.nv-hoverArea rect')
+ .attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' })
+ .attr('width', availableWidth + margin.left + margin.right)
+ .attr('height', availableHeight + margin.top);
+
+ //index is currently global (within the chart), may or may not keep it that way
+ function updateValueLine() {
+ if (paused) return;
+
+ var hoverValue = g.selectAll('.nv-hoverValue').data(index)
+
+ var hoverEnter = hoverValue.enter()
+ .append('g').attr('class', 'nv-hoverValue')
+ .style('stroke-opacity', 0)
+ .style('fill-opacity', 0);
+
+ hoverValue.exit()
+ .transition().duration(250)
+ .style('stroke-opacity', 0)
+ .style('fill-opacity', 0)
+ .remove();
+
+ hoverValue
+ .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' })
+ .transition().duration(250)
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', 1);
+
+ if (!index.length) return;
+
+ hoverEnter.append('line')
+ .attr('x1', 0)
+ .attr('y1', -margin.top)
+ .attr('x2', 0)
+ .attr('y2', availableHeight);
+
+ hoverEnter.append('text').attr('class', 'nv-xValue')
+ .attr('x', -6)
+ .attr('y', -margin.top)
+ .attr('text-anchor', 'end')
+ .attr('dy', '.9em')
+
+ g.select('.nv-hoverValue .nv-xValue')
+ .text(xTickFormat(sparkline.x()(data[index[0]], index[0])));
+
+ hoverEnter.append('text').attr('class', 'nv-yValue')
+ .attr('x', 6)
+ .attr('y', -margin.top)
+ .attr('text-anchor', 'start')
+ .attr('dy', '.9em')
+
+ g.select('.nv-hoverValue .nv-yValue')
+ .text(yTickFormat(sparkline.y()(data[index[0]], index[0])));
+ }
+
+ function sparklineHover() {
+ if (paused) return;
+
+ var pos = d3.mouse(this)[0] - margin.left;
+
+ function getClosestIndex(data, x) {
+ var distance = Math.abs(sparkline.x()(data[0], 0) - x);
+ var closestIndex = 0;
+ for (var i = 0; i < data.length; i++){
+ if (Math.abs(sparkline.x()(data[i], i) - x) < distance) {
+ distance = Math.abs(sparkline.x()(data[i], i) - x);
+ closestIndex = i;
+ }
+ }
+ return closestIndex;
+ }
+
+ index = [getClosestIndex(data, Math.round(x.invert(pos)))];
+ updateValueLine();
+ }
+
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.sparkline = sparkline;
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xTickFormat: {get: function(){return xTickFormat;}, set: function(_){xTickFormat=_;}},
+ yTickFormat: {get: function(){return yTickFormat;}, set: function(_){yTickFormat=_;}},
+ showValue: {get: function(){return showValue;}, set: function(_){showValue=_;}},
+ alignValue: {get: function(){return alignValue;}, set: function(_){alignValue=_;}},
+ rightAlignValue: {get: function(){return rightAlignValue;}, set: function(_){rightAlignValue=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, sparkline);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.models.stackedArea = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , color = nv.utils.defaultColor() // a function that computes the color
+ , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't selet one
+ , getX = function(d) { return d.x } // accessor to get the x value from a data point
+ , getY = function(d) { return d.y } // accessor to get the y value from a data point
+ , style = 'stack'
+ , offset = 'zero'
+ , order = 'default'
+ , interpolate = 'linear' // controls the line interpolation
+ , clipEdge = false // if true, masks lines within x and y scale
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , scatter = nv.models.scatter()
+ , duration = 250
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'areaClick', 'areaMouseover', 'areaMouseout','renderEnd')
+ ;
+
+ // scatter is interactive by default, but this chart isn't so must disable
+ scatter.interactive(false);
+
+ scatter
+ .pointSize(2.2) // default size
+ .pointDomain([2.2, 2.2]) // all the same size by default
+ ;
+
+ /************************************
+ * offset:
+ * 'wiggle' (stream)
+ * 'zero' (stacked)
+ * 'expand' (normalize to 100%)
+ * 'silhouette' (simple centered)
+ *
+ * order:
+ * 'inside-out' (stream)
+ * 'default' (input order)
+ ************************************/
+
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(scatter);
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ // Setup Scales
+ x = scatter.xScale();
+ y = scatter.yScale();
+
+ var dataRaw = data;
+ // Injecting point index into each point because d3.layout.stack().out does not give index
+ data.forEach(function(aseries, i) {
+ aseries.seriesIndex = i;
+ aseries.values = aseries.values.map(function(d, j) {
+ d.index = j;
+ d.seriesIndex = i;
+ return d;
+ });
+ });
+
+ var dataFiltered = data.filter(function(series) {
+ return !series.disabled;
+ });
+
+ data = d3.layout.stack()
+ .order(order)
+ .offset(offset)
+ .values(function(d) { return d.values }) //TODO: make values customizeable in EVERY model in this fashion
+ .x(getX)
+ .y(getY)
+ .out(function(d, y0, y) {
+ var yHeight = (getY(d) === 0) ? 0 : y;
+ d.display = {
+ y: yHeight,
+ y0: y0
+ };
+ })
+ (dataFiltered);
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-stackedarea').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedarea');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-areaWrap');
+ gEnter.append('g').attr('class', 'nv-scatterWrap');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ scatter
+ .width(availableWidth)
+ .height(availableHeight)
+ .x(getX)
+ .y(function(d) { return d.display.y + d.display.y0 })
+ .forceY([0])
+ .color(data.map(function(d,i) {
+ return d.color || color(d, d.seriesIndex);
+ }));
+
+ var scatterWrap = g.select('.nv-scatterWrap')
+ .datum(data);
+
+ scatterWrap.call(scatter);
+
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-edge-clip-' + id)
+ .append('rect');
+
+ wrap.select('#nv-edge-clip-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
+
+ g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
+
+ var area = d3.svg.area()
+ .x(function(d,i) { return x(getX(d,i)) })
+ .y0(function(d) {
+ return y(d.display.y0)
+ })
+ .y1(function(d) {
+ return y(d.display.y + d.display.y0)
+ })
+ .interpolate(interpolate);
+
+ var zeroArea = d3.svg.area()
+ .x(function(d,i) { return x(getX(d,i)) })
+ .y0(function(d) { return y(d.display.y0) })
+ .y1(function(d) { return y(d.display.y0) });
+
+ var path = g.select('.nv-areaWrap').selectAll('path.nv-area')
+ .data(function(d) { return d });
+
+ path.enter().append('path').attr('class', function(d,i) { return 'nv-area nv-area-' + i })
+ .attr('d', function(d,i){
+ return zeroArea(d.values, d.seriesIndex);
+ })
+ .on('mouseover', function(d,i) {
+ d3.select(this).classed('hover', true);
+ dispatch.areaMouseover({
+ point: d,
+ series: d.key,
+ pos: [d3.event.pageX, d3.event.pageY],
+ seriesIndex: d.seriesIndex
+ });
+ })
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.areaMouseout({
+ point: d,
+ series: d.key,
+ pos: [d3.event.pageX, d3.event.pageY],
+ seriesIndex: d.seriesIndex
+ });
+ })
+ .on('click', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.areaClick({
+ point: d,
+ series: d.key,
+ pos: [d3.event.pageX, d3.event.pageY],
+ seriesIndex: d.seriesIndex
+ });
+ });
+
+ path.exit().remove();
+ path.style('fill', function(d,i){
+ return d.color || color(d, d.seriesIndex)
+ })
+ .style('stroke', function(d,i){ return d.color || color(d, d.seriesIndex) });
+ path.watchTransition(renderWatch,'stackedArea path')
+ .attr('d', function(d,i) {
+ return area(d.values,i)
+ });
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ scatter.dispatch.on('elementMouseover.area', function(e) {
+ g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', true);
+ });
+ scatter.dispatch.on('elementMouseout.area', function(e) {
+ g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', false);
+ });
+
+ //Special offset functions
+ chart.d3_stackedOffset_stackPercent = function(stackData) {
+ var n = stackData.length, //How many series
+ m = stackData[0].length, //how many points per series
+ k = 1 / n,
+ i,
+ j,
+ o,
+ y0 = [];
+
+ for (j = 0; j < m; ++j) { //Looping through all points
+ for (i = 0, o = 0; i < dataRaw.length; i++) { //looping through series'
+ o += getY(dataRaw[i].values[j]); //total value of all points at a certian point in time.
+ }
+
+ if (o) for (i = 0; i < n; i++) {
+ stackData[i][j][1] /= o;
+ } else {
+ for (i = 0; i < n; i++) {
+ stackData[i][j][1] = k;
+ }
+ }
+ }
+ for (j = 0; j < m; ++j) y0[j] = 0;
+ return y0;
+ };
+
+ });
+
+ renderWatch.renderEnd('stackedArea immediate');
+ return chart;
+ }
+
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ scatter.dispatch.on('elementClick.area', function(e) {
+ dispatch.areaClick(e);
+ });
+ scatter.dispatch.on('elementMouseover.tooltip', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
+ dispatch.tooltipShow(e);
+ });
+ scatter.dispatch.on('elementMouseout.tooltip', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ //============================================================
+ // Global getters and setters
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.scatter = scatter;
+
+ chart.interpolate = function(_) {
+ if (!arguments.length) return interpolate;
+ interpolate = _;
+ return chart;
+ };
+
+ chart.duration = function(_) {
+ if (!arguments.length) return duration;
+ duration = _;
+ renderWatch.reset(duration);
+ scatter.duration(duration);
+ return chart;
+ };
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ offset: {get: function(){return offset;}, set: function(_){offset=_;}},
+ order: {get: function(){return order;}, set: function(_){order=_;}},
+ interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
+
+ // simple functor options
+ x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
+ y: {get: function(){return getY;}, set: function(_){getY = d3.functor(_);}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ style: {get: function(){return style;}, set: function(_){
+ style = _;
+ switch (style) {
+ case 'stack':
+ chart.offset('zero');
+ chart.order('default');
+ break;
+ case 'stream':
+ chart.offset('wiggle');
+ chart.order('inside-out');
+ break;
+ case 'stream-center':
+ chart.offset('silhouette');
+ chart.order('inside-out');
+ break;
+ case 'expand':
+ chart.offset('expand');
+ chart.order('default');
+ break;
+ case 'stack_percent':
+ chart.offset(chart.d3_stackedOffset_stackPercent);
+ chart.order('default');
+ break;
+ }
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ scatter.duration(duration);
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, scatter);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.models.stackedAreaChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var stacked = nv.models.stackedArea()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , controls = nv.models.legend()
+ , interactiveLayer = nv.interactiveGuideline()
+ ;
+
+ var margin = {top: 30, right: 25, bottom: 50, left: 60}
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor()
+ , showControls = true
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , useInteractiveGuideline = false
+ , tooltips = true
+ , tooltip = function(key, x, y, e, graph) {
+ return '
' + key + ' ' +
+ '
' + y + ' on ' + x + '
'
+ }
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , yAxisTickFormat = d3.format(',.2f')
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = 'No Data Available.'
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState','renderEnd')
+ , controlWidth = 250
+ , cData = ['Stacked','Stream','Expanded']
+ , controlLabels = {}
+ , duration = 250
+ ;
+
+ state.style = stacked.style();
+ xAxis.orient('bottom').tickPadding(7);
+ yAxis.orient((rightAlignYAxis) ? 'right' : 'left');
+
+ controls.updateState(false);
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var renderWatch = nv.utils.renderWatch(dispatch);
+ var style = stacked.style();
+
+ var showTooltip = function(e, offsetElement) {
+ var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
+ top = e.pos[1] + ( offsetElement.offsetTop || 0),
+ x = xAxis.tickFormat()(stacked.x()(e.point, e.pointIndex)),
+ y = yAxis.tickFormat()(stacked.y()(e.point, e.pointIndex)),
+ content = tooltip(e.series.key, x, y, e, chart);
+
+ nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
+ };
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled }),
+ style: stacked.style()
+ };
+ }
+ };
+
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.style !== undefined)
+ style = state.style;
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(stacked);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+
+ var availableWidth = (width || parseInt(container.style('width')) || 960)
+ - margin.left - margin.right,
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+
+ chart.update = function() { container.transition().duration(duration).call(chart); };
+ chart.container = this;
+
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ // DEPRECATED set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
+
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
+
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
+
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup Scales
+ x = stacked.xScale();
+ y = stacked.yScale();
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-stackedAreaChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedAreaChart').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append("rect").style("opacity",0);
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-stackedWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-controlsWrap');
+ gEnter.append('g').attr('class', 'nv-interactive');
+
+ g.select("rect").attr("width",availableWidth).attr("height",availableHeight);
+
+ // Legend
+ if (showLegend) {
+ var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth;
+
+ legend.width(legendWidth);
+ g.select('.nv-legendWrap').datum(data).call(legend);
+
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')');
+ }
+
+ // Controls
+ if (showControls) {
+ var controlsData = [
+ {
+ key: controlLabels.stacked || 'Stacked',
+ metaKey: 'Stacked',
+ disabled: stacked.style() != 'stack',
+ style: 'stack'
+ },
+ {
+ key: controlLabels.stream || 'Stream',
+ metaKey: 'Stream',
+ disabled: stacked.style() != 'stream',
+ style: 'stream'
+ },
+ {
+ key: controlLabels.expanded || 'Expanded',
+ metaKey: 'Expanded',
+ disabled: stacked.style() != 'expand',
+ style: 'expand'
+ },
+ {
+ key: controlLabels.stack_percent || 'Stack %',
+ metaKey: 'Stack_Percent',
+ disabled: stacked.style() != 'stack_percent',
+ style: 'stack_percent'
+ }
+ ];
+
+ controlWidth = (cData.length/3) * 260;
+ controlsData = controlsData.filter(function(d) {
+ return cData.indexOf(d.metaKey) !== -1;
+ });
+
+ controls
+ .width( controlWidth )
+ .color(['#444', '#444', '#444']);
+
+ g.select('.nv-controlsWrap')
+ .datum(controlsData)
+ .call(controls);
+
+ if ( margin.top != Math.max(controls.height(), legend.height()) ) {
+ margin.top = Math.max(controls.height(), legend.height());
+ availableHeight = (height || parseInt(container.style('height')) || 400)
+ - margin.top - margin.bottom;
+ }
+
+ g.select('.nv-controlsWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')');
+ }
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
+
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left: margin.left, top: margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
+
+ stacked
+ .width(availableWidth)
+ .height(availableHeight);
+
+ var stackedWrap = g.select('.nv-stackedWrap')
+ .datum(data);
+
+ stackedWrap.transition().call(stacked);
+
+ // Setup Axes
+ if (showXAxis) {
+ xAxis.scale(x)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize( -availableHeight, 0);
+
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + availableHeight + ')');
+
+ g.select('.nv-x.nv-axis')
+ .transition().duration(0)
+ .call(xAxis);
+ }
+
+ if (showYAxis) {
+ yAxis.scale(y)
+ .ticks(stacked.offset() == 'wiggle' ? 0 : nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize(-availableWidth, 0)
+ .setTickFormat( (stacked.style() == 'expand' || stacked.style() == 'stack_percent')
+ ? d3.format('%') : yAxisTickFormat);
+
+ g.select('.nv-y.nv-axis')
+ .transition().duration(0)
+ .call(yAxis);
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ stacked.dispatch.on('areaClick.toggle', function(e) {
+ if (data.filter(function(d) { return !d.disabled }).length === 1)
+ data.forEach(function(d) {
+ d.disabled = false;
+ });
+ else
+ data.forEach(function(d,i) {
+ d.disabled = (i != e.seriesIndex);
+ });
+
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+
+ chart.update();
+ });
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
+
+ controls.dispatch.on('legendClick', function(d,i) {
+ if (!d.disabled) return;
+
+ controlsData = controlsData.map(function(s) {
+ s.disabled = true;
+ return s;
+ });
+ d.disabled = false;
+
+ stacked.style(d.style);
+
+
+ state.style = stacked.style();
+ dispatch.stateChange(state);
+
+ chart.update();
+ });
+
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ stacked.clearHighlights();
+ var singlePoint, pointIndex, pointXLocation, allData = [];
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ stacked.highlightPoint(i, pointIndex, true);
+ var point = series.values[pointIndex];
+ if (typeof point === 'undefined') return;
+ if (typeof singlePoint === 'undefined') singlePoint = point;
+ if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+
+ //If we are in 'expand' mode, use the stacked percent value instead of raw value.
+ var tooltipValue = (stacked.style() == 'expand') ? point.display.y : chart.y()(point,pointIndex);
+ allData.push({
+ key: series.key,
+ value: tooltipValue,
+ color: color(series,series.seriesIndex),
+ stackedValue: point.display
+ });
+ });
+
+ allData.reverse();
+
+ //Highlight the tooltip entry based on which stack the mouse is closest to.
+ if (allData.length > 2) {
+ var yValue = chart.yScale().invert(e.mouseY);
+ var yDistMax = Infinity, indexToHighlight = null;
+ allData.forEach(function(series,i) {
+
+ //To handle situation where the stacked area chart is negative, we need to use absolute values
+ //when checking if the mouse Y value is within the stack area.
+ yValue = Math.abs(yValue);
+ var stackedY0 = Math.abs(series.stackedValue.y0);
+ var stackedY = Math.abs(series.stackedValue.y);
+ if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0))
+ {
+ indexToHighlight = i;
+ return;
+ }
+ });
+ if (indexToHighlight != null)
+ allData[indexToHighlight].highlight = true;
+ }
+
+ var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
+
+ //If we are in 'expand' mode, force the format to be a percentage.
+ var valueFormatter = (stacked.style() == 'expand') ?
+ function(d,i) {return d3.format(".1%")(d);} :
+ function(d,i) {return yAxis.tickFormat()(d); };
+ interactiveLayer.tooltip
+ .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
+ .chartContainer(that.parentNode)
+ .enabled(tooltips)
+ .valueFormatter(valueFormatter)
+ .data(
+ {
+ value: xValue,
+ series: allData
+ }
+ )();
+
+ interactiveLayer.renderGuideLine(pointXLocation);
+
+ });
+
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ dispatch.tooltipHide();
+ stacked.clearHighlights();
+ });
+
+
+ dispatch.on('tooltipShow', function(e) {
+ if (tooltips) showTooltip(e, that.parentNode);
+ });
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ if (typeof e.style !== 'undefined') {
+ stacked.style(e.style);
+ style = e.style;
+ }
+
+ chart.update();
+ });
+
+ });
+
+ renderWatch.renderEnd('stacked Area chart immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ stacked.dispatch.on('tooltipShow', function(e) {
+ e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
+ dispatch.tooltipShow(e);
+ });
+
+ stacked.dispatch.on('tooltipHide', function(e) {
+ dispatch.tooltipHide(e);
+ });
+
+ dispatch.on('tooltipHide', function() {
+ if (tooltips) nv.tooltip.cleanup();
+ });
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.stacked = stacked;
+ chart.legend = legend;
+ chart.controls = controls;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.interactiveLayer = interactiveLayer;
+
+ yAxis.setTickFormat = yAxis.tickFormat;
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
+ controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
+ yAxisTickFormat: {get: function(){return yAxisTickFormat;}, set: function(_){yAxisTickFormat=_;}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ stacked.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ stacked.color(color);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( rightAlignYAxis ? 'right' : 'left');
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = !!_;
+ if (_) {
+ chart.interactive(false);
+ chart.useVoronoi(false);
+ }
+ }}
+ });
+
+ nv.utils.inheritOptions(chart, stacked);
+ nv.utils.initOptions(chart);
+
+ return chart;
+};
+
+nv.version = "1.7.1";
+})();
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/sonar_graph.js b/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/sonar_graph.js
new file mode 100755
index 00000000..de79082e
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/sonar_graph.js
@@ -0,0 +1,60 @@
+var sonarChart;
+
+nv.addGraph(function () {
+
+ sonarChart = nv.models.lineChart()
+ .interpolate("linear")
+ .options({
+ transitionDuration: 300,
+ useInteractiveGuideline: true
+ })
+ ;
+
+ sonarChart.xScale(d3.time.scale());
+
+ // chart sub-models (ie. xAxis, yAxis, etc) when accessed directly, return themselves, not the parent chart, so need to chain separately
+ sonarChart.xAxis
+ .axisLabel("Date/Time")
+ .ticks(d3.time.seconds)
+ .tickFormat(function (d) {
+ return d3.time.format('%m/%d/%Y %I:%M:%S %p')(new Date(d))
+ })
+ .staggerLabels(true)
+ ;
+
+ sonarChart.yAxis
+ .axisLabel('Sonar')
+ ;
+
+ d3.select('.chart4 svg')
+ .datum(getSonarChartData())
+ .call(sonarChart);
+
+ nv.utils.windowResize(sonarChart.update);
+
+ return sonarChart;
+});
+
+function getSonarChartData() {
+
+ return [
+ {
+ area: true,
+ values: [],
+ key: "Sonar",
+ color: "#34500e"
+ }
+ ];
+
+}
+
+function updateSonarGraph(sonarData) {
+
+ var chartData = getSonarChartData();
+ chartData[0]['values'] = sonarData;
+
+ d3.select('.chart4 svg')
+ .datum(chartData)
+ .transition().duration(500)
+ .call(sonarChart);
+}
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/stream_layers.js b/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/stream_layers.js
new file mode 100755
index 00000000..43d7cfe0
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/stream_layers.js
@@ -0,0 +1,35 @@
+
+/* Inspired by Lee Byron's test data generator. */
+function stream_layers(n, m, o) {
+ if (arguments.length < 3) o = 0;
+ function bump(a) {
+ var x = 1 / (.1 + Math.random()),
+ y = 2 * Math.random() - .5,
+ z = 10 / (.1 + Math.random());
+ for (var i = 0; i < m; i++) {
+ var w = (i / m - y) * z;
+ a[i] += x * Math.exp(-w * w);
+ }
+ }
+ return d3.range(n).map(function() {
+ var a = [], i;
+ for (i = 0; i < m; i++) a[i] = o + o * Math.random();
+ for (i = 0; i < 5; i++) bump(a);
+ return a.map(stream_index);
+ });
+}
+
+/* Another layer generator using gamma distributions. */
+function stream_waves(n, m) {
+ return d3.range(n).map(function(i) {
+ return d3.range(m).map(function(j) {
+ var x = 20 * j / m - i / 3;
+ return 2 * x * Math.exp(-.5 * x);
+ }).map(stream_index);
+ });
+}
+
+function stream_index(d, i) {
+ return {x: i, y: Math.max(0, d)};
+}
+
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/temperature_graph.js b/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/temperature_graph.js
new file mode 100755
index 00000000..4c463b39
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/mydevice/public/js/temperature_graph.js
@@ -0,0 +1,60 @@
+var temperatureChart;
+
+nv.addGraph(function () {
+
+ temperatureChart = nv.models.lineChart()
+ .interpolate("linear")
+ .options({
+ transitionDuration: 300,
+ useInteractiveGuideline: true
+ })
+ ;
+
+ temperatureChart.xScale(d3.time.scale());
+
+ // chart sub-models (ie. xAxis, yAxis, etc) when accessed directly, return themselves, not the parent chart, so need to chain separately
+ temperatureChart.xAxis
+ .axisLabel("Date/Time")
+ .ticks(d3.time.seconds)
+ .tickFormat(function (d) {
+ return d3.time.format('%m/%d/%Y %I:%M:%S %p')(new Date(d))
+ })
+ .staggerLabels(true)
+ ;
+
+ temperatureChart.yAxis
+ .axisLabel('Temperature (C)')
+ ;
+
+ d3.select('.chart1 svg')
+ .datum(getTemperatureChartData())
+ .call(temperatureChart);
+
+ nv.utils.windowResize(temperatureChart.update);
+
+ return temperatureChart;
+});
+
+function getTemperatureChartData() {
+
+ return [
+ {
+ area: true,
+ values: [],
+ key: "Temperature",
+ color: "#34500e"
+ }
+ ];
+
+}
+
+function updateTemperatureGraph(temperatureData) {
+
+ var chartData = getTemperatureChartData();
+ chartData[0]['values'] = temperatureData;
+
+ d3.select('.chart1 svg')
+ .datum(chartData)
+ .transition().duration(500)
+ .call(temperatureChart);
+}
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/bootstrap.min.css b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/bootstrap.min.css
new file mode 100755
index 00000000..d29dc3b4
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/bootstrap.min.css
@@ -0,0 +1,6671 @@
+/*!
+ * Bootstrap v3.3.4 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
+
+html {
+ font-family: sans-serif;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%
+}
+body {
+ margin: 0
+}
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+ display: block
+}
+audio,
+canvas,
+progress,
+video {
+ display: inline-block;
+ vertical-align: baseline
+}
+audio:not([controls]) {
+ display: none;
+ height: 0
+}
+[hidden],
+template {
+ display: none
+}
+a {
+ background-color: transparent
+}
+a:active,
+a:hover {
+ outline: 0
+}
+abbr[title] {
+ border-bottom: 1px dotted
+}
+b,
+strong {
+ font-weight: 700
+}
+dfn {
+ font-style: italic
+}
+h1 {
+ margin: .67em 0;
+ font-size: 2em
+}
+mark {
+ color: #000;
+ background: #ff0
+}
+small {
+ font-size: 80%
+}
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline
+}
+sup {
+ top: -.5em
+}
+sub {
+ bottom: -.25em
+}
+img {
+ border: 0
+}
+svg:not(:root) {
+ overflow: hidden
+}
+figure {
+ margin: 1em 40px
+}
+hr {
+ height: 0;
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box
+}
+pre {
+ overflow: auto
+}
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em
+}
+button,
+input,
+optgroup,
+select,
+textarea {
+ margin: 0;
+ font: inherit;
+ color: inherit
+}
+button {
+ overflow: visible
+}
+button,
+select {
+ text-transform: none
+}
+button,
+html input[type=button],
+input[type=reset],
+input[type=submit] {
+ -webkit-appearance: button;
+ cursor: pointer
+}
+button[disabled],
+html input[disabled] {
+ cursor: default
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ padding: 0;
+ border: 0
+}
+input {
+ line-height: normal
+}
+input[type=checkbox],
+input[type=radio] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0
+}
+input[type=number]::-webkit-inner-spin-button,
+input[type=number]::-webkit-outer-spin-button {
+ height: auto
+}
+input[type=search] {
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield
+}
+input[type=search]::-webkit-search-cancel-button,
+input[type=search]::-webkit-search-decoration {
+ -webkit-appearance: none
+}
+fieldset {
+ padding: .35em .625em .75em;
+ margin: 0 2px;
+ border: 1px solid silver
+}
+legend {
+ padding: 0;
+ border: 0
+}
+textarea {
+ overflow: auto
+}
+optgroup {
+ font-weight: 700
+}
+table {
+ border-spacing: 0;
+ border-collapse: collapse
+}
+td,
+th {
+ padding: 0
+}
+/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
+
+@media print {
+ *,
+ :after,
+ :before {
+ color: #000!important;
+ text-shadow: none!important;
+ background: 0 0!important;
+ -webkit-box-shadow: none!important;
+ box-shadow: none!important
+ }
+ a,
+ a:visited {
+ text-decoration: underline
+ }
+ a[href]:after {
+ content: " (" attr(href) ")"
+ }
+ abbr[title]:after {
+ content: " (" attr(title) ")"
+ }
+ a[href^="javascript:"]:after,
+ a[href^="#"]:after {
+ content: ""
+ }
+ blockquote,
+ pre {
+ border: 1px solid #999;
+ page-break-inside: avoid
+ }
+ thead {
+ display: table-header-group
+ }
+ img,
+ tr {
+ page-break-inside: avoid
+ }
+ img {
+ max-width: 100%!important
+ }
+ h2,
+ h3,
+ p {
+ orphans: 3;
+ widows: 3
+ }
+ h2,
+ h3 {
+ page-break-after: avoid
+ }
+ select {
+ background: #fff!important
+ }
+ .navbar {
+ display: none
+ }
+ .btn>.caret,
+ .dropup>.btn>.caret {
+ border-top-color: #000!important
+ }
+ .label {
+ border: 1px solid #000
+ }
+ .table {
+ border-collapse: collapse!important
+ }
+ .table td,
+ .table th {
+ background-color: #fff!important
+ }
+ .table-bordered td,
+ .table-bordered th {
+ border: 1px solid #ddd!important
+ }
+}
+@font-face {
+ font-family: 'Glyphicons Halflings';
+ src: url(../fonts/glyphicons-halflings-regular.eot);
+ src: url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'), url(../fonts/glyphicons-halflings-regular.woff) format('woff'), url(../fonts/glyphicons-halflings-regular.woff) format('woff'), url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'), url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')
+}
+.glyphicon {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: 400;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale
+}
+.glyphicon-asterisk:before {
+ content: "\2a"
+}
+.glyphicon-plus:before {
+ content: "\2b"
+}
+.glyphicon-eur:before,
+.glyphicon-euro:before {
+ content: "\20ac"
+}
+.glyphicon-minus:before {
+ content: "\2212"
+}
+.glyphicon-cloud:before {
+ content: "\2601"
+}
+.glyphicon-envelope:before {
+ content: "\2709"
+}
+.glyphicon-pencil:before {
+ content: "\270f"
+}
+.glyphicon-glass:before {
+ content: "\e001"
+}
+.glyphicon-music:before {
+ content: "\e002"
+}
+.glyphicon-search:before {
+ content: "\e003"
+}
+.glyphicon-heart:before {
+ content: "\e005"
+}
+.glyphicon-star:before {
+ content: "\e006"
+}
+.glyphicon-star-empty:before {
+ content: "\e007"
+}
+.glyphicon-user:before {
+ content: "\e008"
+}
+.glyphicon-film:before {
+ content: "\e009"
+}
+.glyphicon-th-large:before {
+ content: "\e010"
+}
+.glyphicon-th:before {
+ content: "\e011"
+}
+.glyphicon-th-list:before {
+ content: "\e012"
+}
+.glyphicon-ok:before {
+ content: "\e013"
+}
+.glyphicon-remove:before {
+ content: "\e014"
+}
+.glyphicon-zoom-in:before {
+ content: "\e015"
+}
+.glyphicon-zoom-out:before {
+ content: "\e016"
+}
+.glyphicon-off:before {
+ content: "\e017"
+}
+.glyphicon-signal:before {
+ content: "\e018"
+}
+.glyphicon-cog:before {
+ content: "\e019"
+}
+.glyphicon-trash:before {
+ content: "\e020"
+}
+.glyphicon-home:before {
+ content: "\e021"
+}
+.glyphicon-file:before {
+ content: "\e022"
+}
+.glyphicon-time:before {
+ content: "\e023"
+}
+.glyphicon-road:before {
+ content: "\e024"
+}
+.glyphicon-download-alt:before {
+ content: "\e025"
+}
+.glyphicon-download:before {
+ content: "\e026"
+}
+.glyphicon-upload:before {
+ content: "\e027"
+}
+.glyphicon-inbox:before {
+ content: "\e028"
+}
+.glyphicon-play-circle:before {
+ content: "\e029"
+}
+.glyphicon-repeat:before {
+ content: "\e030"
+}
+.glyphicon-refresh:before {
+ content: "\e031"
+}
+.glyphicon-list-alt:before {
+ content: "\e032"
+}
+.glyphicon-lock:before {
+ content: "\e033"
+}
+.glyphicon-flag:before {
+ content: "\e034"
+}
+.glyphicon-headphones:before {
+ content: "\e035"
+}
+.glyphicon-volume-off:before {
+ content: "\e036"
+}
+.glyphicon-volume-down:before {
+ content: "\e037"
+}
+.glyphicon-volume-up:before {
+ content: "\e038"
+}
+.glyphicon-qrcode:before {
+ content: "\e039"
+}
+.glyphicon-barcode:before {
+ content: "\e040"
+}
+.glyphicon-tag:before {
+ content: "\e041"
+}
+.glyphicon-tags:before {
+ content: "\e042"
+}
+.glyphicon-book:before {
+ content: "\e043"
+}
+.glyphicon-bookmark:before {
+ content: "\e044"
+}
+.glyphicon-print:before {
+ content: "\e045"
+}
+.glyphicon-camera:before {
+ content: "\e046"
+}
+.glyphicon-font:before {
+ content: "\e047"
+}
+.glyphicon-bold:before {
+ content: "\e048"
+}
+.glyphicon-italic:before {
+ content: "\e049"
+}
+.glyphicon-text-height:before {
+ content: "\e050"
+}
+.glyphicon-text-width:before {
+ content: "\e051"
+}
+.glyphicon-align-left:before {
+ content: "\e052"
+}
+.glyphicon-align-center:before {
+ content: "\e053"
+}
+.glyphicon-align-right:before {
+ content: "\e054"
+}
+.glyphicon-align-justify:before {
+ content: "\e055"
+}
+.glyphicon-list:before {
+ content: "\e056"
+}
+.glyphicon-indent-left:before {
+ content: "\e057"
+}
+.glyphicon-indent-right:before {
+ content: "\e058"
+}
+.glyphicon-facetime-video:before {
+ content: "\e059"
+}
+.glyphicon-picture:before {
+ content: "\e060"
+}
+.glyphicon-map-marker:before {
+ content: "\e062"
+}
+.glyphicon-adjust:before {
+ content: "\e063"
+}
+.glyphicon-tint:before {
+ content: "\e064"
+}
+.glyphicon-edit:before {
+ content: "\e065"
+}
+.glyphicon-share:before {
+ content: "\e066"
+}
+.glyphicon-check:before {
+ content: "\e067"
+}
+.glyphicon-move:before {
+ content: "\e068"
+}
+.glyphicon-step-backward:before {
+ content: "\e069"
+}
+.glyphicon-fast-backward:before {
+ content: "\e070"
+}
+.glyphicon-backward:before {
+ content: "\e071"
+}
+.glyphicon-play:before {
+ content: "\e072"
+}
+.glyphicon-pause:before {
+ content: "\e073"
+}
+.glyphicon-stop:before {
+ content: "\e074"
+}
+.glyphicon-forward:before {
+ content: "\e075"
+}
+.glyphicon-fast-forward:before {
+ content: "\e076"
+}
+.glyphicon-step-forward:before {
+ content: "\e077"
+}
+.glyphicon-eject:before {
+ content: "\e078"
+}
+.glyphicon-chevron-left:before {
+ content: "\e079"
+}
+.glyphicon-chevron-right:before {
+ content: "\e080"
+}
+.glyphicon-plus-sign:before {
+ content: "\e081"
+}
+.glyphicon-minus-sign:before {
+ content: "\e082"
+}
+.glyphicon-remove-sign:before {
+ content: "\e083"
+}
+.glyphicon-ok-sign:before {
+ content: "\e084"
+}
+.glyphicon-question-sign:before {
+ content: "\e085"
+}
+.glyphicon-info-sign:before {
+ content: "\e086"
+}
+.glyphicon-screenshot:before {
+ content: "\e087"
+}
+.glyphicon-remove-circle:before {
+ content: "\e088"
+}
+.glyphicon-ok-circle:before {
+ content: "\e089"
+}
+.glyphicon-ban-circle:before {
+ content: "\e090"
+}
+.glyphicon-arrow-left:before {
+ content: "\e091"
+}
+.glyphicon-arrow-right:before {
+ content: "\e092"
+}
+.glyphicon-arrow-up:before {
+ content: "\e093"
+}
+.glyphicon-arrow-down:before {
+ content: "\e094"
+}
+.glyphicon-share-alt:before {
+ content: "\e095"
+}
+.glyphicon-resize-full:before {
+ content: "\e096"
+}
+.glyphicon-resize-small:before {
+ content: "\e097"
+}
+.glyphicon-exclamation-sign:before {
+ content: "\e101"
+}
+.glyphicon-gift:before {
+ content: "\e102"
+}
+.glyphicon-leaf:before {
+ content: "\e103"
+}
+.glyphicon-fire:before {
+ content: "\e104"
+}
+.glyphicon-eye-open:before {
+ content: "\e105"
+}
+.glyphicon-eye-close:before {
+ content: "\e106"
+}
+.glyphicon-warning-sign:before {
+ content: "\e107"
+}
+.glyphicon-plane:before {
+ content: "\e108"
+}
+.glyphicon-calendar:before {
+ content: "\e109"
+}
+.glyphicon-random:before {
+ content: "\e110"
+}
+.glyphicon-comment:before {
+ content: "\e111"
+}
+.glyphicon-magnet:before {
+ content: "\e112"
+}
+.glyphicon-chevron-up:before {
+ content: "\e113"
+}
+.glyphicon-chevron-down:before {
+ content: "\e114"
+}
+.glyphicon-retweet:before {
+ content: "\e115"
+}
+.glyphicon-shopping-cart:before {
+ content: "\e116"
+}
+.glyphicon-folder-close:before {
+ content: "\e117"
+}
+.glyphicon-folder-open:before {
+ content: "\e118"
+}
+.glyphicon-resize-vertical:before {
+ content: "\e119"
+}
+.glyphicon-resize-horizontal:before {
+ content: "\e120"
+}
+.glyphicon-hdd:before {
+ content: "\e121"
+}
+.glyphicon-bullhorn:before {
+ content: "\e122"
+}
+.glyphicon-bell:before {
+ content: "\e123"
+}
+.glyphicon-certificate:before {
+ content: "\e124"
+}
+.glyphicon-thumbs-up:before {
+ content: "\e125"
+}
+.glyphicon-thumbs-down:before {
+ content: "\e126"
+}
+.glyphicon-hand-right:before {
+ content: "\e127"
+}
+.glyphicon-hand-left:before {
+ content: "\e128"
+}
+.glyphicon-hand-up:before {
+ content: "\e129"
+}
+.glyphicon-hand-down:before {
+ content: "\e130"
+}
+.glyphicon-circle-arrow-right:before {
+ content: "\e131"
+}
+.glyphicon-circle-arrow-left:before {
+ content: "\e132"
+}
+.glyphicon-circle-arrow-up:before {
+ content: "\e133"
+}
+.glyphicon-circle-arrow-down:before {
+ content: "\e134"
+}
+.glyphicon-globe:before {
+ content: "\e135"
+}
+.glyphicon-wrench:before {
+ content: "\e136"
+}
+.glyphicon-tasks:before {
+ content: "\e137"
+}
+.glyphicon-filter:before {
+ content: "\e138"
+}
+.glyphicon-briefcase:before {
+ content: "\e139"
+}
+.glyphicon-fullscreen:before {
+ content: "\e140"
+}
+.glyphicon-dashboard:before {
+ content: "\e141"
+}
+.glyphicon-paperclip:before {
+ content: "\e142"
+}
+.glyphicon-heart-empty:before {
+ content: "\e143"
+}
+.glyphicon-link:before {
+ content: "\e144"
+}
+.glyphicon-phone:before {
+ content: "\e145"
+}
+.glyphicon-pushpin:before {
+ content: "\e146"
+}
+.glyphicon-usd:before {
+ content: "\e148"
+}
+.glyphicon-gbp:before {
+ content: "\e149"
+}
+.glyphicon-sort:before {
+ content: "\e150"
+}
+.glyphicon-sort-by-alphabet:before {
+ content: "\e151"
+}
+.glyphicon-sort-by-alphabet-alt:before {
+ content: "\e152"
+}
+.glyphicon-sort-by-order:before {
+ content: "\e153"
+}
+.glyphicon-sort-by-order-alt:before {
+ content: "\e154"
+}
+.glyphicon-sort-by-attributes:before {
+ content: "\e155"
+}
+.glyphicon-sort-by-attributes-alt:before {
+ content: "\e156"
+}
+.glyphicon-unchecked:before {
+ content: "\e157"
+}
+.glyphicon-expand:before {
+ content: "\e158"
+}
+.glyphicon-collapse-down:before {
+ content: "\e159"
+}
+.glyphicon-collapse-up:before {
+ content: "\e160"
+}
+.glyphicon-log-in:before {
+ content: "\e161"
+}
+.glyphicon-flash:before {
+ content: "\e162"
+}
+.glyphicon-log-out:before {
+ content: "\e163"
+}
+.glyphicon-new-window:before {
+ content: "\e164"
+}
+.glyphicon-record:before {
+ content: "\e165"
+}
+.glyphicon-save:before {
+ content: "\e166"
+}
+.glyphicon-open:before {
+ content: "\e167"
+}
+.glyphicon-saved:before {
+ content: "\e168"
+}
+.glyphicon-import:before {
+ content: "\e169"
+}
+.glyphicon-export:before {
+ content: "\e170"
+}
+.glyphicon-send:before {
+ content: "\e171"
+}
+.glyphicon-floppy-disk:before {
+ content: "\e172"
+}
+.glyphicon-floppy-saved:before {
+ content: "\e173"
+}
+.glyphicon-floppy-remove:before {
+ content: "\e174"
+}
+.glyphicon-floppy-save:before {
+ content: "\e175"
+}
+.glyphicon-floppy-open:before {
+ content: "\e176"
+}
+.glyphicon-credit-card:before {
+ content: "\e177"
+}
+.glyphicon-transfer:before {
+ content: "\e178"
+}
+.glyphicon-cutlery:before {
+ content: "\e179"
+}
+.glyphicon-header:before {
+ content: "\e180"
+}
+.glyphicon-compressed:before {
+ content: "\e181"
+}
+.glyphicon-earphone:before {
+ content: "\e182"
+}
+.glyphicon-phone-alt:before {
+ content: "\e183"
+}
+.glyphicon-tower:before {
+ content: "\e184"
+}
+.glyphicon-stats:before {
+ content: "\e185"
+}
+.glyphicon-sd-video:before {
+ content: "\e186"
+}
+.glyphicon-hd-video:before {
+ content: "\e187"
+}
+.glyphicon-subtitles:before {
+ content: "\e188"
+}
+.glyphicon-sound-stereo:before {
+ content: "\e189"
+}
+.glyphicon-sound-dolby:before {
+ content: "\e190"
+}
+.glyphicon-sound-5-1:before {
+ content: "\e191"
+}
+.glyphicon-sound-6-1:before {
+ content: "\e192"
+}
+.glyphicon-sound-7-1:before {
+ content: "\e193"
+}
+.glyphicon-copyright-mark:before {
+ content: "\e194"
+}
+.glyphicon-registration-mark:before {
+ content: "\e195"
+}
+.glyphicon-cloud-download:before {
+ content: "\e197"
+}
+.glyphicon-cloud-upload:before {
+ content: "\e198"
+}
+.glyphicon-tree-conifer:before {
+ content: "\e199"
+}
+.glyphicon-tree-deciduous:before {
+ content: "\e200"
+}
+.glyphicon-cd:before {
+ content: "\e201"
+}
+.glyphicon-save-file:before {
+ content: "\e202"
+}
+.glyphicon-open-file:before {
+ content: "\e203"
+}
+.glyphicon-level-up:before {
+ content: "\e204"
+}
+.glyphicon-copy:before {
+ content: "\e205"
+}
+.glyphicon-paste:before {
+ content: "\e206"
+}
+.glyphicon-alert:before {
+ content: "\e209"
+}
+.glyphicon-equalizer:before {
+ content: "\e210"
+}
+.glyphicon-king:before {
+ content: "\e211"
+}
+.glyphicon-queen:before {
+ content: "\e212"
+}
+.glyphicon-pawn:before {
+ content: "\e213"
+}
+.glyphicon-bishop:before {
+ content: "\e214"
+}
+.glyphicon-knight:before {
+ content: "\e215"
+}
+.glyphicon-baby-formula:before {
+ content: "\e216"
+}
+.glyphicon-tent:before {
+ content: "\26fa"
+}
+.glyphicon-blackboard:before {
+ content: "\e218"
+}
+.glyphicon-bed:before {
+ content: "\e219"
+}
+.glyphicon-apple:before {
+ content: "\f8ff"
+}
+.glyphicon-erase:before {
+ content: "\e221"
+}
+.glyphicon-hourglass:before {
+ content: "\231b"
+}
+.glyphicon-lamp:before {
+ content: "\e223"
+}
+.glyphicon-duplicate:before {
+ content: "\e224"
+}
+.glyphicon-piggy-bank:before {
+ content: "\e225"
+}
+.glyphicon-scissors:before {
+ content: "\e226"
+}
+.glyphicon-bitcoin:before {
+ content: "\e227"
+}
+.glyphicon-btc:before {
+ content: "\e227"
+}
+.glyphicon-xbt:before {
+ content: "\e227"
+}
+.glyphicon-yen:before {
+ content: "\00a5"
+}
+.glyphicon-jpy:before {
+ content: "\00a5"
+}
+.glyphicon-ruble:before {
+ content: "\20bd"
+}
+.glyphicon-rub:before {
+ content: "\20bd"
+}
+.glyphicon-scale:before {
+ content: "\e230"
+}
+.glyphicon-ice-lolly:before {
+ content: "\e231"
+}
+.glyphicon-ice-lolly-tasted:before {
+ content: "\e232"
+}
+.glyphicon-education:before {
+ content: "\e233"
+}
+.glyphicon-option-horizontal:before {
+ content: "\e234"
+}
+.glyphicon-option-vertical:before {
+ content: "\e235"
+}
+.glyphicon-menu-hamburger:before {
+ content: "\e236"
+}
+.glyphicon-modal-window:before {
+ content: "\e237"
+}
+.glyphicon-oil:before {
+ content: "\e238"
+}
+.glyphicon-grain:before {
+ content: "\e239"
+}
+.glyphicon-sunglasses:before {
+ content: "\e240"
+}
+.glyphicon-text-size:before {
+ content: "\e241"
+}
+.glyphicon-text-color:before {
+ content: "\e242"
+}
+.glyphicon-text-background:before {
+ content: "\e243"
+}
+.glyphicon-object-align-top:before {
+ content: "\e244"
+}
+.glyphicon-object-align-bottom:before {
+ content: "\e245"
+}
+.glyphicon-object-align-horizontal:before {
+ content: "\e246"
+}
+.glyphicon-object-align-left:before {
+ content: "\e247"
+}
+.glyphicon-object-align-vertical:before {
+ content: "\e248"
+}
+.glyphicon-object-align-right:before {
+ content: "\e249"
+}
+.glyphicon-triangle-right:before {
+ content: "\e250"
+}
+.glyphicon-triangle-left:before {
+ content: "\e251"
+}
+.glyphicon-triangle-bottom:before {
+ content: "\e252"
+}
+.glyphicon-triangle-top:before {
+ content: "\e253"
+}
+.glyphicon-console:before {
+ content: "\e254"
+}
+.glyphicon-superscript:before {
+ content: "\e255"
+}
+.glyphicon-subscript:before {
+ content: "\e256"
+}
+.glyphicon-menu-left:before {
+ content: "\e257"
+}
+.glyphicon-menu-right:before {
+ content: "\e258"
+}
+.glyphicon-menu-down:before {
+ content: "\e259"
+}
+.glyphicon-menu-up:before {
+ content: "\e260"
+}
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box
+}
+:after,
+:before {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box
+}
+html {
+ font-size: 10px;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
+}
+body {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #333;
+ background-color: #fff
+}
+button,
+input,
+select,
+textarea {
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit
+}
+a {
+ color: #337ab7;
+ text-decoration: none
+}
+a:focus,
+a:hover {
+ color: #23527c;
+ text-decoration: underline
+}
+a:focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px
+}
+figure {
+ margin: 0
+}
+img {
+ vertical-align: middle
+}
+.carousel-inner>.item>a>img,
+.carousel-inner>.item>img,
+.img-responsive,
+.thumbnail a>img,
+.thumbnail>img {
+ display: block;
+ max-width: 100%;
+ height: auto
+}
+.img-rounded {
+ border-radius: 6px
+}
+.img-thumbnail {
+ display: inline-block;
+ max-width: 100%;
+ height: auto;
+ padding: 4px;
+ line-height: 1.42857143;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ -webkit-transition: all .2s ease-in-out;
+ -o-transition: all .2s ease-in-out;
+ transition: all .2s ease-in-out
+}
+.img-circle {
+ border-radius: 50%
+}
+hr {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ border: 0;
+ border-top: 1px solid #eee
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0
+}
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto
+}
+[role=button] {
+ cursor: pointer
+}
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: inherit;
+ font-weight: 500;
+ line-height: 1.1;
+ color: inherit
+}
+.h1 .small,
+.h1 small,
+.h2 .small,
+.h2 small,
+.h3 .small,
+.h3 small,
+.h4 .small,
+.h4 small,
+.h5 .small,
+.h5 small,
+.h6 .small,
+.h6 small,
+h1 .small,
+h1 small,
+h2 .small,
+h2 small,
+h3 .small,
+h3 small,
+h4 .small,
+h4 small,
+h5 .small,
+h5 small,
+h6 .small,
+h6 small {
+ font-weight: 400;
+ line-height: 1;
+ color: #777
+}
+.h1,
+.h2,
+.h3,
+h1,
+h2,
+h3 {
+ margin-top: 20px;
+ margin-bottom: 10px
+}
+.h1 .small,
+.h1 small,
+.h2 .small,
+.h2 small,
+.h3 .small,
+.h3 small,
+h1 .small,
+h1 small,
+h2 .small,
+h2 small,
+h3 .small,
+h3 small {
+ font-size: 65%
+}
+.h4,
+.h5,
+.h6,
+h4,
+h5,
+h6 {
+ margin-top: 10px;
+ margin-bottom: 10px
+}
+.h4 .small,
+.h4 small,
+.h5 .small,
+.h5 small,
+.h6 .small,
+.h6 small,
+h4 .small,
+h4 small,
+h5 .small,
+h5 small,
+h6 .small,
+h6 small {
+ font-size: 75%
+}
+.h1,
+h1 {
+ font-size: 36px
+}
+.h2,
+h2 {
+ font-size: 30px
+}
+.h3,
+h3 {
+ font-size: 24px
+}
+.h4,
+h4 {
+ font-size: 18px
+}
+.h5,
+h5 {
+ font-size: 14px
+}
+.h6,
+h6 {
+ font-size: 12px
+}
+p {
+ margin: 0 0 10px
+}
+.lead {
+ margin-bottom: 20px;
+ font-size: 16px;
+ font-weight: 300;
+ line-height: 1.4
+}
+@media (min-width: 768px) {
+ .lead {
+ font-size: 21px
+ }
+}
+.small,
+small {
+ font-size: 85%
+}
+.mark,
+mark {
+ padding: .2em;
+ background-color: #fcf8e3
+}
+.text-left {
+ text-align: left
+}
+.text-right {
+ text-align: right
+}
+.text-center {
+ text-align: center
+}
+.text-justify {
+ text-align: justify
+}
+.text-nowrap {
+ white-space: nowrap
+}
+.text-lowercase {
+ text-transform: lowercase
+}
+.text-uppercase {
+ text-transform: uppercase
+}
+.text-capitalize {
+ text-transform: capitalize
+}
+.text-muted {
+ color: #777
+}
+.text-primary {
+ color: #337ab7
+}
+a.text-primary:hover {
+ color: #286090
+}
+.text-success {
+ color: #3c763d
+}
+a.text-success:hover {
+ color: #2b542c
+}
+.text-info {
+ color: #31708f
+}
+a.text-info:hover {
+ color: #245269
+}
+.text-warning {
+ color: #8a6d3b
+}
+a.text-warning:hover {
+ color: #66512c
+}
+.text-danger {
+ color: #a94442
+}
+a.text-danger:hover {
+ color: #843534
+}
+.bg-primary {
+ color: #fff;
+ background-color: #337ab7
+}
+a.bg-primary:hover {
+ background-color: #286090
+}
+.bg-success {
+ background-color: #dff0d8
+}
+a.bg-success:hover {
+ background-color: #c1e2b3
+}
+.bg-info {
+ background-color: #d9edf7
+}
+a.bg-info:hover {
+ background-color: #afd9ee
+}
+.bg-warning {
+ background-color: #fcf8e3
+}
+a.bg-warning:hover {
+ background-color: #f7ecb5
+}
+.bg-danger {
+ background-color: #f2dede
+}
+a.bg-danger:hover {
+ background-color: #e4b9b9
+}
+.page-header {
+ padding-bottom: 9px;
+ margin: 40px 0 20px;
+ border-bottom: 1px solid #eee
+}
+ol,
+ul {
+ margin-top: 0;
+ margin-bottom: 10px
+}
+ol ol,
+ol ul,
+ul ol,
+ul ul {
+ margin-bottom: 0
+}
+.list-unstyled {
+ padding-left: 0;
+ list-style: none
+}
+.list-inline {
+ padding-left: 0;
+ margin-left: -5px;
+ list-style: none
+}
+.list-inline>li {
+ display: inline-block;
+ padding-right: 5px;
+ padding-left: 5px
+}
+dl {
+ margin-top: 0;
+ margin-bottom: 20px
+}
+dd,
+dt {
+ line-height: 1.42857143
+}
+dt {
+ font-weight: 700
+}
+dd {
+ margin-left: 0
+}
+@media (min-width: 768px) {
+ .dl-horizontal dt {
+ float: left;
+ width: 160px;
+ overflow: hidden;
+ clear: left;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap
+ }
+ .dl-horizontal dd {
+ margin-left: 180px
+ }
+}
+abbr[data-original-title],
+abbr[title] {
+ cursor: help;
+ border-bottom: 1px dotted #777
+}
+.initialism {
+ font-size: 90%;
+ text-transform: uppercase
+}
+blockquote {
+ padding: 10px 20px;
+ margin: 0 0 20px;
+ font-size: 17.5px;
+ border-left: 5px solid #eee
+}
+blockquote ol:last-child,
+blockquote p:last-child,
+blockquote ul:last-child {
+ margin-bottom: 0
+}
+blockquote .small,
+blockquote footer,
+blockquote small {
+ display: block;
+ font-size: 80%;
+ line-height: 1.42857143;
+ color: #777
+}
+blockquote .small:before,
+blockquote footer:before,
+blockquote small:before {
+ content: '\2014 \00A0'
+}
+.blockquote-reverse,
+blockquote.pull-right {
+ padding-right: 15px;
+ padding-left: 0;
+ text-align: right;
+ border-right: 5px solid #eee;
+ border-left: 0
+}
+.blockquote-reverse .small:before,
+.blockquote-reverse footer:before,
+.blockquote-reverse small:before,
+blockquote.pull-right .small:before,
+blockquote.pull-right footer:before,
+blockquote.pull-right small:before {
+ content: ''
+}
+.blockquote-reverse .small:after,
+.blockquote-reverse footer:after,
+.blockquote-reverse small:after,
+blockquote.pull-right .small:after,
+blockquote.pull-right footer:after,
+blockquote.pull-right small:after {
+ content: '\00A0 \2014'
+}
+address {
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 1.42857143
+}
+code,
+kbd,
+pre,
+samp {
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace
+}
+code {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: #c7254e;
+ background-color: #f9f2f4;
+ border-radius: 4px
+}
+kbd {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: #fff;
+ background-color: #333;
+ border-radius: 3px;
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25)
+}
+kbd kbd {
+ padding: 0;
+ font-size: 100%;
+ font-weight: 700;
+ -webkit-box-shadow: none;
+ box-shadow: none
+}
+pre {
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 1.42857143;
+ color: #333;
+ word-break: break-all;
+ word-wrap: break-word;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border-radius: 4px
+}
+pre code {
+ padding: 0;
+ font-size: inherit;
+ color: inherit;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border-radius: 0
+}
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll
+}
+.container {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-right: auto;
+ margin-left: auto
+}
+@media (min-width: 768px) {
+ .container {
+ width: 750px
+ }
+}
+@media (min-width: 992px) {
+ .container {
+ width: 970px
+ }
+}
+@media (min-width: 1200px) {
+ .container {
+ width: 1230px
+ }
+}
+.container-fluid {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-right: auto;
+ margin-left: auto
+}
+.row {
+ margin-right: -15px;
+ margin-left: -15px
+}
+.col-lg-1,
+.col-lg-10,
+.col-lg-11,
+.col-lg-12,
+.col-lg-2,
+.col-lg-3,
+.col-lg-4,
+.col-lg-5,
+.col-lg-6,
+.col-lg-7,
+.col-lg-8,
+.col-lg-9,
+.col-md-1,
+.col-md-10,
+.col-md-11,
+.col-md-12,
+.col-md-2,
+.col-md-3,
+.col-md-4,
+.col-md-5,
+.col-md-6,
+.col-md-7,
+.col-md-8,
+.col-md-9,
+.col-sm-1,
+.col-sm-10,
+.col-sm-11,
+.col-sm-12,
+.col-sm-2,
+.col-sm-3,
+.col-sm-4,
+.col-sm-5,
+.col-sm-6,
+.col-sm-7,
+.col-sm-8,
+.col-sm-9,
+.col-xs-1,
+.col-xs-10,
+.col-xs-11,
+.col-xs-12,
+.col-xs-2,
+.col-xs-3,
+.col-xs-4,
+.col-xs-5,
+.col-xs-6,
+.col-xs-7,
+.col-xs-8,
+.col-xs-9 {
+ position: relative;
+ min-height: 1px;
+ padding-right: 15px;
+ padding-left: 15px
+}
+.col-xs-1,
+.col-xs-10,
+.col-xs-11,
+.col-xs-12,
+.col-xs-2,
+.col-xs-3,
+.col-xs-4,
+.col-xs-5,
+.col-xs-6,
+.col-xs-7,
+.col-xs-8,
+.col-xs-9 {
+ float: left
+}
+.col-xs-12 {
+ width: 100%
+}
+.col-xs-11 {
+ width: 91.66666667%
+}
+.col-xs-10 {
+ width: 83.33333333%
+}
+.col-xs-9 {
+ width: 75%
+}
+.col-xs-8 {
+ width: 66.66666667%
+}
+.col-xs-7 {
+ width: 58.33333333%
+}
+.col-xs-6 {
+ width: 50%
+}
+.col-xs-5 {
+ width: 41.66666667%
+}
+.col-xs-4 {
+ width: 33.33333333%
+}
+.col-xs-3 {
+ width: 25%
+}
+.col-xs-2 {
+ width: 16.66666667%
+}
+.col-xs-1 {
+ width: 8.33333333%
+}
+.col-xs-pull-12 {
+ right: 100%
+}
+.col-xs-pull-11 {
+ right: 91.66666667%
+}
+.col-xs-pull-10 {
+ right: 83.33333333%
+}
+.col-xs-pull-9 {
+ right: 75%
+}
+.col-xs-pull-8 {
+ right: 66.66666667%
+}
+.col-xs-pull-7 {
+ right: 58.33333333%
+}
+.col-xs-pull-6 {
+ right: 50%
+}
+.col-xs-pull-5 {
+ right: 41.66666667%
+}
+.col-xs-pull-4 {
+ right: 33.33333333%
+}
+.col-xs-pull-3 {
+ right: 25%
+}
+.col-xs-pull-2 {
+ right: 16.66666667%
+}
+.col-xs-pull-1 {
+ right: 8.33333333%
+}
+.col-xs-pull-0 {
+ right: auto
+}
+.col-xs-push-12 {
+ left: 100%
+}
+.col-xs-push-11 {
+ left: 91.66666667%
+}
+.col-xs-push-10 {
+ left: 83.33333333%
+}
+.col-xs-push-9 {
+ left: 75%
+}
+.col-xs-push-8 {
+ left: 66.66666667%
+}
+.col-xs-push-7 {
+ left: 58.33333333%
+}
+.col-xs-push-6 {
+ left: 50%
+}
+.col-xs-push-5 {
+ left: 41.66666667%
+}
+.col-xs-push-4 {
+ left: 33.33333333%
+}
+.col-xs-push-3 {
+ left: 25%
+}
+.col-xs-push-2 {
+ left: 16.66666667%
+}
+.col-xs-push-1 {
+ left: 8.33333333%
+}
+.col-xs-push-0 {
+ left: auto
+}
+.col-xs-offset-12 {
+ margin-left: 100%
+}
+.col-xs-offset-11 {
+ margin-left: 91.66666667%
+}
+.col-xs-offset-10 {
+ margin-left: 83.33333333%
+}
+.col-xs-offset-9 {
+ margin-left: 75%
+}
+.col-xs-offset-8 {
+ margin-left: 66.66666667%
+}
+.col-xs-offset-7 {
+ margin-left: 58.33333333%
+}
+.col-xs-offset-6 {
+ margin-left: 50%
+}
+.col-xs-offset-5 {
+ margin-left: 41.66666667%
+}
+.col-xs-offset-4 {
+ margin-left: 33.33333333%
+}
+.col-xs-offset-3 {
+ margin-left: 25%
+}
+.col-xs-offset-2 {
+ margin-left: 16.66666667%
+}
+.col-xs-offset-1 {
+ margin-left: 8.33333333%
+}
+.col-xs-offset-0 {
+ margin-left: 0
+}
+@media (min-width: 768px) {
+ .col-sm-1,
+ .col-sm-10,
+ .col-sm-11,
+ .col-sm-12,
+ .col-sm-2,
+ .col-sm-3,
+ .col-sm-4,
+ .col-sm-5,
+ .col-sm-6,
+ .col-sm-7,
+ .col-sm-8,
+ .col-sm-9 {
+ float: left
+ }
+ .col-sm-12 {
+ width: 100%
+ }
+ .col-sm-11 {
+ width: 91.66666667%
+ }
+ .col-sm-10 {
+ width: 83.33333333%
+ }
+ .col-sm-9 {
+ width: 75%
+ }
+ .col-sm-8 {
+ width: 66.66666667%
+ }
+ .col-sm-7 {
+ width: 58.33333333%
+ }
+ .col-sm-6 {
+ width: 50%
+ }
+ .col-sm-5 {
+ width: 41.66666667%
+ }
+ .col-sm-4 {
+ width: 33.33333333%
+ }
+ .col-sm-3 {
+ width: 25%
+ }
+ .col-sm-2 {
+ width: 16.66666667%
+ }
+ .col-sm-1 {
+ width: 8.33333333%
+ }
+ .col-sm-pull-12 {
+ right: 100%
+ }
+ .col-sm-pull-11 {
+ right: 91.66666667%
+ }
+ .col-sm-pull-10 {
+ right: 83.33333333%
+ }
+ .col-sm-pull-9 {
+ right: 75%
+ }
+ .col-sm-pull-8 {
+ right: 66.66666667%
+ }
+ .col-sm-pull-7 {
+ right: 58.33333333%
+ }
+ .col-sm-pull-6 {
+ right: 50%
+ }
+ .col-sm-pull-5 {
+ right: 41.66666667%
+ }
+ .col-sm-pull-4 {
+ right: 33.33333333%
+ }
+ .col-sm-pull-3 {
+ right: 25%
+ }
+ .col-sm-pull-2 {
+ right: 16.66666667%
+ }
+ .col-sm-pull-1 {
+ right: 8.33333333%
+ }
+ .col-sm-pull-0 {
+ right: auto
+ }
+ .col-sm-push-12 {
+ left: 100%
+ }
+ .col-sm-push-11 {
+ left: 91.66666667%
+ }
+ .col-sm-push-10 {
+ left: 83.33333333%
+ }
+ .col-sm-push-9 {
+ left: 75%
+ }
+ .col-sm-push-8 {
+ left: 66.66666667%
+ }
+ .col-sm-push-7 {
+ left: 58.33333333%
+ }
+ .col-sm-push-6 {
+ left: 50%
+ }
+ .col-sm-push-5 {
+ left: 41.66666667%
+ }
+ .col-sm-push-4 {
+ left: 33.33333333%
+ }
+ .col-sm-push-3 {
+ left: 25%
+ }
+ .col-sm-push-2 {
+ left: 16.66666667%
+ }
+ .col-sm-push-1 {
+ left: 8.33333333%
+ }
+ .col-sm-push-0 {
+ left: auto
+ }
+ .col-sm-offset-12 {
+ margin-left: 100%
+ }
+ .col-sm-offset-11 {
+ margin-left: 91.66666667%
+ }
+ .col-sm-offset-10 {
+ margin-left: 83.33333333%
+ }
+ .col-sm-offset-9 {
+ margin-left: 75%
+ }
+ .col-sm-offset-8 {
+ margin-left: 66.66666667%
+ }
+ .col-sm-offset-7 {
+ margin-left: 58.33333333%
+ }
+ .col-sm-offset-6 {
+ margin-left: 50%
+ }
+ .col-sm-offset-5 {
+ margin-left: 41.66666667%
+ }
+ .col-sm-offset-4 {
+ margin-left: 33.33333333%
+ }
+ .col-sm-offset-3 {
+ margin-left: 25%
+ }
+ .col-sm-offset-2 {
+ margin-left: 16.66666667%
+ }
+ .col-sm-offset-1 {
+ margin-left: 8.33333333%
+ }
+ .col-sm-offset-0 {
+ margin-left: 0
+ }
+}
+@media (min-width: 992px) {
+ .col-md-1,
+ .col-md-10,
+ .col-md-11,
+ .col-md-12,
+ .col-md-2,
+ .col-md-3,
+ .col-md-4,
+ .col-md-5,
+ .col-md-6,
+ .col-md-7,
+ .col-md-8,
+ .col-md-9 {
+ float: left
+ }
+ .col-md-12 {
+ width: 100%
+ }
+ .col-md-11 {
+ width: 91.66666667%
+ }
+ .col-md-10 {
+ width: 83.33333333%
+ }
+ .col-md-9 {
+ width: 75%
+ }
+ .col-md-8 {
+ width: 66.66666667%
+ }
+ .col-md-7 {
+ width: 58.33333333%
+ }
+ .col-md-6 {
+ width: 50%
+ }
+ .col-md-5 {
+ width: 41.66666667%
+ }
+ .col-md-4 {
+ width: 33.33333333%
+ }
+ .col-md-3 {
+ width: 25%
+ }
+ .col-md-2 {
+ width: 16.66666667%
+ }
+ .col-md-1 {
+ width: 8.33333333%
+ }
+ .col-md-pull-12 {
+ right: 100%
+ }
+ .col-md-pull-11 {
+ right: 91.66666667%
+ }
+ .col-md-pull-10 {
+ right: 83.33333333%
+ }
+ .col-md-pull-9 {
+ right: 75%
+ }
+ .col-md-pull-8 {
+ right: 66.66666667%
+ }
+ .col-md-pull-7 {
+ right: 58.33333333%
+ }
+ .col-md-pull-6 {
+ right: 50%
+ }
+ .col-md-pull-5 {
+ right: 41.66666667%
+ }
+ .col-md-pull-4 {
+ right: 33.33333333%
+ }
+ .col-md-pull-3 {
+ right: 25%
+ }
+ .col-md-pull-2 {
+ right: 16.66666667%
+ }
+ .col-md-pull-1 {
+ right: 8.33333333%
+ }
+ .col-md-pull-0 {
+ right: auto
+ }
+ .col-md-push-12 {
+ left: 100%
+ }
+ .col-md-push-11 {
+ left: 91.66666667%
+ }
+ .col-md-push-10 {
+ left: 83.33333333%
+ }
+ .col-md-push-9 {
+ left: 75%
+ }
+ .col-md-push-8 {
+ left: 66.66666667%
+ }
+ .col-md-push-7 {
+ left: 58.33333333%
+ }
+ .col-md-push-6 {
+ left: 50%
+ }
+ .col-md-push-5 {
+ left: 41.66666667%
+ }
+ .col-md-push-4 {
+ left: 33.33333333%
+ }
+ .col-md-push-3 {
+ left: 25%
+ }
+ .col-md-push-2 {
+ left: 16.66666667%
+ }
+ .col-md-push-1 {
+ left: 8.33333333%
+ }
+ .col-md-push-0 {
+ left: auto
+ }
+ .col-md-offset-12 {
+ margin-left: 100%
+ }
+ .col-md-offset-11 {
+ margin-left: 91.66666667%
+ }
+ .col-md-offset-10 {
+ margin-left: 83.33333333%
+ }
+ .col-md-offset-9 {
+ margin-left: 75%
+ }
+ .col-md-offset-8 {
+ margin-left: 66.66666667%
+ }
+ .col-md-offset-7 {
+ margin-left: 58.33333333%
+ }
+ .col-md-offset-6 {
+ margin-left: 50%
+ }
+ .col-md-offset-5 {
+ margin-left: 41.66666667%
+ }
+ .col-md-offset-4 {
+ margin-left: 33.33333333%
+ }
+ .col-md-offset-3 {
+ margin-left: 25%
+ }
+ .col-md-offset-2 {
+ margin-left: 16.66666667%
+ }
+ .col-md-offset-1 {
+ margin-left: 8.33333333%
+ }
+ .col-md-offset-0 {
+ margin-left: 0
+ }
+}
+@media (min-width: 1200px) {
+ .col-lg-1,
+ .col-lg-10,
+ .col-lg-11,
+ .col-lg-12,
+ .col-lg-2,
+ .col-lg-3,
+ .col-lg-4,
+ .col-lg-5,
+ .col-lg-6,
+ .col-lg-7,
+ .col-lg-8,
+ .col-lg-9 {
+ float: left
+ }
+ .col-lg-12 {
+ width: 100%
+ }
+ .col-lg-11 {
+ width: 91.66666667%
+ }
+ .col-lg-10 {
+ width: 83.33333333%
+ }
+ .col-lg-9 {
+ width: 75%
+ }
+ .col-lg-8 {
+ width: 66.66666667%
+ }
+ .col-lg-7 {
+ width: 58.33333333%
+ }
+ .col-lg-6 {
+ width: 50%
+ }
+ .col-lg-5 {
+ width: 41.66666667%
+ }
+ .col-lg-4 {
+ width: 33.33333333%
+ }
+ .col-lg-3 {
+ width: 25%
+ }
+ .col-lg-2 {
+ width: 16.66666667%
+ }
+ .col-lg-1 {
+ width: 8.33333333%
+ }
+ .col-lg-pull-12 {
+ right: 100%
+ }
+ .col-lg-pull-11 {
+ right: 91.66666667%
+ }
+ .col-lg-pull-10 {
+ right: 83.33333333%
+ }
+ .col-lg-pull-9 {
+ right: 75%
+ }
+ .col-lg-pull-8 {
+ right: 66.66666667%
+ }
+ .col-lg-pull-7 {
+ right: 58.33333333%
+ }
+ .col-lg-pull-6 {
+ right: 50%
+ }
+ .col-lg-pull-5 {
+ right: 41.66666667%
+ }
+ .col-lg-pull-4 {
+ right: 33.33333333%
+ }
+ .col-lg-pull-3 {
+ right: 25%
+ }
+ .col-lg-pull-2 {
+ right: 16.66666667%
+ }
+ .col-lg-pull-1 {
+ right: 8.33333333%
+ }
+ .col-lg-pull-0 {
+ right: auto
+ }
+ .col-lg-push-12 {
+ left: 100%
+ }
+ .col-lg-push-11 {
+ left: 91.66666667%
+ }
+ .col-lg-push-10 {
+ left: 83.33333333%
+ }
+ .col-lg-push-9 {
+ left: 75%
+ }
+ .col-lg-push-8 {
+ left: 66.66666667%
+ }
+ .col-lg-push-7 {
+ left: 58.33333333%
+ }
+ .col-lg-push-6 {
+ left: 50%
+ }
+ .col-lg-push-5 {
+ left: 41.66666667%
+ }
+ .col-lg-push-4 {
+ left: 33.33333333%
+ }
+ .col-lg-push-3 {
+ left: 25%
+ }
+ .col-lg-push-2 {
+ left: 16.66666667%
+ }
+ .col-lg-push-1 {
+ left: 8.33333333%
+ }
+ .col-lg-push-0 {
+ left: auto
+ }
+ .col-lg-offset-12 {
+ margin-left: 100%
+ }
+ .col-lg-offset-11 {
+ margin-left: 91.66666667%
+ }
+ .col-lg-offset-10 {
+ margin-left: 83.33333333%
+ }
+ .col-lg-offset-9 {
+ margin-left: 75%
+ }
+ .col-lg-offset-8 {
+ margin-left: 66.66666667%
+ }
+ .col-lg-offset-7 {
+ margin-left: 58.33333333%
+ }
+ .col-lg-offset-6 {
+ margin-left: 50%
+ }
+ .col-lg-offset-5 {
+ margin-left: 41.66666667%
+ }
+ .col-lg-offset-4 {
+ margin-left: 33.33333333%
+ }
+ .col-lg-offset-3 {
+ margin-left: 25%
+ }
+ .col-lg-offset-2 {
+ margin-left: 16.66666667%
+ }
+ .col-lg-offset-1 {
+ margin-left: 8.33333333%
+ }
+ .col-lg-offset-0 {
+ margin-left: 0
+ }
+}
+table {
+ background-color: transparent
+}
+caption {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ color: #777;
+ text-align: left
+}
+th {
+ text-align: left
+}
+.table {
+ width: 100%;
+ max-width: 100%;
+ margin-bottom: 20px
+}
+.table>tbody>tr>td,
+.table>tbody>tr>th,
+.table>tfoot>tr>td,
+.table>tfoot>tr>th,
+.table>thead>tr>td,
+.table>thead>tr>th {
+ padding: 8px;
+ line-height: 1.42857143;
+ vertical-align: top;
+ border-top: 1px solid #ddd
+}
+.table>thead>tr>th {
+ vertical-align: bottom;
+ border-bottom: 2px solid #ddd
+}
+.table>caption+thead>tr:first-child>td,
+.table>caption+thead>tr:first-child>th,
+.table>colgroup+thead>tr:first-child>td,
+.table>colgroup+thead>tr:first-child>th,
+.table>thead:first-child>tr:first-child>td,
+.table>thead:first-child>tr:first-child>th {
+ border-top: 0
+}
+.table>tbody+tbody {
+ border-top: 2px solid #ddd
+}
+.table .table {
+ background-color: #fff
+}
+.table-condensed>tbody>tr>td,
+.table-condensed>tbody>tr>th,
+.table-condensed>tfoot>tr>td,
+.table-condensed>tfoot>tr>th,
+.table-condensed>thead>tr>td,
+.table-condensed>thead>tr>th {
+ padding: 5px
+}
+.table-bordered {
+ border: 1px solid #ddd
+}
+.table-bordered>tbody>tr>td,
+.table-bordered>tbody>tr>th,
+.table-bordered>tfoot>tr>td,
+.table-bordered>tfoot>tr>th,
+.table-bordered>thead>tr>td,
+.table-bordered>thead>tr>th {
+ border: 1px solid #ddd
+}
+.table-bordered>thead>tr>td,
+.table-bordered>thead>tr>th {
+ border-bottom-width: 2px
+}
+.table-striped>tbody>tr:nth-of-type(odd) {
+ background-color: #f9f9f9
+}
+.table-hover>tbody>tr:hover {
+ background-color: #f5f5f5
+}
+table col[class*=col-] {
+ position: static;
+ display: table-column;
+ float: none
+}
+table td[class*=col-],
+table th[class*=col-] {
+ position: static;
+ display: table-cell;
+ float: none
+}
+.table>tbody>tr.active>td,
+.table>tbody>tr.active>th,
+.table>tbody>tr>td.active,
+.table>tbody>tr>th.active,
+.table>tfoot>tr.active>td,
+.table>tfoot>tr.active>th,
+.table>tfoot>tr>td.active,
+.table>tfoot>tr>th.active,
+.table>thead>tr.active>td,
+.table>thead>tr.active>th,
+.table>thead>tr>td.active,
+.table>thead>tr>th.active {
+ background-color: #f5f5f5
+}
+.table-hover>tbody>tr.active:hover>td,
+.table-hover>tbody>tr.active:hover>th,
+.table-hover>tbody>tr:hover>.active,
+.table-hover>tbody>tr>td.active:hover,
+.table-hover>tbody>tr>th.active:hover {
+ background-color: #e8e8e8
+}
+.table>tbody>tr.success>td,
+.table>tbody>tr.success>th,
+.table>tbody>tr>td.success,
+.table>tbody>tr>th.success,
+.table>tfoot>tr.success>td,
+.table>tfoot>tr.success>th,
+.table>tfoot>tr>td.success,
+.table>tfoot>tr>th.success,
+.table>thead>tr.success>td,
+.table>thead>tr.success>th,
+.table>thead>tr>td.success,
+.table>thead>tr>th.success {
+ background-color: #dff0d8
+}
+.table-hover>tbody>tr.success:hover>td,
+.table-hover>tbody>tr.success:hover>th,
+.table-hover>tbody>tr:hover>.success,
+.table-hover>tbody>tr>td.success:hover,
+.table-hover>tbody>tr>th.success:hover {
+ background-color: #d0e9c6
+}
+.table>tbody>tr.info>td,
+.table>tbody>tr.info>th,
+.table>tbody>tr>td.info,
+.table>tbody>tr>th.info,
+.table>tfoot>tr.info>td,
+.table>tfoot>tr.info>th,
+.table>tfoot>tr>td.info,
+.table>tfoot>tr>th.info,
+.table>thead>tr.info>td,
+.table>thead>tr.info>th,
+.table>thead>tr>td.info,
+.table>thead>tr>th.info {
+ background-color: #d9edf7
+}
+.table-hover>tbody>tr.info:hover>td,
+.table-hover>tbody>tr.info:hover>th,
+.table-hover>tbody>tr:hover>.info,
+.table-hover>tbody>tr>td.info:hover,
+.table-hover>tbody>tr>th.info:hover {
+ background-color: #c4e3f3
+}
+.table>tbody>tr.warning>td,
+.table>tbody>tr.warning>th,
+.table>tbody>tr>td.warning,
+.table>tbody>tr>th.warning,
+.table>tfoot>tr.warning>td,
+.table>tfoot>tr.warning>th,
+.table>tfoot>tr>td.warning,
+.table>tfoot>tr>th.warning,
+.table>thead>tr.warning>td,
+.table>thead>tr.warning>th,
+.table>thead>tr>td.warning,
+.table>thead>tr>th.warning {
+ background-color: #fcf8e3
+}
+.table-hover>tbody>tr.warning:hover>td,
+.table-hover>tbody>tr.warning:hover>th,
+.table-hover>tbody>tr:hover>.warning,
+.table-hover>tbody>tr>td.warning:hover,
+.table-hover>tbody>tr>th.warning:hover {
+ background-color: #faf2cc
+}
+.table>tbody>tr.danger>td,
+.table>tbody>tr.danger>th,
+.table>tbody>tr>td.danger,
+.table>tbody>tr>th.danger,
+.table>tfoot>tr.danger>td,
+.table>tfoot>tr.danger>th,
+.table>tfoot>tr>td.danger,
+.table>tfoot>tr>th.danger,
+.table>thead>tr.danger>td,
+.table>thead>tr.danger>th,
+.table>thead>tr>td.danger,
+.table>thead>tr>th.danger {
+ background-color: #f2dede
+}
+.table-hover>tbody>tr.danger:hover>td,
+.table-hover>tbody>tr.danger:hover>th,
+.table-hover>tbody>tr:hover>.danger,
+.table-hover>tbody>tr>td.danger:hover,
+.table-hover>tbody>tr>th.danger:hover {
+ background-color: #ebcccc
+}
+.table-responsive {
+ min-height: .01%;
+ overflow-x: auto
+}
+@media screen and (max-width: 767px) {
+ .table-responsive {
+ width: 100%;
+ margin-bottom: 15px;
+ overflow-y: hidden;
+ -ms-overflow-style: -ms-autohiding-scrollbar;
+ border: 1px solid #ddd
+ }
+ .table-responsive>.table {
+ margin-bottom: 0
+ }
+ .table-responsive>.table>tbody>tr>td,
+ .table-responsive>.table>tbody>tr>th,
+ .table-responsive>.table>tfoot>tr>td,
+ .table-responsive>.table>tfoot>tr>th,
+ .table-responsive>.table>thead>tr>td,
+ .table-responsive>.table>thead>tr>th {
+ white-space: nowrap
+ }
+ .table-responsive>.table-bordered {
+ border: 0
+ }
+ .table-responsive>.table-bordered>tbody>tr>td:first-child,
+ .table-responsive>.table-bordered>tbody>tr>th:first-child,
+ .table-responsive>.table-bordered>tfoot>tr>td:first-child,
+ .table-responsive>.table-bordered>tfoot>tr>th:first-child,
+ .table-responsive>.table-bordered>thead>tr>td:first-child,
+ .table-responsive>.table-bordered>thead>tr>th:first-child {
+ border-left: 0
+ }
+ .table-responsive>.table-bordered>tbody>tr>td:last-child,
+ .table-responsive>.table-bordered>tbody>tr>th:last-child,
+ .table-responsive>.table-bordered>tfoot>tr>td:last-child,
+ .table-responsive>.table-bordered>tfoot>tr>th:last-child,
+ .table-responsive>.table-bordered>thead>tr>td:last-child,
+ .table-responsive>.table-bordered>thead>tr>th:last-child {
+ border-right: 0
+ }
+ .table-responsive>.table-bordered>tbody>tr:last-child>td,
+ .table-responsive>.table-bordered>tbody>tr:last-child>th,
+ .table-responsive>.table-bordered>tfoot>tr:last-child>td,
+ .table-responsive>.table-bordered>tfoot>tr:last-child>th {
+ border-bottom: 0
+ }
+}
+fieldset {
+ min-width: 0;
+ padding: 0;
+ margin: 0;
+ border: 0
+}
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: inherit;
+ color: #333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5
+}
+label {
+ display: inline-block;
+ max-width: 100%;
+ margin-bottom: 5px;
+ font-weight: 700
+}
+input[type=search] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box
+}
+input[type=checkbox],
+input[type=radio] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ line-height: normal
+}
+input[type=file] {
+ display: block
+}
+input[type=range] {
+ display: block;
+ width: 100%
+}
+select[multiple],
+select[size] {
+ height: auto
+}
+input[type=file]:focus,
+input[type=checkbox]:focus,
+input[type=radio]:focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px
+}
+output {
+ display: block;
+ padding-top: 7px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555
+}
+.form-control {
+ display: block;
+ width: 100%;
+ height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555;
+ background-color: #fff;
+ background-image: none;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
+ -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s
+}
+.form-control:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6)
+}
+.form-control::-moz-placeholder {
+ color: #999;
+ opacity: 1
+}
+.form-control:-ms-input-placeholder {
+ color: #999
+}
+.form-control::-webkit-input-placeholder {
+ color: #999
+}
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+ background-color: #eee;
+ opacity: 1
+}
+.form-control[disabled],
+fieldset[disabled] .form-control {
+ cursor: not-allowed
+}
+textarea.form-control {
+ height: auto
+}
+input[type=search] {
+ -webkit-appearance: none
+}
+@media screen and (-webkit-min-device-pixel-ratio: 0) {
+ input[type=date],
+ input[type=time],
+ input[type=datetime-local],
+ input[type=month] {
+ line-height: 34px
+ }
+ .input-group-sm input[type=date],
+ .input-group-sm input[type=time],
+ .input-group-sm input[type=datetime-local],
+ .input-group-sm input[type=month],
+ input[type=date].input-sm,
+ input[type=time].input-sm,
+ input[type=datetime-local].input-sm,
+ input[type=month].input-sm {
+ line-height: 30px
+ }
+ .input-group-lg input[type=date],
+ .input-group-lg input[type=time],
+ .input-group-lg input[type=datetime-local],
+ .input-group-lg input[type=month],
+ input[type=date].input-lg,
+ input[type=time].input-lg,
+ input[type=datetime-local].input-lg,
+ input[type=month].input-lg {
+ line-height: 46px
+ }
+}
+.form-group {
+ margin-bottom: 15px
+}
+.checkbox,
+.radio {
+ position: relative;
+ display: block;
+ margin-top: 10px;
+ margin-bottom: 10px
+}
+.checkbox label,
+.radio label {
+ min-height: 20px;
+ padding-left: 20px;
+ margin-bottom: 0;
+ font-weight: 400;
+ cursor: pointer
+}
+.checkbox input[type=checkbox],
+.checkbox-inline input[type=checkbox],
+.radio input[type=radio],
+.radio-inline input[type=radio] {
+ position: absolute;
+ margin-top: 4px \9;
+ margin-left: -20px
+}
+.checkbox+.checkbox,
+.radio+.radio {
+ margin-top: -5px
+}
+.checkbox-inline,
+.radio-inline {
+ position: relative;
+ display: inline-block;
+ padding-left: 20px;
+ margin-bottom: 0;
+ font-weight: 400;
+ vertical-align: middle;
+ cursor: pointer
+}
+.checkbox-inline+.checkbox-inline,
+.radio-inline+.radio-inline {
+ margin-top: 0;
+ margin-left: 10px
+}
+fieldset[disabled] input[type=checkbox],
+fieldset[disabled] input[type=radio],
+input[type=checkbox].disabled,
+input[type=checkbox][disabled],
+input[type=radio].disabled,
+input[type=radio][disabled] {
+ cursor: not-allowed
+}
+.checkbox-inline.disabled,
+.radio-inline.disabled,
+fieldset[disabled] .checkbox-inline,
+fieldset[disabled] .radio-inline {
+ cursor: not-allowed
+}
+.checkbox.disabled label,
+.radio.disabled label,
+fieldset[disabled] .checkbox label,
+fieldset[disabled] .radio label {
+ cursor: not-allowed
+}
+.form-control-static {
+ min-height: 34px;
+ padding-top: 7px;
+ padding-bottom: 7px;
+ margin-bottom: 0
+}
+.form-control-static.input-lg,
+.form-control-static.input-sm {
+ padding-right: 0;
+ padding-left: 0
+}
+.input-sm {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px
+}
+select.input-sm {
+ height: 30px;
+ line-height: 30px
+}
+select[multiple].input-sm,
+textarea.input-sm {
+ height: auto
+}
+.form-group-sm .form-control {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px
+}
+select.form-group-sm .form-control {
+ height: 30px;
+ line-height: 30px
+}
+select[multiple].form-group-sm .form-control,
+textarea.form-group-sm .form-control {
+ height: auto
+}
+.form-group-sm .form-control-static {
+ height: 30px;
+ min-height: 32px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5
+}
+.input-lg {
+ height: 46px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px
+}
+select.input-lg {
+ height: 46px;
+ line-height: 46px
+}
+select[multiple].input-lg,
+textarea.input-lg {
+ height: auto
+}
+.form-group-lg .form-control {
+ height: 46px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px
+}
+select.form-group-lg .form-control {
+ height: 46px;
+ line-height: 46px
+}
+select[multiple].form-group-lg .form-control,
+textarea.form-group-lg .form-control {
+ height: auto
+}
+.form-group-lg .form-control-static {
+ height: 46px;
+ min-height: 38px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333
+}
+.has-feedback {
+ position: relative
+}
+.has-feedback .form-control {
+ padding-right: 42.5px
+}
+.form-control-feedback {
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 2;
+ display: block;
+ width: 34px;
+ height: 34px;
+ line-height: 34px;
+ text-align: center;
+ pointer-events: none
+}
+.input-lg+.form-control-feedback {
+ width: 46px;
+ height: 46px;
+ line-height: 46px
+}
+.input-sm+.form-control-feedback {
+ width: 30px;
+ height: 30px;
+ line-height: 30px
+}
+.has-success .checkbox,
+.has-success .checkbox-inline,
+.has-success .control-label,
+.has-success .help-block,
+.has-success .radio,
+.has-success .radio-inline,
+.has-success.checkbox label,
+.has-success.checkbox-inline label,
+.has-success.radio label,
+.has-success.radio-inline label {
+ color: #3c763d
+}
+.has-success .form-control {
+ border-color: #3c763d;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075)
+}
+.has-success .form-control:focus {
+ border-color: #2b542c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168
+}
+.has-success .input-group-addon {
+ color: #3c763d;
+ background-color: #dff0d8;
+ border-color: #3c763d
+}
+.has-success .form-control-feedback {
+ color: #3c763d
+}
+.has-warning .checkbox,
+.has-warning .checkbox-inline,
+.has-warning .control-label,
+.has-warning .help-block,
+.has-warning .radio,
+.has-warning .radio-inline,
+.has-warning.checkbox label,
+.has-warning.checkbox-inline label,
+.has-warning.radio label,
+.has-warning.radio-inline label {
+ color: #8a6d3b
+}
+.has-warning .form-control {
+ border-color: #8a6d3b;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075)
+}
+.has-warning .form-control:focus {
+ border-color: #66512c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b
+}
+.has-warning .input-group-addon {
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+ border-color: #8a6d3b
+}
+.has-warning .form-control-feedback {
+ color: #8a6d3b
+}
+.has-error .checkbox,
+.has-error .checkbox-inline,
+.has-error .control-label,
+.has-error .help-block,
+.has-error .radio,
+.has-error .radio-inline,
+.has-error.checkbox label,
+.has-error.checkbox-inline label,
+.has-error.radio label,
+.has-error.radio-inline label {
+ color: #a94442
+}
+.has-error .form-control {
+ border-color: #a94442;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075)
+}
+.has-error .form-control:focus {
+ border-color: #843534;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483
+}
+.has-error .input-group-addon {
+ color: #a94442;
+ background-color: #f2dede;
+ border-color: #a94442
+}
+.has-error .form-control-feedback {
+ color: #a94442
+}
+.has-feedback label~.form-control-feedback {
+ top: 25px
+}
+.has-feedback label.sr-only~.form-control-feedback {
+ top: 0
+}
+.help-block {
+ display: block;
+ margin-top: 5px;
+ margin-bottom: 10px;
+ color: #737373
+}
+@media (min-width: 768px) {
+ .form-inline .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle
+ }
+ .form-inline .form-control {
+ display: inline-block;
+ width: auto;
+ vertical-align: middle
+ }
+ .form-inline .form-control-static {
+ display: inline-block
+ }
+ .form-inline .input-group {
+ display: inline-table;
+ vertical-align: middle
+ }
+ .form-inline .input-group .form-control,
+ .form-inline .input-group .input-group-addon,
+ .form-inline .input-group .input-group-btn {
+ width: auto
+ }
+ .form-inline .input-group>.form-control {
+ width: 100%
+ }
+ .form-inline .control-label {
+ margin-bottom: 0;
+ vertical-align: middle
+ }
+ .form-inline .checkbox,
+ .form-inline .radio {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle
+ }
+ .form-inline .checkbox label,
+ .form-inline .radio label {
+ padding-left: 0
+ }
+ .form-inline .checkbox input[type=checkbox],
+ .form-inline .radio input[type=radio] {
+ position: relative;
+ margin-left: 0
+ }
+ .form-inline .has-feedback .form-control-feedback {
+ top: 0
+ }
+}
+.form-horizontal .checkbox,
+.form-horizontal .checkbox-inline,
+.form-horizontal .radio,
+.form-horizontal .radio-inline {
+ padding-top: 7px;
+ margin-top: 0;
+ margin-bottom: 0
+}
+.form-horizontal .checkbox,
+.form-horizontal .radio {
+ min-height: 27px
+}
+.form-horizontal .form-group {
+ margin-right: -15px;
+ margin-left: -15px
+}
+@media (min-width: 768px) {
+ .form-horizontal .control-label {
+ padding-top: 7px;
+ margin-bottom: 0;
+ text-align: right
+ }
+}
+.form-horizontal .has-feedback .form-control-feedback {
+ right: 15px
+}
+@media (min-width: 768px) {
+ .form-horizontal .form-group-lg .control-label {
+ padding-top: 14.33px
+ }
+}
+@media (min-width: 768px) {
+ .form-horizontal .form-group-sm .control-label {
+ padding-top: 6px
+ }
+}
+.btn {
+ display: inline-block;
+ padding: 6px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1.42857143;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -ms-touch-action: manipulation;
+ touch-action: manipulation;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px
+}
+.btn.active.focus,
+.btn.active:focus,
+.btn.focus,
+.btn:active.focus,
+.btn:active:focus,
+.btn:focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px
+}
+.btn.focus,
+.btn:focus,
+.btn:hover {
+ color: #333;
+ text-decoration: none
+}
+.btn.active,
+.btn:active {
+ background-image: none;
+ outline: 0;
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125)
+}
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+ pointer-events: none;
+ cursor: not-allowed;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ opacity: .65
+}
+.btn-default {
+ color: #333;
+ background-color: #fff;
+ border-color: #ccc
+}
+.btn-default.active,
+.btn-default.focus,
+.btn-default:active,
+.btn-default:focus,
+.btn-default:hover,
+.open>.dropdown-toggle.btn-default {
+ color: #333;
+ background-color: #e6e6e6;
+ border-color: #adadad
+}
+.btn-default.active,
+.btn-default:active,
+.open>.dropdown-toggle.btn-default {
+ background-image: none
+}
+.btn-default.disabled,
+.btn-default.disabled.active,
+.btn-default.disabled.focus,
+.btn-default.disabled:active,
+.btn-default.disabled:focus,
+.btn-default.disabled:hover,
+.btn-default[disabled],
+.btn-default[disabled].active,
+.btn-default[disabled].focus,
+.btn-default[disabled]:active,
+.btn-default[disabled]:focus,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default,
+fieldset[disabled] .btn-default.active,
+fieldset[disabled] .btn-default.focus,
+fieldset[disabled] .btn-default:active,
+fieldset[disabled] .btn-default:focus,
+fieldset[disabled] .btn-default:hover {
+ background-color: #fff;
+ border-color: #ccc
+}
+.btn-default .badge {
+ color: #fff;
+ background-color: #333
+}
+.btn-primary {
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #2e6da4
+}
+.btn-primary.active,
+.btn-primary.focus,
+.btn-primary:active,
+.btn-primary:focus,
+.btn-primary:hover,
+.open>.dropdown-toggle.btn-primary {
+ color: #fff;
+ background-color: #286090;
+ border-color: #204d74
+}
+.btn-primary.active,
+.btn-primary:active,
+.open>.dropdown-toggle.btn-primary {
+ background-image: none
+}
+.btn-primary.disabled,
+.btn-primary.disabled.active,
+.btn-primary.disabled.focus,
+.btn-primary.disabled:active,
+.btn-primary.disabled:focus,
+.btn-primary.disabled:hover,
+.btn-primary[disabled],
+.btn-primary[disabled].active,
+.btn-primary[disabled].focus,
+.btn-primary[disabled]:active,
+.btn-primary[disabled]:focus,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary,
+fieldset[disabled] .btn-primary.active,
+fieldset[disabled] .btn-primary.focus,
+fieldset[disabled] .btn-primary:active,
+fieldset[disabled] .btn-primary:focus,
+fieldset[disabled] .btn-primary:hover {
+ background-color: #337ab7;
+ border-color: #2e6da4
+}
+.btn-primary .badge {
+ color: #337ab7;
+ background-color: #fff
+}
+.btn-success {
+ color: #fff;
+ background-color: #5cb85c;
+ border-color: #4cae4c
+}
+.btn-success.active,
+.btn-success.focus,
+.btn-success:active,
+.btn-success:focus,
+.btn-success:hover,
+.open>.dropdown-toggle.btn-success {
+ color: #fff;
+ background-color: #449d44;
+ border-color: #398439
+}
+.btn-success.active,
+.btn-success:active,
+.open>.dropdown-toggle.btn-success {
+ background-image: none
+}
+.btn-success.disabled,
+.btn-success.disabled.active,
+.btn-success.disabled.focus,
+.btn-success.disabled:active,
+.btn-success.disabled:focus,
+.btn-success.disabled:hover,
+.btn-success[disabled],
+.btn-success[disabled].active,
+.btn-success[disabled].focus,
+.btn-success[disabled]:active,
+.btn-success[disabled]:focus,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success,
+fieldset[disabled] .btn-success.active,
+fieldset[disabled] .btn-success.focus,
+fieldset[disabled] .btn-success:active,
+fieldset[disabled] .btn-success:focus,
+fieldset[disabled] .btn-success:hover {
+ background-color: #5cb85c;
+ border-color: #4cae4c
+}
+.btn-success .badge {
+ color: #5cb85c;
+ background-color: #fff
+}
+.btn-info {
+ color: #fff;
+ background-color: #5bc0de;
+ border-color: #46b8da
+}
+.btn-info.active,
+.btn-info.focus,
+.btn-info:active,
+.btn-info:focus,
+.btn-info:hover,
+.open>.dropdown-toggle.btn-info {
+ color: #fff;
+ background-color: #31b0d5;
+ border-color: #269abc
+}
+.btn-info.active,
+.btn-info:active,
+.open>.dropdown-toggle.btn-info {
+ background-image: none
+}
+.btn-info.disabled,
+.btn-info.disabled.active,
+.btn-info.disabled.focus,
+.btn-info.disabled:active,
+.btn-info.disabled:focus,
+.btn-info.disabled:hover,
+.btn-info[disabled],
+.btn-info[disabled].active,
+.btn-info[disabled].focus,
+.btn-info[disabled]:active,
+.btn-info[disabled]:focus,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info,
+fieldset[disabled] .btn-info.active,
+fieldset[disabled] .btn-info.focus,
+fieldset[disabled] .btn-info:active,
+fieldset[disabled] .btn-info:focus,
+fieldset[disabled] .btn-info:hover {
+ background-color: #5bc0de;
+ border-color: #46b8da
+}
+.btn-info .badge {
+ color: #5bc0de;
+ background-color: #fff
+}
+.btn-warning {
+ color: #fff;
+ background-color: #f0ad4e;
+ border-color: #eea236
+}
+.btn-warning.active,
+.btn-warning.focus,
+.btn-warning:active,
+.btn-warning:focus,
+.btn-warning:hover,
+.open>.dropdown-toggle.btn-warning {
+ color: #fff;
+ background-color: #ec971f;
+ border-color: #d58512
+}
+.btn-warning.active,
+.btn-warning:active,
+.open>.dropdown-toggle.btn-warning {
+ background-image: none
+}
+.btn-warning.disabled,
+.btn-warning.disabled.active,
+.btn-warning.disabled.focus,
+.btn-warning.disabled:active,
+.btn-warning.disabled:focus,
+.btn-warning.disabled:hover,
+.btn-warning[disabled],
+.btn-warning[disabled].active,
+.btn-warning[disabled].focus,
+.btn-warning[disabled]:active,
+.btn-warning[disabled]:focus,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning,
+fieldset[disabled] .btn-warning.active,
+fieldset[disabled] .btn-warning.focus,
+fieldset[disabled] .btn-warning:active,
+fieldset[disabled] .btn-warning:focus,
+fieldset[disabled] .btn-warning:hover {
+ background-color: #f0ad4e;
+ border-color: #eea236
+}
+.btn-warning .badge {
+ color: #f0ad4e;
+ background-color: #fff
+}
+.btn-danger {
+ color: #fff;
+ background-color: #d9534f;
+ border-color: #d43f3a
+}
+.btn-danger.active,
+.btn-danger.focus,
+.btn-danger:active,
+.btn-danger:focus,
+.btn-danger:hover,
+.open>.dropdown-toggle.btn-danger {
+ color: #fff;
+ background-color: #c9302c;
+ border-color: #ac2925
+}
+.btn-danger.active,
+.btn-danger:active,
+.open>.dropdown-toggle.btn-danger {
+ background-image: none
+}
+.btn-danger.disabled,
+.btn-danger.disabled.active,
+.btn-danger.disabled.focus,
+.btn-danger.disabled:active,
+.btn-danger.disabled:focus,
+.btn-danger.disabled:hover,
+.btn-danger[disabled],
+.btn-danger[disabled].active,
+.btn-danger[disabled].focus,
+.btn-danger[disabled]:active,
+.btn-danger[disabled]:focus,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger,
+fieldset[disabled] .btn-danger.active,
+fieldset[disabled] .btn-danger.focus,
+fieldset[disabled] .btn-danger:active,
+fieldset[disabled] .btn-danger:focus,
+fieldset[disabled] .btn-danger:hover {
+ background-color: #d9534f;
+ border-color: #d43f3a
+}
+.btn-danger .badge {
+ color: #d9534f;
+ background-color: #fff
+}
+.btn-link {
+ font-weight: 400;
+ color: #337ab7;
+ border-radius: 0
+}
+.btn-link,
+.btn-link.active,
+.btn-link:active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+ background-color: transparent;
+ -webkit-box-shadow: none;
+ box-shadow: none
+}
+.btn-link,
+.btn-link:active,
+.btn-link:focus,
+.btn-link:hover {
+ border-color: transparent
+}
+.btn-link:focus,
+.btn-link:hover {
+ color: #23527c;
+ text-decoration: underline;
+ background-color: transparent
+}
+.btn-link[disabled]:focus,
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:focus,
+fieldset[disabled] .btn-link:hover {
+ color: #777;
+ text-decoration: none
+}
+.btn-group-lg>.btn,
+.btn-lg {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px
+}
+.btn-group-sm>.btn,
+.btn-sm {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px
+}
+.btn-group-xs>.btn,
+.btn-xs {
+ padding: 1px 5px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px
+}
+.btn-block {
+ display: block;
+ width: 100%
+}
+.btn-block+.btn-block {
+ margin-top: 5px
+}
+input[type=button].btn-block,
+input[type=reset].btn-block,
+input[type=submit].btn-block {
+ width: 100%
+}
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity .15s linear;
+ -o-transition: opacity .15s linear;
+ transition: opacity .15s linear
+}
+.fade.in {
+ opacity: 1
+}
+.collapse {
+ display: none
+}
+.collapse.in {
+ display: block
+}
+tr.collapse.in {
+ display: table-row
+}
+tbody.collapse.in {
+ display: table-row-group
+}
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition-timing-function: ease;
+ -o-transition-timing-function: ease;
+ transition-timing-function: ease;
+ -webkit-transition-duration: .35s;
+ -o-transition-duration: .35s;
+ transition-duration: .35s;
+ -webkit-transition-property: height, visibility;
+ -o-transition-property: height, visibility;
+ transition-property: height, visibility
+}
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: 2px;
+ vertical-align: middle;
+ border-top: 4px dashed;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent
+}
+.dropdown,
+.dropup {
+ position: relative
+}
+.dropdown-toggle:focus {
+ outline: 0
+}
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ font-size: 14px;
+ text-align: left;
+ list-style: none;
+ background-color: #fff;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, .15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, .175)
+}
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto
+}
+.dropdown-menu .divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5
+}
+.dropdown-menu>li>a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: 400;
+ line-height: 1.42857143;
+ color: #333;
+ white-space: nowrap
+}
+.dropdown-menu>li>a:focus,
+.dropdown-menu>li>a:hover {
+ color: #262626;
+ text-decoration: none;
+ background-color: #f5f5f5
+}
+.dropdown-menu>.active>a,
+.dropdown-menu>.active>a:focus,
+.dropdown-menu>.active>a:hover {
+ color: #fff;
+ text-decoration: none;
+ background-color: #337ab7;
+ outline: 0
+}
+.dropdown-menu>.disabled>a,
+.dropdown-menu>.disabled>a:focus,
+.dropdown-menu>.disabled>a:hover {
+ color: #777
+}
+.dropdown-menu>.disabled>a:focus,
+.dropdown-menu>.disabled>a:hover {
+ text-decoration: none;
+ cursor: not-allowed;
+ background-color: transparent;
+ background-image: none;
+ filter: progid: DXImageTransform.Microsoft.gradient(enabled=false)
+}
+.open>.dropdown-menu {
+ display: block
+}
+.open>a {
+ outline: 0
+}
+.dropdown-menu-right {
+ right: 0;
+ left: auto
+}
+.dropdown-menu-left {
+ right: auto;
+ left: 0
+}
+.dropdown-header {
+ display: block;
+ padding: 3px 20px;
+ font-size: 12px;
+ line-height: 1.42857143;
+ color: #777;
+ white-space: nowrap
+}
+.dropdown-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 990
+}
+.pull-right>.dropdown-menu {
+ right: 0;
+ left: auto
+}
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ content: "";
+ border-top: 0;
+ border-bottom: 4px solid
+}
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 2px
+}
+@media (min-width: 768px) {
+ .navbar-right .dropdown-menu {
+ right: 0;
+ left: auto
+ }
+ .navbar-right .dropdown-menu-left {
+ right: auto;
+ left: 0
+ }
+}
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle
+}
+.btn-group-vertical>.btn,
+.btn-group>.btn {
+ position: relative;
+ float: left
+}
+.btn-group-vertical>.btn.active,
+.btn-group-vertical>.btn:active,
+.btn-group-vertical>.btn:focus,
+.btn-group-vertical>.btn:hover,
+.btn-group>.btn.active,
+.btn-group>.btn:active,
+.btn-group>.btn:focus,
+.btn-group>.btn:hover {
+ z-index: 2
+}
+.btn-group .btn+.btn,
+.btn-group .btn+.btn-group,
+.btn-group .btn-group+.btn,
+.btn-group .btn-group+.btn-group {
+ margin-left: -1px
+}
+.btn-toolbar {
+ margin-left: -5px
+}
+.btn-toolbar .btn-group,
+.btn-toolbar .input-group {
+ float: left
+}
+.btn-toolbar>.btn,
+.btn-toolbar>.btn-group,
+.btn-toolbar>.input-group {
+ margin-left: 5px
+}
+.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+ border-radius: 0
+}
+.btn-group>.btn:first-child {
+ margin-left: 0
+}
+.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0
+}
+.btn-group>.btn:last-child:not(:first-child),
+.btn-group>.dropdown-toggle:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0
+}
+.btn-group>.btn-group {
+ float: left
+}
+.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn {
+ border-radius: 0
+}
+.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,
+.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0
+}
+.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0
+}
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0
+}
+.btn-group>.btn+.dropdown-toggle {
+ padding-right: 8px;
+ padding-left: 8px
+}
+.btn-group>.btn-lg+.dropdown-toggle {
+ padding-right: 12px;
+ padding-left: 12px
+}
+.btn-group.open .dropdown-toggle {
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125)
+}
+.btn-group.open .dropdown-toggle.btn-link {
+ -webkit-box-shadow: none;
+ box-shadow: none
+}
+.btn .caret {
+ margin-left: 0
+}
+.btn-lg .caret {
+ border-width: 5px 5px 0;
+ border-bottom-width: 0
+}
+.dropup .btn-lg .caret {
+ border-width: 0 5px 5px
+}
+.btn-group-vertical>.btn,
+.btn-group-vertical>.btn-group,
+.btn-group-vertical>.btn-group>.btn {
+ display: block;
+ float: none;
+ width: 100%;
+ max-width: 100%
+}
+.btn-group-vertical>.btn-group>.btn {
+ float: none
+}
+.btn-group-vertical>.btn+.btn,
+.btn-group-vertical>.btn+.btn-group,
+.btn-group-vertical>.btn-group+.btn,
+.btn-group-vertical>.btn-group+.btn-group {
+ margin-top: -1px;
+ margin-left: 0
+}
+.btn-group-vertical>.btn:not(:first-child):not(:last-child) {
+ border-radius: 0
+}
+.btn-group-vertical>.btn:first-child:not(:last-child) {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0
+}
+.btn-group-vertical>.btn:last-child:not(:first-child) {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: 4px
+}
+.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn {
+ border-radius: 0
+}
+.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,
+.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0
+}
+.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0
+}
+.btn-group-justified {
+ display: table;
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: separate
+}
+.btn-group-justified>.btn,
+.btn-group-justified>.btn-group {
+ display: table-cell;
+ float: none;
+ width: 1%
+}
+.btn-group-justified>.btn-group .btn {
+ width: 100%
+}
+.btn-group-justified>.btn-group .dropdown-menu {
+ left: auto
+}
+[data-toggle=buttons]>.btn input[type=checkbox],
+[data-toggle=buttons]>.btn input[type=radio],
+[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],
+[data-toggle=buttons]>.btn-group>.btn input[type=radio] {
+ position: absolute;
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none
+}
+.input-group {
+ position: relative;
+ display: table;
+ border-collapse: separate
+}
+.input-group[class*=col-] {
+ float: none;
+ padding-right: 0;
+ padding-left: 0
+}
+.input-group .form-control {
+ position: relative;
+ z-index: 2;
+ float: left;
+ width: 100%;
+ margin-bottom: 0
+}
+.input-group-lg>.form-control,
+.input-group-lg>.input-group-addon,
+.input-group-lg>.input-group-btn>.btn {
+ height: 46px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.3333333;
+ border-radius: 6px
+}
+select.input-group-lg>.form-control,
+select.input-group-lg>.input-group-addon,
+select.input-group-lg>.input-group-btn>.btn {
+ height: 46px;
+ line-height: 46px
+}
+select[multiple].input-group-lg>.form-control,
+select[multiple].input-group-lg>.input-group-addon,
+select[multiple].input-group-lg>.input-group-btn>.btn,
+textarea.input-group-lg>.form-control,
+textarea.input-group-lg>.input-group-addon,
+textarea.input-group-lg>.input-group-btn>.btn {
+ height: auto
+}
+.input-group-sm>.form-control,
+.input-group-sm>.input-group-addon,
+.input-group-sm>.input-group-btn>.btn {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px
+}
+select.input-group-sm>.form-control,
+select.input-group-sm>.input-group-addon,
+select.input-group-sm>.input-group-btn>.btn {
+ height: 30px;
+ line-height: 30px
+}
+select[multiple].input-group-sm>.form-control,
+select[multiple].input-group-sm>.input-group-addon,
+select[multiple].input-group-sm>.input-group-btn>.btn,
+textarea.input-group-sm>.form-control,
+textarea.input-group-sm>.input-group-addon,
+textarea.input-group-sm>.input-group-btn>.btn {
+ height: auto
+}
+.input-group .form-control,
+.input-group-addon,
+.input-group-btn {
+ display: table-cell
+}
+.input-group .form-control:not(:first-child):not(:last-child),
+.input-group-addon:not(:first-child):not(:last-child),
+.input-group-btn:not(:first-child):not(:last-child) {
+ border-radius: 0
+}
+.input-group-addon,
+.input-group-btn {
+ width: 1%;
+ white-space: nowrap;
+ vertical-align: middle
+}
+.input-group-addon {
+ padding: 6px 12px;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1;
+ color: #555;
+ text-align: center;
+ background-color: #eee;
+ border: 1px solid #ccc;
+ border-radius: 4px
+}
+.input-group-addon.input-sm {
+ padding: 5px 10px;
+ font-size: 12px;
+ border-radius: 3px
+}
+.input-group-addon.input-lg {
+ padding: 10px 16px;
+ font-size: 18px;
+ border-radius: 6px
+}
+.input-group-addon input[type=checkbox],
+.input-group-addon input[type=radio] {
+ margin-top: 0
+}
+.input-group .form-control:first-child,
+.input-group-addon:first-child,
+.input-group-btn:first-child>.btn,
+.input-group-btn:first-child>.btn-group>.btn,
+.input-group-btn:first-child>.dropdown-toggle,
+.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,
+.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0
+}
+.input-group-addon:first-child {
+ border-right: 0
+}
+.input-group .form-control:last-child,
+.input-group-addon:last-child,
+.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,
+.input-group-btn:first-child>.btn:not(:first-child),
+.input-group-btn:last-child>.btn,
+.input-group-btn:last-child>.btn-group>.btn,
+.input-group-btn:last-child>.dropdown-toggle {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0
+}
+.input-group-addon:last-child {
+ border-left: 0
+}
+.input-group-btn {
+ position: relative;
+ font-size: 0;
+ white-space: nowrap
+}
+.input-group-btn>.btn {
+ position: relative
+}
+.input-group-btn>.btn+.btn {
+ margin-left: -1px
+}
+.input-group-btn>.btn:active,
+.input-group-btn>.btn:focus,
+.input-group-btn>.btn:hover {
+ z-index: 2
+}
+.input-group-btn:first-child>.btn,
+.input-group-btn:first-child>.btn-group {
+ margin-right: -1px
+}
+.input-group-btn:last-child>.btn,
+.input-group-btn:last-child>.btn-group {
+ margin-left: -1px
+}
+.nav {
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none
+}
+.nav>li {
+ position: relative;
+ display: block
+}
+.nav>li>a {
+ position: relative;
+ display: block;
+ padding: 10px 15px
+}
+.nav>li>a:focus,
+.nav>li>a:hover {
+ text-decoration: none;
+ background-color: #eee
+}
+.nav>li.disabled>a {
+ color: #777
+}
+.nav>li.disabled>a:focus,
+.nav>li.disabled>a:hover {
+ color: #777;
+ text-decoration: none;
+ cursor: not-allowed;
+ background-color: transparent
+}
+.nav .open>a,
+.nav .open>a:focus,
+.nav .open>a:hover {
+ background-color: #eee;
+ border-color: #337ab7
+}
+.nav .nav-divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5
+}
+.nav>li>a>img {
+ max-width: none
+}
+.nav-tabs {
+ border-bottom: 1px solid #ddd
+}
+.nav-tabs>li {
+ float: left;
+ margin-bottom: -1px
+}
+.nav-tabs>li>a {
+ margin-right: 2px;
+ line-height: 1.42857143;
+ border: 1px solid transparent;
+ border-radius: 4px 4px 0 0
+}
+.nav-tabs>li>a:hover {
+ border-color: #eee #eee #ddd
+}
+.nav-tabs>li.active>a,
+.nav-tabs>li.active>a:focus,
+.nav-tabs>li.active>a:hover {
+ color: #555;
+ cursor: default;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-bottom-color: transparent
+}
+.nav-tabs.nav-justified {
+ width: 100%;
+ border-bottom: 0
+}
+.nav-tabs.nav-justified>li {
+ float: none
+}
+.nav-tabs.nav-justified>li>a {
+ margin-bottom: 5px;
+ text-align: center
+}
+.nav-tabs.nav-justified>.dropdown .dropdown-menu {
+ top: auto;
+ left: auto
+}
+@media (min-width: 768px) {
+ .nav-tabs.nav-justified>li {
+ display: table-cell;
+ width: 1%
+ }
+ .nav-tabs.nav-justified>li>a {
+ margin-bottom: 0
+ }
+}
+.nav-tabs.nav-justified>li>a {
+ margin-right: 0;
+ border-radius: 4px
+}
+.nav-tabs.nav-justified>.active>a,
+.nav-tabs.nav-justified>.active>a:focus,
+.nav-tabs.nav-justified>.active>a:hover {
+ border: 1px solid #ddd
+}
+@media (min-width: 768px) {
+ .nav-tabs.nav-justified>li>a {
+ border-bottom: 1px solid #ddd;
+ border-radius: 4px 4px 0 0
+ }
+ .nav-tabs.nav-justified>.active>a,
+ .nav-tabs.nav-justified>.active>a:focus,
+ .nav-tabs.nav-justified>.active>a:hover {
+ border-bottom-color: #fff
+ }
+}
+.nav-pills>li {
+ float: left
+}
+.nav-pills>li>a {
+ border-radius: 4px
+}
+.nav-pills>li+li {
+ margin-left: 2px
+}
+.nav-pills>li.active>a,
+.nav-pills>li.active>a:focus,
+.nav-pills>li.active>a:hover {
+ color: #fff;
+ background-color: #337ab7
+}
+.nav-stacked>li {
+ float: none
+}
+.nav-stacked>li+li {
+ margin-top: 2px;
+ margin-left: 0
+}
+.nav-justified {
+ width: 100%
+}
+.nav-justified>li {
+ float: none
+}
+.nav-justified>li>a {
+ margin-bottom: 5px;
+ text-align: center
+}
+.nav-justified>.dropdown .dropdown-menu {
+ top: auto;
+ left: auto
+}
+@media (min-width: 768px) {
+ .nav-justified>li {
+ display: table-cell;
+ width: 1%
+ }
+ .nav-justified>li>a {
+ margin-bottom: 0
+ }
+}
+.nav-tabs-justified {
+ border-bottom: 0
+}
+.nav-tabs-justified>li>a {
+ margin-right: 0;
+ border-radius: 4px
+}
+.nav-tabs-justified>.active>a,
+.nav-tabs-justified>.active>a:focus,
+.nav-tabs-justified>.active>a:hover {
+ border: 1px solid #ddd
+}
+@media (min-width: 768px) {
+ .nav-tabs-justified>li>a {
+ border-bottom: 1px solid #ddd;
+ border-radius: 4px 4px 0 0
+ }
+ .nav-tabs-justified>.active>a,
+ .nav-tabs-justified>.active>a:focus,
+ .nav-tabs-justified>.active>a:hover {
+ border-bottom-color: #fff
+ }
+}
+.tab-content>.tab-pane {
+ display: none
+}
+.tab-content>.active {
+ display: block
+}
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0
+}
+.navbar {
+ position: relative;
+ min-height: 50px;
+ margin-bottom: 20px;
+ border: 1px solid transparent
+}
+@media (min-width: 768px) {
+ .navbar {
+ border-radius: 4px
+ }
+}
+@media (min-width: 768px) {
+ .navbar-header {
+ float: left
+ }
+}
+.navbar-collapse {
+ padding-right: 15px;
+ padding-left: 15px;
+ overflow-x: visible;
+ -webkit-overflow-scrolling: touch;
+ border-top: 1px solid transparent;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1)
+}
+.navbar-collapse.in {
+ overflow-y: auto
+}
+@media (min-width: 768px) {
+ .navbar-collapse {
+ width: auto;
+ border-top: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none
+ }
+ .navbar-collapse.collapse {
+ display: block!important;
+ height: auto!important;
+ padding-bottom: 0;
+ overflow: visible!important
+ }
+ .navbar-collapse.in {
+ overflow-y: visible
+ }
+ .navbar-fixed-bottom .navbar-collapse,
+ .navbar-fixed-top .navbar-collapse,
+ .navbar-static-top .navbar-collapse {
+ padding-right: 0;
+ padding-left: 0
+ }
+}
+.navbar-fixed-bottom .navbar-collapse,
+.navbar-fixed-top .navbar-collapse {
+ max-height: 340px
+}
+@media (max-device-width: 480px)and (orientation: landscape) {
+ .navbar-fixed-bottom .navbar-collapse,
+ .navbar-fixed-top .navbar-collapse {
+ max-height: 200px
+ }
+}
+.container-fluid>.navbar-collapse,
+.container-fluid>.navbar-header,
+.container>.navbar-collapse,
+.container>.navbar-header {
+ margin-right: -15px;
+ margin-left: -15px
+}
+@media (min-width: 768px) {
+ .container-fluid>.navbar-collapse,
+ .container-fluid>.navbar-header,
+ .container>.navbar-collapse,
+ .container>.navbar-header {
+ margin-right: 0;
+ margin-left: 0
+ }
+}
+.navbar-static-top {
+ z-index: 1000;
+ border-width: 0 0 1px
+}
+@media (min-width: 768px) {
+ .navbar-static-top {
+ border-radius: 0
+ }
+}
+.navbar-fixed-bottom,
+.navbar-fixed-top {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030
+}
+@media (min-width: 768px) {
+ .navbar-fixed-bottom,
+ .navbar-fixed-top {
+ border-radius: 0
+ }
+}
+.navbar-fixed-top {
+ top: 0;
+ border-width: 0 0 1px
+}
+.navbar-fixed-bottom {
+ bottom: 0;
+ margin-bottom: 0;
+ border-width: 1px 0 0
+}
+.navbar-brand {
+ float: left;
+ height: 50px;
+ padding: 15px 15px;
+ font-size: 18px;
+ line-height: 20px
+}
+.navbar-brand:focus,
+.navbar-brand:hover {
+ text-decoration: none
+}
+.navbar-brand>img {
+ display: block
+}
+@media (min-width: 768px) {
+ .navbar>.container .navbar-brand,
+ .navbar>.container-fluid .navbar-brand {
+ margin-left: -15px
+ }
+}
+.navbar-toggle {
+ position: relative;
+ float: right;
+ padding: 9px 10px;
+ margin-top: 8px;
+ margin-right: 15px;
+ margin-bottom: 8px;
+ background-color: transparent;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px
+}
+.navbar-toggle:focus {
+ outline: 0
+}
+.navbar-toggle .icon-bar {
+ display: block;
+ width: 22px;
+ height: 2px;
+ border-radius: 1px
+}
+.navbar-toggle .icon-bar+.icon-bar {
+ margin-top: 4px
+}
+@media (min-width: 768px) {
+ .navbar-toggle {
+ display: none
+ }
+}
+.navbar-nav {
+ margin: 7.5px -15px
+}
+.navbar-nav>li>a {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ line-height: 20px
+}
+@media (max-width: 767px) {
+ .navbar-nav .open .dropdown-menu {
+ position: static;
+ float: none;
+ width: auto;
+ margin-top: 0;
+ background-color: transparent;
+ border: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none
+ }
+ .navbar-nav .open .dropdown-menu .dropdown-header,
+ .navbar-nav .open .dropdown-menu>li>a {
+ padding: 5px 15px 5px 25px
+ }
+ .navbar-nav .open .dropdown-menu>li>a {
+ line-height: 20px
+ }
+ .navbar-nav .open .dropdown-menu>li>a:focus,
+ .navbar-nav .open .dropdown-menu>li>a:hover {
+ background-image: none
+ }
+}
+@media (min-width: 768px) {
+ .navbar-nav {
+ float: left;
+ margin: 0
+ }
+ .navbar-nav>li {
+ float: left
+ }
+ .navbar-nav>li>a {
+ padding-top: 15px;
+ padding-bottom: 15px
+ }
+}
+.navbar-form {
+ padding: 10px 15px;
+ margin-top: 8px;
+ margin-right: -15px;
+ margin-bottom: 8px;
+ margin-left: -15px;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1)
+}
+@media (min-width: 768px) {
+ .navbar-form .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle
+ }
+ .navbar-form .form-control {
+ display: inline-block;
+ width: auto;
+ vertical-align: middle
+ }
+ .navbar-form .form-control-static {
+ display: inline-block
+ }
+ .navbar-form .input-group {
+ display: inline-table;
+ vertical-align: middle
+ }
+ .navbar-form .input-group .form-control,
+ .navbar-form .input-group .input-group-addon,
+ .navbar-form .input-group .input-group-btn {
+ width: auto
+ }
+ .navbar-form .input-group>.form-control {
+ width: 100%
+ }
+ .navbar-form .control-label {
+ margin-bottom: 0;
+ vertical-align: middle
+ }
+ .navbar-form .checkbox,
+ .navbar-form .radio {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle
+ }
+ .navbar-form .checkbox label,
+ .navbar-form .radio label {
+ padding-left: 0
+ }
+ .navbar-form .checkbox input[type=checkbox],
+ .navbar-form .radio input[type=radio] {
+ position: relative;
+ margin-left: 0
+ }
+ .navbar-form .has-feedback .form-control-feedback {
+ top: 0
+ }
+}
+@media (max-width: 767px) {
+ .navbar-form .form-group {
+ margin-bottom: 5px
+ }
+ .navbar-form .form-group:last-child {
+ margin-bottom: 0
+ }
+}
+@media (min-width: 768px) {
+ .navbar-form {
+ width: auto;
+ padding-top: 0;
+ padding-bottom: 0;
+ margin-right: 0;
+ margin-left: 0;
+ border: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none
+ }
+}
+.navbar-nav>li>.dropdown-menu {
+ margin-top: 0;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0
+}
+.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu {
+ margin-bottom: 0;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0
+}
+.navbar-btn {
+ margin-top: 8px;
+ margin-bottom: 8px
+}
+.navbar-btn.btn-sm {
+ margin-top: 10px;
+ margin-bottom: 10px
+}
+.navbar-btn.btn-xs {
+ margin-top: 14px;
+ margin-bottom: 14px
+}
+.navbar-text {
+ margin-top: 15px;
+ margin-bottom: 15px
+}
+@media (min-width: 768px) {
+ .navbar-text {
+ float: left;
+ margin-right: 15px;
+ margin-left: 15px
+ }
+}
+@media (min-width: 768px) {
+ .navbar-left {
+ float: left!important
+ }
+ .navbar-right {
+ float: right!important;
+ margin-right: -15px
+ }
+ .navbar-right~.navbar-right {
+ margin-right: 0
+ }
+}
+.navbar-default {
+ background-color: #f8f8f8;
+ border-color: #e7e7e7
+}
+.navbar-default .navbar-brand {
+ color: #777
+}
+.navbar-default .navbar-brand:focus,
+.navbar-default .navbar-brand:hover {
+ color: #5e5e5e;
+ background-color: transparent
+}
+.navbar-default .navbar-text {
+ color: #777
+}
+.navbar-default .navbar-nav>li>a {
+ color: #777
+}
+.navbar-default .navbar-nav>li>a:focus,
+.navbar-default .navbar-nav>li>a:hover {
+ color: #333;
+ background-color: transparent
+}
+.navbar-default .navbar-nav>.active>a,
+.navbar-default .navbar-nav>.active>a:focus,
+.navbar-default .navbar-nav>.active>a:hover {
+ color: #555;
+ background-color: #e7e7e7
+}
+.navbar-default .navbar-nav>.disabled>a,
+.navbar-default .navbar-nav>.disabled>a:focus,
+.navbar-default .navbar-nav>.disabled>a:hover {
+ color: #ccc;
+ background-color: transparent
+}
+.navbar-default .navbar-toggle {
+ border-color: #ddd
+}
+.navbar-default .navbar-toggle:focus,
+.navbar-default .navbar-toggle:hover {
+ background-color: #ddd
+}
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #888
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+ border-color: #e7e7e7
+}
+.navbar-default .navbar-nav>.open>a,
+.navbar-default .navbar-nav>.open>a:focus,
+.navbar-default .navbar-nav>.open>a:hover {
+ color: #555;
+ background-color: #e7e7e7
+}
+@media (max-width: 767px) {
+ .navbar-default .navbar-nav .open .dropdown-menu>li>a {
+ color: #777
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,
+ .navbar-default .navbar-nav .open .dropdown-menu>li>a:hover {
+ color: #333;
+ background-color: transparent
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu>.active>a,
+ .navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,
+ .navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover {
+ color: #555;
+ background-color: #e7e7e7
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,
+ .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,
+ .navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover {
+ color: #ccc;
+ background-color: transparent
+ }
+}
+.navbar-default .navbar-link {
+ color: #777
+}
+.navbar-default .navbar-link:hover {
+ color: #333
+}
+.navbar-default .btn-link {
+ color: #777
+}
+.navbar-default .btn-link:focus,
+.navbar-default .btn-link:hover {
+ color: #333
+}
+.navbar-default .btn-link[disabled]:focus,
+.navbar-default .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-default .btn-link:focus,
+fieldset[disabled] .navbar-default .btn-link:hover {
+ color: #ccc
+}
+.navbar-inverse {
+ background-color: #222;
+ border-color: #080808
+}
+.navbar-inverse .navbar-brand {
+ color: #9d9d9d
+}
+.navbar-inverse .navbar-brand:focus,
+.navbar-inverse .navbar-brand:hover {
+ color: #fff;
+ background-color: transparent
+}
+.navbar-inverse .navbar-text {
+ color: #9d9d9d
+}
+.navbar-inverse .navbar-nav>li>a {
+ color: #9d9d9d
+}
+.navbar-inverse .navbar-nav>li>a:focus,
+.navbar-inverse .navbar-nav>li>a:hover {
+ color: #fff;
+ background-color: transparent
+}
+.navbar-inverse .navbar-nav>.active>a,
+.navbar-inverse .navbar-nav>.active>a:focus,
+.navbar-inverse .navbar-nav>.active>a:hover {
+ color: #fff;
+ background-color: #080808
+}
+.navbar-inverse .navbar-nav>.disabled>a,
+.navbar-inverse .navbar-nav>.disabled>a:focus,
+.navbar-inverse .navbar-nav>.disabled>a:hover {
+ color: #444;
+ background-color: transparent
+}
+.navbar-inverse .navbar-toggle {
+ border-color: #333
+}
+.navbar-inverse .navbar-toggle:focus,
+.navbar-inverse .navbar-toggle:hover {
+ background-color: #333
+}
+.navbar-inverse .navbar-toggle .icon-bar {
+ background-color: #fff
+}
+.navbar-inverse .navbar-collapse,
+.navbar-inverse .navbar-form {
+ border-color: #101010
+}
+.navbar-inverse .navbar-nav>.open>a,
+.navbar-inverse .navbar-nav>.open>a:focus,
+.navbar-inverse .navbar-nav>.open>a:hover {
+ color: #fff;
+ background-color: #080808
+}
+@media (max-width: 767px) {
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header {
+ border-color: #080808
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
+ background-color: #080808
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu>li>a {
+ color: #9d9d9d
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,
+ .navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover {
+ color: #fff;
+ background-color: transparent
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover {
+ color: #fff;
+ background-color: #080808
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,
+ .navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover {
+ color: #444;
+ background-color: transparent
+ }
+}
+.navbar-inverse .navbar-link {
+ color: #9d9d9d
+}
+.navbar-inverse .navbar-link:hover {
+ color: #fff
+}
+.navbar-inverse .btn-link {
+ color: #9d9d9d
+}
+.navbar-inverse .btn-link:focus,
+.navbar-inverse .btn-link:hover {
+ color: #fff
+}
+.navbar-inverse .btn-link[disabled]:focus,
+.navbar-inverse .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-inverse .btn-link:focus,
+fieldset[disabled] .navbar-inverse .btn-link:hover {
+ color: #444
+}
+.breadcrumb {
+ padding: 8px 15px;
+ margin-bottom: 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 4px
+}
+.breadcrumb>li {
+ display: inline-block
+}
+.breadcrumb>li+li:before {
+ padding: 0 5px;
+ color: #ccc;
+ content: "/\00a0"
+}
+.breadcrumb>.active {
+ color: #777
+}
+.pagination {
+ display: inline-block;
+ padding-left: 0;
+ margin: 20px 0;
+ border-radius: 4px
+}
+.pagination>li {
+ display: inline
+}
+.pagination>li>a,
+.pagination>li>span {
+ position: relative;
+ float: left;
+ padding: 6px 12px;
+ margin-left: -1px;
+ line-height: 1.42857143;
+ color: #337ab7;
+ text-decoration: none;
+ background-color: #fff;
+ border: 1px solid #ddd
+}
+.pagination>li:first-child>a,
+.pagination>li:first-child>span {
+ margin-left: 0;
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px
+}
+.pagination>li:last-child>a,
+.pagination>li:last-child>span {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px
+}
+.pagination>li>a:focus,
+.pagination>li>a:hover,
+.pagination>li>span:focus,
+.pagination>li>span:hover {
+ color: #23527c;
+ background-color: #eee;
+ border-color: #ddd
+}
+.pagination>.active>a,
+.pagination>.active>a:focus,
+.pagination>.active>a:hover,
+.pagination>.active>span,
+.pagination>.active>span:focus,
+.pagination>.active>span:hover {
+ z-index: 2;
+ color: #fff;
+ cursor: default;
+ background-color: #337ab7;
+ border-color: #337ab7
+}
+.pagination>.disabled>a,
+.pagination>.disabled>a:focus,
+.pagination>.disabled>a:hover,
+.pagination>.disabled>span,
+.pagination>.disabled>span:focus,
+.pagination>.disabled>span:hover {
+ color: #777;
+ cursor: not-allowed;
+ background-color: #fff;
+ border-color: #ddd
+}
+.pagination-lg>li>a,
+.pagination-lg>li>span {
+ padding: 10px 16px;
+ font-size: 18px
+}
+.pagination-lg>li:first-child>a,
+.pagination-lg>li:first-child>span {
+ border-top-left-radius: 6px;
+ border-bottom-left-radius: 6px
+}
+.pagination-lg>li:last-child>a,
+.pagination-lg>li:last-child>span {
+ border-top-right-radius: 6px;
+ border-bottom-right-radius: 6px
+}
+.pagination-sm>li>a,
+.pagination-sm>li>span {
+ padding: 5px 10px;
+ font-size: 12px
+}
+.pagination-sm>li:first-child>a,
+.pagination-sm>li:first-child>span {
+ border-top-left-radius: 3px;
+ border-bottom-left-radius: 3px
+}
+.pagination-sm>li:last-child>a,
+.pagination-sm>li:last-child>span {
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px
+}
+.pager {
+ padding-left: 0;
+ margin: 20px 0;
+ text-align: center;
+ list-style: none
+}
+.pager li {
+ display: inline
+}
+.pager li>a,
+.pager li>span {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-radius: 15px
+}
+.pager li>a:focus,
+.pager li>a:hover {
+ text-decoration: none;
+ background-color: #eee
+}
+.pager .next>a,
+.pager .next>span {
+ float: right
+}
+.pager .previous>a,
+.pager .previous>span {
+ float: left
+}
+.pager .disabled>a,
+.pager .disabled>a:focus,
+.pager .disabled>a:hover,
+.pager .disabled>span {
+ color: #777;
+ cursor: not-allowed;
+ background-color: #fff
+}
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: 700;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em
+}
+a.label:focus,
+a.label:hover {
+ color: #fff;
+ text-decoration: none;
+ cursor: pointer
+}
+.label:empty {
+ display: none
+}
+.btn .label {
+ position: relative;
+ top: -1px
+}
+.label-default {
+ background-color: #777
+}
+.label-default[href]:focus,
+.label-default[href]:hover {
+ background-color: #5e5e5e
+}
+.label-primary {
+ background-color: #337ab7
+}
+.label-primary[href]:focus,
+.label-primary[href]:hover {
+ background-color: #286090
+}
+.label-success {
+ background-color: #5cb85c
+}
+.label-success[href]:focus,
+.label-success[href]:hover {
+ background-color: #449d44
+}
+.label-info {
+ background-color: #5bc0de
+}
+.label-info[href]:focus,
+.label-info[href]:hover {
+ background-color: #31b0d5
+}
+.label-warning {
+ background-color: #f0ad4e
+}
+.label-warning[href]:focus,
+.label-warning[href]:hover {
+ background-color: #ec971f
+}
+.label-danger {
+ background-color: #d9534f
+}
+.label-danger[href]:focus,
+.label-danger[href]:hover {
+ background-color: #c9302c
+}
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: 700;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ background-color: #777;
+ border-radius: 10px
+}
+.badge:empty {
+ display: none
+}
+.btn .badge {
+ position: relative;
+ top: -1px
+}
+.btn-group-xs>.btn .badge,
+.btn-xs .badge {
+ top: 0;
+ padding: 1px 5px
+}
+a.badge:focus,
+a.badge:hover {
+ color: #fff;
+ text-decoration: none;
+ cursor: pointer
+}
+.list-group-item.active>.badge,
+.nav-pills>.active>a>.badge {
+ color: #337ab7;
+ background-color: #fff
+}
+.list-group-item>.badge {
+ float: right
+}
+.list-group-item>.badge+.badge {
+ margin-right: 5px
+}
+.nav-pills>li>a>.badge {
+ margin-left: 3px
+}
+.jumbotron {
+ padding: 30px 15px;
+ margin-bottom: 30px;
+ color: inherit;
+ background-color: #eee
+}
+.jumbotron .h1,
+.jumbotron h1 {
+ color: inherit
+}
+.jumbotron p {
+ margin-bottom: 15px;
+ font-size: 21px;
+ font-weight: 200
+}
+.jumbotron>hr {
+ border-top-color: #d5d5d5
+}
+.container .jumbotron,
+.container-fluid .jumbotron {
+ border-radius: 6px
+}
+.jumbotron .container {
+ max-width: 100%
+}
+@media screen and (min-width: 768px) {
+ .jumbotron {
+ padding: 48px 0
+ }
+ .container .jumbotron,
+ .container-fluid .jumbotron {
+ padding-right: 60px;
+ padding-left: 60px
+ }
+ .jumbotron .h1,
+ .jumbotron h1 {
+ font-size: 63px
+ }
+}
+.thumbnail {
+ display: block;
+ padding: 4px;
+ margin-bottom: 20px;
+ line-height: 1.42857143;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ -webkit-transition: border .2s ease-in-out;
+ -o-transition: border .2s ease-in-out;
+ transition: border .2s ease-in-out
+}
+.thumbnail a>img,
+.thumbnail>img {
+ margin-right: auto;
+ margin-left: auto
+}
+a.thumbnail.active,
+a.thumbnail:focus,
+a.thumbnail:hover {
+ border-color: #337ab7
+}
+.thumbnail .caption {
+ padding: 9px;
+ color: #333
+}
+.alert {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ border-radius: 4px
+}
+.alert h4 {
+ margin-top: 0;
+ color: inherit
+}
+.alert .alert-link {
+ font-weight: 700
+}
+.alert>p,
+.alert>ul {
+ margin-bottom: 0
+}
+.alert>p+p {
+ margin-top: 5px
+}
+.alert-dismissable,
+.alert-dismissible {
+ padding-right: 35px
+}
+.alert-dismissable .close,
+.alert-dismissible .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ color: inherit
+}
+.alert-success {
+ color: #3c763d;
+ background-color: #dff0d8;
+ border-color: #d6e9c6
+}
+.alert-success hr {
+ border-top-color: #c9e2b3
+}
+.alert-success .alert-link {
+ color: #2b542c
+}
+.alert-info {
+ color: #31708f;
+ background-color: #d9edf7;
+ border-color: #bce8f1
+}
+.alert-info hr {
+ border-top-color: #a6e1ec
+}
+.alert-info .alert-link {
+ color: #245269
+}
+.alert-warning {
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+ border-color: #faebcc
+}
+.alert-warning hr {
+ border-top-color: #f7e1b5
+}
+.alert-warning .alert-link {
+ color: #66512c
+}
+.alert-danger {
+ color: #a94442;
+ background-color: #f2dede;
+ border-color: #ebccd1
+}
+.alert-danger hr {
+ border-top-color: #e4b9c0
+}
+.alert-danger .alert-link {
+ color: #843534
+}
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0
+ }
+ to {
+ background-position: 0 0
+ }
+}
+@-o-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0
+ }
+ to {
+ background-position: 0 0
+ }
+}
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0
+ }
+ to {
+ background-position: 0 0
+ }
+}
+.progress {
+ height: 20px;
+ margin-bottom: 20px;
+ overflow: hidden;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1)
+}
+.progress-bar {
+ float: left;
+ width: 0;
+ height: 100%;
+ font-size: 12px;
+ line-height: 20px;
+ color: #fff;
+ text-align: center;
+ background-color: #337ab7;
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);
+ -webkit-transition: width .6s ease;
+ -o-transition: width .6s ease;
+ transition: width .6s ease
+}
+.progress-bar-striped,
+.progress-striped .progress-bar {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ -webkit-background-size: 40px 40px;
+ background-size: 40px 40px
+}
+.progress-bar.active,
+.progress.active .progress-bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite
+}
+.progress-bar-success {
+ background-color: #5cb85c
+}
+.progress-striped .progress-bar-success {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent)
+}
+.progress-bar-info {
+ background-color: #5bc0de
+}
+.progress-striped .progress-bar-info {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent)
+}
+.progress-bar-warning {
+ background-color: #f0ad4e
+}
+.progress-striped .progress-bar-warning {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent)
+}
+.progress-bar-danger {
+ background-color: #d9534f
+}
+.progress-striped .progress-bar-danger {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent)
+}
+.media {
+ margin-top: 15px
+}
+.media:first-child {
+ margin-top: 0
+}
+.media,
+.media-body {
+ overflow: hidden;
+ zoom: 1
+}
+.media-body {
+ width: 10000px
+}
+.media-object {
+ display: block
+}
+.media-right,
+.media>.pull-right {
+ padding-left: 10px
+}
+.media-left,
+.media>.pull-left {
+ padding-right: 10px
+}
+.media-body,
+.media-left,
+.media-right {
+ display: table-cell;
+ vertical-align: top
+}
+.media-middle {
+ vertical-align: middle
+}
+.media-bottom {
+ vertical-align: bottom
+}
+.media-heading {
+ margin-top: 0;
+ margin-bottom: 5px
+}
+.media-list {
+ padding-left: 0;
+ list-style: none
+}
+.list-group {
+ padding-left: 0;
+ margin-bottom: 20px
+}
+.list-group-item {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+ margin-bottom: -1px;
+ background-color: #fff;
+ border: 1px solid #ddd
+}
+.list-group-item:first-child {
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px
+}
+.list-group-item:last-child {
+ margin-bottom: 0;
+ border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px
+}
+a.list-group-item {
+ color: #555
+}
+a.list-group-item .list-group-item-heading {
+ color: #333
+}
+a.list-group-item:focus,
+a.list-group-item:hover {
+ color: #555;
+ text-decoration: none;
+ background-color: #f5f5f5
+}
+.list-group-item.disabled,
+.list-group-item.disabled:focus,
+.list-group-item.disabled:hover {
+ color: #777;
+ cursor: not-allowed;
+ background-color: #eee
+}
+.list-group-item.disabled .list-group-item-heading,
+.list-group-item.disabled:focus .list-group-item-heading,
+.list-group-item.disabled:hover .list-group-item-heading {
+ color: inherit
+}
+.list-group-item.disabled .list-group-item-text,
+.list-group-item.disabled:focus .list-group-item-text,
+.list-group-item.disabled:hover .list-group-item-text {
+ color: #777
+}
+.list-group-item.active,
+.list-group-item.active:focus,
+.list-group-item.active:hover {
+ z-index: 2;
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #337ab7
+}
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active .list-group-item-heading>.small,
+.list-group-item.active .list-group-item-heading>small,
+.list-group-item.active:focus .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading>.small,
+.list-group-item.active:focus .list-group-item-heading>small,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading>.small,
+.list-group-item.active:hover .list-group-item-heading>small {
+ color: inherit
+}
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text {
+ color: #c7ddef
+}
+.list-group-item-success {
+ color: #3c763d;
+ background-color: #dff0d8
+}
+a.list-group-item-success {
+ color: #3c763d
+}
+a.list-group-item-success .list-group-item-heading {
+ color: inherit
+}
+a.list-group-item-success:focus,
+a.list-group-item-success:hover {
+ color: #3c763d;
+ background-color: #d0e9c6
+}
+a.list-group-item-success.active,
+a.list-group-item-success.active:focus,
+a.list-group-item-success.active:hover {
+ color: #fff;
+ background-color: #3c763d;
+ border-color: #3c763d
+}
+.list-group-item-info {
+ color: #31708f;
+ background-color: #d9edf7
+}
+a.list-group-item-info {
+ color: #31708f
+}
+a.list-group-item-info .list-group-item-heading {
+ color: inherit
+}
+a.list-group-item-info:focus,
+a.list-group-item-info:hover {
+ color: #31708f;
+ background-color: #c4e3f3
+}
+a.list-group-item-info.active,
+a.list-group-item-info.active:focus,
+a.list-group-item-info.active:hover {
+ color: #fff;
+ background-color: #31708f;
+ border-color: #31708f
+}
+.list-group-item-warning {
+ color: #8a6d3b;
+ background-color: #fcf8e3
+}
+a.list-group-item-warning {
+ color: #8a6d3b
+}
+a.list-group-item-warning .list-group-item-heading {
+ color: inherit
+}
+a.list-group-item-warning:focus,
+a.list-group-item-warning:hover {
+ color: #8a6d3b;
+ background-color: #faf2cc
+}
+a.list-group-item-warning.active,
+a.list-group-item-warning.active:focus,
+a.list-group-item-warning.active:hover {
+ color: #fff;
+ background-color: #8a6d3b;
+ border-color: #8a6d3b
+}
+.list-group-item-danger {
+ color: #a94442;
+ background-color: #f2dede
+}
+a.list-group-item-danger {
+ color: #a94442
+}
+a.list-group-item-danger .list-group-item-heading {
+ color: inherit
+}
+a.list-group-item-danger:focus,
+a.list-group-item-danger:hover {
+ color: #a94442;
+ background-color: #ebcccc
+}
+a.list-group-item-danger.active,
+a.list-group-item-danger.active:focus,
+a.list-group-item-danger.active:hover {
+ color: #fff;
+ background-color: #a94442;
+ border-color: #a94442
+}
+.list-group-item-heading {
+ margin-top: 0;
+ margin-bottom: 5px
+}
+.list-group-item-text {
+ margin-bottom: 0;
+ line-height: 1.3
+}
+.panel {
+ margin-bottom: 20px;
+ background-color: #fff;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, .05)
+}
+.panel-body {
+ padding: 15px
+}
+.panel-heading {
+ padding: 10px 15px;
+ border-bottom: 1px solid transparent;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px
+}
+.panel-heading>.dropdown .dropdown-toggle {
+ color: inherit
+}
+.panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 16px;
+ color: inherit
+}
+.panel-title>.small,
+.panel-title>.small>a,
+.panel-title>a,
+.panel-title>small,
+.panel-title>small>a {
+ color: inherit
+}
+.panel-footer {
+ padding: 10px 15px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #ddd;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px
+}
+.panel>.list-group,
+.panel>.panel-collapse>.list-group {
+ margin-bottom: 0
+}
+.panel>.list-group .list-group-item,
+.panel>.panel-collapse>.list-group .list-group-item {
+ border-width: 1px 0;
+ border-radius: 0
+}
+.panel>.list-group:first-child .list-group-item:first-child,
+.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child {
+ border-top: 0;
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px
+}
+.panel>.list-group:last-child .list-group-item:last-child,
+.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child {
+ border-bottom: 0;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px
+}
+.panel-heading+.list-group .list-group-item:first-child {
+ border-top-width: 0
+}
+.list-group+.panel-footer {
+ border-top-width: 0
+}
+.panel>.panel-collapse>.table,
+.panel>.table,
+.panel>.table-responsive>.table {
+ margin-bottom: 0
+}
+.panel>.panel-collapse>.table caption,
+.panel>.table caption,
+.panel>.table-responsive>.table caption {
+ padding-right: 15px;
+ padding-left: 15px
+}
+.panel>.table-responsive:first-child>.table:first-child,
+.panel>.table:first-child {
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px
+}
+.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,
+.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,
+.panel>.table:first-child>tbody:first-child>tr:first-child,
+.panel>.table:first-child>thead:first-child>tr:first-child {
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px
+}
+.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,
+.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,
+.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,
+.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,
+.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,
+.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,
+.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,
+.panel>.table:first-child>thead:first-child>tr:first-child th:first-child {
+ border-top-left-radius: 3px
+}
+.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,
+.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,
+.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,
+.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,
+.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,
+.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,
+.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,
+.panel>.table:first-child>thead:first-child>tr:first-child th:last-child {
+ border-top-right-radius: 3px
+}
+.panel>.table-responsive:last-child>.table:last-child,
+.panel>.table:last-child {
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px
+}
+.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,
+.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,
+.panel>.table:last-child>tbody:last-child>tr:last-child,
+.panel>.table:last-child>tfoot:last-child>tr:last-child {
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px
+}
+.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,
+.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,
+.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,
+.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,
+.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,
+.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,
+.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,
+.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child {
+ border-bottom-left-radius: 3px
+}
+.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,
+.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,
+.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,
+.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,
+.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,
+.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,
+.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,
+.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child {
+ border-bottom-right-radius: 3px
+}
+.panel>.panel-body+.table,
+.panel>.panel-body+.table-responsive,
+.panel>.table+.panel-body,
+.panel>.table-responsive+.panel-body {
+ border-top: 1px solid #ddd
+}
+.panel>.table>tbody:first-child>tr:first-child td,
+.panel>.table>tbody:first-child>tr:first-child th {
+ border-top: 0
+}
+.panel>.table-bordered,
+.panel>.table-responsive>.table-bordered {
+ border: 0
+}
+.panel>.table-bordered>tbody>tr>td:first-child,
+.panel>.table-bordered>tbody>tr>th:first-child,
+.panel>.table-bordered>tfoot>tr>td:first-child,
+.panel>.table-bordered>tfoot>tr>th:first-child,
+.panel>.table-bordered>thead>tr>td:first-child,
+.panel>.table-bordered>thead>tr>th:first-child,
+.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,
+.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,
+.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,
+.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,
+.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,
+.panel>.table-responsive>.table-bordered>thead>tr>th:first-child {
+ border-left: 0
+}
+.panel>.table-bordered>tbody>tr>td:last-child,
+.panel>.table-bordered>tbody>tr>th:last-child,
+.panel>.table-bordered>tfoot>tr>td:last-child,
+.panel>.table-bordered>tfoot>tr>th:last-child,
+.panel>.table-bordered>thead>tr>td:last-child,
+.panel>.table-bordered>thead>tr>th:last-child,
+.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,
+.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,
+.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,
+.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,
+.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,
+.panel>.table-responsive>.table-bordered>thead>tr>th:last-child {
+ border-right: 0
+}
+.panel>.table-bordered>tbody>tr:first-child>td,
+.panel>.table-bordered>tbody>tr:first-child>th,
+.panel>.table-bordered>thead>tr:first-child>td,
+.panel>.table-bordered>thead>tr:first-child>th,
+.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,
+.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,
+.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,
+.panel>.table-responsive>.table-bordered>thead>tr:first-child>th {
+ border-bottom: 0
+}
+.panel>.table-bordered>tbody>tr:last-child>td,
+.panel>.table-bordered>tbody>tr:last-child>th,
+.panel>.table-bordered>tfoot>tr:last-child>td,
+.panel>.table-bordered>tfoot>tr:last-child>th,
+.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,
+.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,
+.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,
+.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th {
+ border-bottom: 0
+}
+.panel>.table-responsive {
+ margin-bottom: 0;
+ border: 0
+}
+.panel-group {
+ margin-bottom: 20px
+}
+.panel-group .panel {
+ margin-bottom: 0;
+ border-radius: 4px
+}
+.panel-group .panel+.panel {
+ margin-top: 5px
+}
+.panel-group .panel-heading {
+ border-bottom: 0
+}
+.panel-group .panel-heading+.panel-collapse>.list-group,
+.panel-group .panel-heading+.panel-collapse>.panel-body {
+ border-top: 1px solid #ddd
+}
+.panel-group .panel-footer {
+ border-top: 0
+}
+.panel-group .panel-footer+.panel-collapse .panel-body {
+ border-bottom: 1px solid #ddd
+}
+.panel-default {
+ border-color: #ddd
+}
+.panel-default>.panel-heading {
+ color: #333;
+ background-color: #f5f5f5;
+ border-color: #ddd
+}
+.panel-default>.panel-heading+.panel-collapse>.panel-body {
+ border-top-color: #ddd
+}
+.panel-default>.panel-heading .badge {
+ color: #f5f5f5;
+ background-color: #333
+}
+.panel-default>.panel-footer+.panel-collapse>.panel-body {
+ border-bottom-color: #ddd
+}
+.panel-primary {
+ border-color: #337ab7
+}
+.panel-primary>.panel-heading {
+ color: #fff;
+ background-color: #337ab7;
+ border-color: #337ab7
+}
+.panel-primary>.panel-heading+.panel-collapse>.panel-body {
+ border-top-color: #337ab7
+}
+.panel-primary>.panel-heading .badge {
+ color: #337ab7;
+ background-color: #fff
+}
+.panel-primary>.panel-footer+.panel-collapse>.panel-body {
+ border-bottom-color: #337ab7
+}
+.panel-success {
+ border-color: #d6e9c6
+}
+.panel-success>.panel-heading {
+ color: #3c763d;
+ background-color: #dff0d8;
+ border-color: #d6e9c6
+}
+.panel-success>.panel-heading+.panel-collapse>.panel-body {
+ border-top-color: #d6e9c6
+}
+.panel-success>.panel-heading .badge {
+ color: #dff0d8;
+ background-color: #3c763d
+}
+.panel-success>.panel-footer+.panel-collapse>.panel-body {
+ border-bottom-color: #d6e9c6
+}
+.panel-info {
+ border-color: #bce8f1
+}
+.panel-info>.panel-heading {
+ color: #31708f;
+ background-color: #d9edf7;
+ border-color: #bce8f1
+}
+.panel-info>.panel-heading+.panel-collapse>.panel-body {
+ border-top-color: #bce8f1
+}
+.panel-info>.panel-heading .badge {
+ color: #d9edf7;
+ background-color: #31708f
+}
+.panel-info>.panel-footer+.panel-collapse>.panel-body {
+ border-bottom-color: #bce8f1
+}
+.panel-warning {
+ border-color: #faebcc
+}
+.panel-warning>.panel-heading {
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+ border-color: #faebcc
+}
+.panel-warning>.panel-heading+.panel-collapse>.panel-body {
+ border-top-color: #faebcc
+}
+.panel-warning>.panel-heading .badge {
+ color: #fcf8e3;
+ background-color: #8a6d3b
+}
+.panel-warning>.panel-footer+.panel-collapse>.panel-body {
+ border-bottom-color: #faebcc
+}
+.panel-danger {
+ border-color: #ebccd1
+}
+.panel-danger>.panel-heading {
+ color: #a94442;
+ background-color: #f2dede;
+ border-color: #ebccd1
+}
+.panel-danger>.panel-heading+.panel-collapse>.panel-body {
+ border-top-color: #ebccd1
+}
+.panel-danger>.panel-heading .badge {
+ color: #f2dede;
+ background-color: #a94442
+}
+.panel-danger>.panel-footer+.panel-collapse>.panel-body {
+ border-bottom-color: #ebccd1
+}
+.embed-responsive {
+ position: relative;
+ display: block;
+ height: 0;
+ padding: 0;
+ overflow: hidden
+}
+.embed-responsive .embed-responsive-item,
+.embed-responsive embed,
+.embed-responsive iframe,
+.embed-responsive object,
+.embed-responsive video {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: 0
+}
+.embed-responsive-16by9 {
+ padding-bottom: 56.25%
+}
+.embed-responsive-4by3 {
+ padding-bottom: 75%
+}
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05)
+}
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, .15)
+}
+.well-lg {
+ padding: 24px;
+ border-radius: 6px
+}
+.well-sm {
+ padding: 9px;
+ border-radius: 3px
+}
+.close {
+ float: right;
+ font-size: 21px;
+ font-weight: 700;
+ line-height: 1;
+ color: #000;
+ text-shadow: 0 1px 0 #fff;
+ filter: alpha(opacity=20);
+ opacity: .2
+}
+.close:focus,
+.close:hover {
+ color: #000;
+ text-decoration: none;
+ cursor: pointer;
+ filter: alpha(opacity=50);
+ opacity: .5
+}
+button.close {
+ -webkit-appearance: none;
+ padding: 0;
+ cursor: pointer;
+ background: 0 0;
+ border: 0
+}
+.modal-open {
+ overflow: hidden
+}
+.modal {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1050;
+ display: none;
+ overflow: hidden;
+ -webkit-overflow-scrolling: touch;
+ outline: 0
+}
+.modal.fade .modal-dialog {
+ -webkit-transition: -webkit-transform .3s ease-out;
+ -o-transition: -o-transform .3s ease-out;
+ transition: transform .3s ease-out;
+ -webkit-transform: translate(0, -25%);
+ -ms-transform: translate(0, -25%);
+ -o-transform: translate(0, -25%);
+ transform: translate(0, -25%)
+}
+.modal.in .modal-dialog {
+ -webkit-transform: translate(0, 0);
+ -ms-transform: translate(0, 0);
+ -o-transform: translate(0, 0);
+ transform: translate(0, 0)
+}
+.modal-open .modal {
+ overflow-x: hidden;
+ overflow-y: auto
+}
+.modal-dialog {
+ position: relative;
+ width: auto;
+ margin: 10px
+}
+.modal-content {
+ position: relative;
+ background-color: #fff;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ border: 1px solid #999;
+ border: 1px solid rgba(0, 0, 0, .2);
+ border-radius: 6px;
+ outline: 0;
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);
+ box-shadow: 0 3px 9px rgba(0, 0, 0, .5)
+}
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: #000
+}
+.modal-backdrop.fade {
+ filter: alpha(opacity=0);
+ opacity: 0
+}
+.modal-backdrop.in {
+ filter: alpha(opacity=50);
+ opacity: .5
+}
+.modal-header {
+ min-height: 16.43px;
+ padding: 15px;
+ border-bottom: 1px solid #e5e5e5
+}
+.modal-header .close {
+ margin-top: -2px
+}
+.modal-title {
+ margin: 0;
+ line-height: 1.42857143
+}
+.modal-body {
+ position: relative;
+ padding: 15px
+}
+.modal-footer {
+ padding: 15px;
+ text-align: right;
+ border-top: 1px solid #e5e5e5
+}
+.modal-footer .btn+.btn {
+ margin-bottom: 0;
+ margin-left: 5px
+}
+.modal-footer .btn-group .btn+.btn {
+ margin-left: -1px
+}
+.modal-footer .btn-block+.btn-block {
+ margin-left: 0
+}
+.modal-scrollbar-measure {
+ position: absolute;
+ top: -9999px;
+ width: 50px;
+ height: 50px;
+ overflow: scroll
+}
+@media (min-width: 768px) {
+ .modal-dialog {
+ width: 600px;
+ margin: 30px auto
+ }
+ .modal-content {
+ -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, .5)
+ }
+ .modal-sm {
+ width: 300px
+ }
+}
+@media (min-width: 992px) {
+ .modal-lg {
+ width: 900px
+ }
+}
+.tooltip {
+ position: absolute;
+ z-index: 1070;
+ display: block;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 1.4;
+ filter: alpha(opacity=0);
+ opacity: 0
+}
+.tooltip.in {
+ filter: alpha(opacity=90);
+ opacity: .9
+}
+.tooltip.top {
+ padding: 5px 0;
+ margin-top: -3px
+}
+.tooltip.right {
+ padding: 0 5px;
+ margin-left: 3px
+}
+.tooltip.bottom {
+ padding: 5px 0;
+ margin-top: 3px
+}
+.tooltip.left {
+ padding: 0 5px;
+ margin-left: -3px
+}
+.tooltip-inner {
+ max-width: 200px;
+ padding: 3px 8px;
+ color: #fff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000;
+ border-radius: 4px
+}
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid
+}
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000
+}
+.tooltip.top-left .tooltip-arrow {
+ right: 5px;
+ bottom: 0;
+ margin-bottom: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000
+}
+.tooltip.top-right .tooltip-arrow {
+ bottom: 0;
+ left: 5px;
+ margin-bottom: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000
+}
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-width: 5px 5px 5px 0;
+ border-right-color: #000
+}
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-width: 5px 0 5px 5px;
+ border-left-color: #000
+}
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000
+}
+.tooltip.bottom-left .tooltip-arrow {
+ top: 0;
+ right: 5px;
+ margin-top: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000
+}
+.tooltip.bottom-right .tooltip-arrow {
+ top: 0;
+ left: 5px;
+ margin-top: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000
+}
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1060;
+ display: none;
+ max-width: 276px;
+ padding: 1px;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1.42857143;
+ text-align: left;
+ white-space: normal;
+ background-color: #fff;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, .2);
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, .2)
+}
+.popover.top {
+ margin-top: -10px
+}
+.popover.right {
+ margin-left: 10px
+}
+.popover.bottom {
+ margin-top: 10px
+}
+.popover.left {
+ margin-left: -10px
+}
+.popover-title {
+ padding: 8px 14px;
+ margin: 0;
+ font-size: 14px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ border-radius: 5px 5px 0 0
+}
+.popover-content {
+ padding: 9px 14px
+}
+.popover>.arrow,
+.popover>.arrow:after {
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid
+}
+.popover>.arrow {
+ border-width: 11px
+}
+.popover>.arrow:after {
+ content: "";
+ border-width: 10px
+}
+.popover.top>.arrow {
+ bottom: -11px;
+ left: 50%;
+ margin-left: -11px;
+ border-top-color: #999;
+ border-top-color: rgba(0, 0, 0, .25);
+ border-bottom-width: 0
+}
+.popover.top>.arrow:after {
+ bottom: 1px;
+ margin-left: -10px;
+ content: " ";
+ border-top-color: #fff;
+ border-bottom-width: 0
+}
+.popover.right>.arrow {
+ top: 50%;
+ left: -11px;
+ margin-top: -11px;
+ border-right-color: #999;
+ border-right-color: rgba(0, 0, 0, .25);
+ border-left-width: 0
+}
+.popover.right>.arrow:after {
+ bottom: -10px;
+ left: 1px;
+ content: " ";
+ border-right-color: #fff;
+ border-left-width: 0
+}
+.popover.bottom>.arrow {
+ top: -11px;
+ left: 50%;
+ margin-left: -11px;
+ border-top-width: 0;
+ border-bottom-color: #999;
+ border-bottom-color: rgba(0, 0, 0, .25)
+}
+.popover.bottom>.arrow:after {
+ top: 1px;
+ margin-left: -10px;
+ content: " ";
+ border-top-width: 0;
+ border-bottom-color: #fff
+}
+.popover.left>.arrow {
+ top: 50%;
+ right: -11px;
+ margin-top: -11px;
+ border-right-width: 0;
+ border-left-color: #999;
+ border-left-color: rgba(0, 0, 0, .25)
+}
+.popover.left>.arrow:after {
+ right: 1px;
+ bottom: -10px;
+ content: " ";
+ border-right-width: 0;
+ border-left-color: #fff
+}
+.carousel {
+ position: relative
+}
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden
+}
+.carousel-inner>.item {
+ position: relative;
+ display: none;
+ -webkit-transition: .6s ease-in-out left;
+ -o-transition: .6s ease-in-out left;
+ transition: .6s ease-in-out left
+}
+.carousel-inner>.item>a>img,
+.carousel-inner>.item>img {
+ line-height: 1
+}
+@media all and (transform-3d),
+(-webkit-transform-3d) {
+ .carousel-inner>.item {
+ -webkit-transition: -webkit-transform .6s ease-in-out;
+ -o-transition: -o-transform .6s ease-in-out;
+ transition: transform .6s ease-in-out;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ -webkit-perspective: 1000;
+ perspective: 1000
+ }
+ .carousel-inner>.item.active.right,
+ .carousel-inner>.item.next {
+ left: 0;
+ -webkit-transform: translate3d(100%, 0, 0);
+ transform: translate3d(100%, 0, 0)
+ }
+ .carousel-inner>.item.active.left,
+ .carousel-inner>.item.prev {
+ left: 0;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0)
+ }
+ .carousel-inner>.item.active,
+ .carousel-inner>.item.next.left,
+ .carousel-inner>.item.prev.right {
+ left: 0;
+ -webkit-transform: translate3d(0, 0, 0);
+ transform: translate3d(0, 0, 0)
+ }
+}
+.carousel-inner>.active,
+.carousel-inner>.next,
+.carousel-inner>.prev {
+ display: block
+}
+.carousel-inner>.active {
+ left: 0
+}
+.carousel-inner>.next,
+.carousel-inner>.prev {
+ position: absolute;
+ top: 0;
+ width: 100%
+}
+.carousel-inner>.next {
+ left: 100%
+}
+.carousel-inner>.prev {
+ left: -100%
+}
+.carousel-inner>.next.left,
+.carousel-inner>.prev.right {
+ left: 0
+}
+.carousel-inner>.active.left {
+ left: -100%
+}
+.carousel-inner>.active.right {
+ left: 100%
+}
+.carousel-control {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 15%;
+ font-size: 20px;
+ color: #fff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
+ filter: alpha(opacity=50);
+ opacity: .5
+}
+.carousel-control.left {
+ background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0, rgba(0, 0, 0, .0001) 100%);
+ background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0, rgba(0, 0, 0, .0001) 100%);
+ background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));
+ background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0, rgba(0, 0, 0, .0001) 100%);
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+ background-repeat: repeat-x
+}
+.carousel-control.right {
+ right: 0;
+ left: auto;
+ background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0, rgba(0, 0, 0, .5) 100%);
+ background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0, rgba(0, 0, 0, .5) 100%);
+ background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));
+ background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0, rgba(0, 0, 0, .5) 100%);
+ filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+ background-repeat: repeat-x
+}
+.carousel-control:focus,
+.carousel-control:hover {
+ color: #fff;
+ text-decoration: none;
+ filter: alpha(opacity=90);
+ outline: 0;
+ opacity: .9
+}
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right,
+.carousel-control .icon-next,
+.carousel-control .icon-prev {
+ position: absolute;
+ top: 50%;
+ z-index: 5;
+ display: inline-block
+}
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .icon-prev {
+ left: 50%;
+ margin-left: -10px
+}
+.carousel-control .glyphicon-chevron-right,
+.carousel-control .icon-next {
+ right: 50%;
+ margin-right: -10px
+}
+.carousel-control .icon-next,
+.carousel-control .icon-prev {
+ width: 20px;
+ height: 20px;
+ margin-top: -10px;
+ font-family: serif;
+ line-height: 1
+}
+.carousel-control .icon-prev:before {
+ content: '\2039'
+}
+.carousel-control .icon-next:before {
+ content: '\203a'
+}
+.carousel-indicators {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ z-index: 15;
+ width: 60%;
+ padding-left: 0;
+ margin-left: -30%;
+ text-align: center;
+ list-style: none
+}
+.carousel-indicators li {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ margin: 1px;
+ text-indent: -999px;
+ cursor: pointer;
+ background-color: #000 \9;
+ background-color: rgba(0, 0, 0, 0);
+ border: 1px solid #fff;
+ border-radius: 10px
+}
+.carousel-indicators .active {
+ width: 12px;
+ height: 12px;
+ margin: 0;
+ background-color: #fff
+}
+.carousel-caption {
+ position: absolute;
+ right: 15%;
+ bottom: 20px;
+ left: 15%;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ color: #fff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, .6)
+}
+.carousel-caption .btn {
+ text-shadow: none
+}
+@media screen and (min-width: 768px) {
+ .carousel-control .glyphicon-chevron-left,
+ .carousel-control .glyphicon-chevron-right,
+ .carousel-control .icon-next,
+ .carousel-control .icon-prev {
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ font-size: 30px
+ }
+ .carousel-control .glyphicon-chevron-left,
+ .carousel-control .icon-prev {
+ margin-left: -15px
+ }
+ .carousel-control .glyphicon-chevron-right,
+ .carousel-control .icon-next {
+ margin-right: -15px
+ }
+ .carousel-caption {
+ right: 20%;
+ left: 20%;
+ padding-bottom: 30px
+ }
+ .carousel-indicators {
+ bottom: 20px
+ }
+}
+.btn-group-vertical>.btn-group:after,
+.btn-group-vertical>.btn-group:before,
+.btn-toolbar:after,
+.btn-toolbar:before,
+.clearfix:after,
+.clearfix:before,
+.container-fluid:after,
+.container-fluid:before,
+.container:after,
+.container:before,
+.dl-horizontal dd:after,
+.dl-horizontal dd:before,
+.form-horizontal .form-group:after,
+.form-horizontal .form-group:before,
+.modal-footer:after,
+.modal-footer:before,
+.nav:after,
+.nav:before,
+.navbar-collapse:after,
+.navbar-collapse:before,
+.navbar-header:after,
+.navbar-header:before,
+.navbar:after,
+.navbar:before,
+.pager:after,
+.pager:before,
+.panel-body:after,
+.panel-body:before,
+.row:after,
+.row:before {
+ display: table;
+ content: " "
+}
+.btn-group-vertical>.btn-group:after,
+.btn-toolbar:after,
+.clearfix:after,
+.container-fluid:after,
+.container:after,
+.dl-horizontal dd:after,
+.form-horizontal .form-group:after,
+.modal-footer:after,
+.nav:after,
+.navbar-collapse:after,
+.navbar-header:after,
+.navbar:after,
+.pager:after,
+.panel-body:after,
+.row:after {
+ clear: both
+}
+.center-block {
+ display: block;
+ margin-right: auto;
+ margin-left: auto
+}
+.pull-right {
+ float: right!important
+}
+.pull-left {
+ float: left!important
+}
+.hide {
+ display: none!important
+}
+.show {
+ display: block!important
+}
+.invisible {
+ visibility: hidden
+}
+.text-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0
+}
+.hidden {
+ display: none!important
+}
+.affix {
+ position: fixed
+}
+@-ms-viewport {
+ width: device-width
+}
+.visible-lg,
+.visible-md,
+.visible-sm,
+.visible-xs {
+ display: none!important
+}
+.visible-lg-block,
+.visible-lg-inline,
+.visible-lg-inline-block,
+.visible-md-block,
+.visible-md-inline,
+.visible-md-inline-block,
+.visible-sm-block,
+.visible-sm-inline,
+.visible-sm-inline-block,
+.visible-xs-block,
+.visible-xs-inline,
+.visible-xs-inline-block {
+ display: none!important
+}
+@media (max-width: 767px) {
+ .visible-xs {
+ display: block!important
+ }
+ table.visible-xs {
+ display: table
+ }
+ tr.visible-xs {
+ display: table-row!important
+ }
+ td.visible-xs,
+ th.visible-xs {
+ display: table-cell!important
+ }
+}
+@media (max-width: 767px) {
+ .visible-xs-block {
+ display: block!important
+ }
+}
+@media (max-width: 767px) {
+ .visible-xs-inline {
+ display: inline!important
+ }
+}
+@media (max-width: 767px) {
+ .visible-xs-inline-block {
+ display: inline-block!important
+ }
+}
+@media (min-width: 768px)and (max-width: 991px) {
+ .visible-sm {
+ display: block!important
+ }
+ table.visible-sm {
+ display: table
+ }
+ tr.visible-sm {
+ display: table-row!important
+ }
+ td.visible-sm,
+ th.visible-sm {
+ display: table-cell!important
+ }
+}
+@media (min-width: 768px)and (max-width: 991px) {
+ .visible-sm-block {
+ display: block!important
+ }
+}
+@media (min-width: 768px)and (max-width: 991px) {
+ .visible-sm-inline {
+ display: inline!important
+ }
+}
+@media (min-width: 768px)and (max-width: 991px) {
+ .visible-sm-inline-block {
+ display: inline-block!important
+ }
+}
+@media (min-width: 992px)and (max-width: 1199px) {
+ .visible-md {
+ display: block!important
+ }
+ table.visible-md {
+ display: table
+ }
+ tr.visible-md {
+ display: table-row!important
+ }
+ td.visible-md,
+ th.visible-md {
+ display: table-cell!important
+ }
+}
+@media (min-width: 992px)and (max-width: 1199px) {
+ .visible-md-block {
+ display: block!important
+ }
+}
+@media (min-width: 992px)and (max-width: 1199px) {
+ .visible-md-inline {
+ display: inline!important
+ }
+}
+@media (min-width: 992px)and (max-width: 1199px) {
+ .visible-md-inline-block {
+ display: inline-block!important
+ }
+}
+@media (min-width: 1200px) {
+ .visible-lg {
+ display: block!important
+ }
+ table.visible-lg {
+ display: table
+ }
+ tr.visible-lg {
+ display: table-row!important
+ }
+ td.visible-lg,
+ th.visible-lg {
+ display: table-cell!important
+ }
+}
+@media (min-width: 1200px) {
+ .visible-lg-block {
+ display: block!important
+ }
+}
+@media (min-width: 1200px) {
+ .visible-lg-inline {
+ display: inline!important
+ }
+}
+@media (min-width: 1200px) {
+ .visible-lg-inline-block {
+ display: inline-block!important
+ }
+}
+@media (max-width: 767px) {
+ .hidden-xs {
+ display: none!important
+ }
+}
+@media (min-width: 768px)and (max-width: 991px) {
+ .hidden-sm {
+ display: none!important
+ }
+}
+@media (min-width: 992px)and (max-width: 1199px) {
+ .hidden-md {
+ display: none!important
+ }
+}
+@media (min-width: 1200px) {
+ .hidden-lg {
+ display: none!important
+ }
+}
+.visible-print {
+ display: none!important
+}
+@media print {
+ .visible-print {
+ display: block!important
+ }
+ table.visible-print {
+ display: table
+ }
+ tr.visible-print {
+ display: table-row!important
+ }
+ td.visible-print,
+ th.visible-print {
+ display: table-cell!important
+ }
+}
+.visible-print-block {
+ display: none!important
+}
+@media print {
+ .visible-print-block {
+ display: block!important
+ }
+}
+.visible-print-inline {
+ display: none!important
+}
+@media print {
+ .visible-print-inline {
+ display: inline!important
+ }
+}
+.visible-print-inline-block {
+ display: none!important
+}
+@media print {
+ .visible-print-inline-block {
+ display: inline-block!important
+ }
+}
+@media print {
+ .hidden-print {
+ display: none!important
+ }
+}
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/custom-theme.css b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/custom-theme.css
new file mode 100755
index 00000000..a4256032
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/custom-theme.css
@@ -0,0 +1,74 @@
+.navbar-default {
+ background-color: #2a2a2a;
+ border-color:#2a2a2a ;
+}
+.navbar-nav{color: #a5a5a5; font-weight:300; padding-top: 5px; padding-left:0px;}
+.form-control {background-color: #121212; border: 0px;}
+.btn-default {background-color: #121212; border: 1px solid #121212;}
+.container { padding:0px;}
+.container-fluid{padding-left:0px;}
+.navbar-default .navbar-nav > li > a {color: #a5a5a5; font-size: 14px; font-weight: 500;}
+.navbar-form .input-group > .form-control { height: 39px; margin-top: 0; }
+.input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { height: 39px;}
+.container-bg{background-image: url("../images/content-bg.png"); background-repeat: no-repeat; background-position: top left; min-height: 550px; overflow: hidden; padding: 0 15px;}
+.navbar-default .navbar-nav > li > a :focus,.navbar-default .navbar-nav>li>a:hover{color:#ccc;background-color:transparent;}
+.btn-default.active,
+.btn-default.focus,
+.btn-default:active,
+.btn-default:focus,
+.btn-default:hover,
+.open>.dropdown-toggle.btn-default {
+ color: #ccc;
+ background-color: #121212;
+ border-color: #121212;
+}
+input {
+ background-color: #333;
+ border: 1px solid #2a2a2a;
+ border-radius: 3px;
+ height: 45px;
+ margin: 5px 0;
+ width: 100%;
+ padding-left: 10px;
+}
+input[type="checkbox"], input[type="radio"] {
+ line-height: normal;
+ margin-top: -3px;
+ width: 30px;
+}
+.navbar-default .navbar-nav>li>a:focus,
+.navbar-default .navbar-nav>li>a:hover {
+ color: #747474;
+ outline: 0;
+}
+.navbar {
+ margin-bottom: 5px;
+}
+.navbar-brand {
+ padding: 15px 10px;
+}
+.navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:focus, .navbar-default .navbar-nav > .open > a:hover {
+ color: #fff;
+ background-color:#202020 ;
+}
+.dropdown-menu {
+ background-clip: padding-box;
+ background-color: #202020;
+ border: 0px solid rgba(0, 0, 0, 0.15);
+ border-radius: 0;
+ box-shadow: none;
+ display: none;
+ float: left;
+ font-size: 14px;
+ left: 0;
+ color: #a5a5a5;
+}
+.dropdown-menu > li > a {
+ color: #a5a5a5;
+}
+.dropdown-menu>li>a:focus,
+.dropdown-menu>li>a:hover {
+ color: #fff;
+ text-decoration: none;
+ background-color: #313131;
+}
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/fontwso2-extend.css b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/fontwso2-extend.css
new file mode 100755
index 00000000..032768d9
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/fontwso2-extend.css
@@ -0,0 +1,177 @@
+.fw, [class^="fw-"], [class*=" fw-"] {
+ display: inline-block;
+ font: normal normal normal 14px/1 fontwso2;
+ font-size: inherit;
+ speak: none;
+ font-style: normal;
+ font-weight: normal;
+ font-variant: normal;
+ text-transform: none;
+ vertical-align: middle;
+
+ /* Better Font Rendering =========== */
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ text-rendering: auto;
+ transform: translate(0, 0);
+}
+
+/* icon options */
+.fw-lg {
+ font-size: 1.33333333em;
+ line-height: 0.75em;
+ vertical-align: -15%;
+}
+.fw-1-5x {
+ font-size: 1.5em;
+}
+.fw-2x {
+ font-size: 2em;
+}
+.fw-3x {
+ font-size: 3em;
+}
+.fw-4x {
+ font-size: 4em;
+}
+.fw-5x {
+ font-size: 5em;
+}
+.fw-fixedwidth {
+ width: 1.28571429em;
+ text-align: center;
+}
+.fw-ul {
+ padding-left: 0;
+ margin-left: 2.14285714em;
+ list-style-type: none;
+}
+.fw-ul > li {
+ position: relative;
+}
+.fw-li {
+ position: absolute;
+ left: -2.14285714em;
+ width: 2.14285714em;
+ top: 0.14285714em;
+ text-align: center;
+}
+.fw-li.fw-lg {
+ left: -1.85714286em;
+}
+.fw-border {
+ padding: .2em .25em .15em;
+ border: solid 0.08em #eeeeee;
+ border-radius: .1em;
+}
+.pull-right {
+ float: right;
+}
+.pull-left {
+ float: left;
+}
+.fw.pull-left {
+ margin-right: .3em;
+}
+.fw.pull-right {
+ margin-left: .3em;
+}
+.fw-spin {
+ -webkit-animation: wso2icon-spin 2s infinite linear;
+ animation: wso2icon-spin 2s infinite linear;
+}
+.fw-pulse {
+ -webkit-animation: wso2icon-spin 1s infinite steps(8);
+ animation: wso2icon-spin 1s infinite steps(8);
+}
+@-webkit-keyframes wso2icon-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+@keyframes wso2icon-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+.fw-rotate-90 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+ -webkit-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
+.fw-rotate-180 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.fw-rotate-270 {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
+ -webkit-transform: rotate(270deg);
+ -ms-transform: rotate(270deg);
+ transform: rotate(270deg);
+}
+.fw-flip-horizontal {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
+ -webkit-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+.fw-flip-vertical {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
+ -webkit-transform: scale(1, -1);
+ -ms-transform: scale(1, -1);
+ transform: scale(1, -1);
+}
+:root .fw-rotate-90,
+:root .fw-rotate-180,
+:root .fw-rotate-270,
+:root .fw-flip-horizontal,
+:root .fw-flip-vertical {
+ filter: none;
+}
+.fw-stack, .fw-stack-md {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 1.8em;
+ vertical-align: middle;
+ text-align: center;
+}
+.fw-stack-md {
+ width: 1em;
+}
+.fw-stack-1x,
+.fw-stack-2x,
+.fw-stack-1-5x {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.fw-stack-1x {
+ line-height: inherit;
+ font-size: 0.9em;
+ }
+.fw-stack-1-5x {
+ line-height: inherit;
+ font-size: 1.2em;
+}
+.fw-stack-2x {
+ font-size: 1.8em;
+}
+.fw-inverse {
+ color: #ffffff;
+}
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/fontwso2.css b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/fontwso2.css
new file mode 100755
index 00000000..463b22ec
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/fontwso2.css
@@ -0,0 +1,481 @@
+@font-face {
+ font-family: 'fontwso2';
+ src:url('../fonts/fontwso2.eot?3uii6i');
+ src:url('../fonts/fontwso2.eot?#iefix3uii6i') format('embedded-opentype'),
+ url('../fonts/fontwso2.woff?3uii6i') format('woff'),
+ url('../fonts/fontwso2.ttf?3uii6i') format('truetype'),
+ url('../fonts/fontwso2.svg?3uii6i#fontwso2') format('svg');
+ font-weight: normal;
+ font-style: normal;
+}
+
+.fw {
+ font-family: 'fontwso2';
+ speak: none;
+ font-style: normal;
+ font-weight: normal;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 1;
+
+ /* Better Font Rendering =========== */
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.fw-lifecycle:before {
+ content: "\e617";
+}
+
+.fw-globe:before {
+ content: "\e666";
+}
+
+.fw-filter:before {
+ content: "\e65b";
+}
+
+.fw-store:before {
+ content: "\e61b";
+}
+
+.fw-apple:before {
+ content: "\e613";
+}
+
+.fw-android:before {
+ content: "\e653";
+}
+
+.fw-windows:before {
+ content: "\e654";
+}
+
+.fw-monitor:before {
+ content: "\e658";
+}
+
+.fw-laptop:before {
+ content: "\e659";
+}
+
+.fw-computer:before {
+ content: "\e655";
+}
+
+.fw-mobile:before {
+ content: "\e656";
+}
+
+.fw-dots:before {
+ content: "\e615";
+}
+
+.fw-tiles:before {
+ content: "\e60e";
+}
+
+.fw-menu:before {
+ content: "\e60f";
+}
+
+.fw-list:before {
+ content: "\e610";
+}
+
+.fw-grid:before {
+ content: "\e611";
+}
+
+.fw-list2:before {
+ content: "\e667";
+}
+
+.fw-user:before {
+ content: "\e601";
+}
+
+.fw-add-user:before {
+ content: "\e668";
+}
+
+.fw-key:before {
+ content: "\e665";
+}
+
+.fw-star:before {
+ content: "\e65c";
+}
+
+.fw-star-2:before {
+ content: "\e612";
+}
+
+.fw-view:before {
+ content: "\e652";
+}
+
+.fw-edit:before {
+ content: "\e65d";
+}
+
+.fw-register2:before {
+ content: "\e669";
+}
+
+.fw-register:before {
+ content: "\e65f";
+}
+
+.fw-search:before {
+ content: "\e657";
+}
+
+.fw-send:before {
+ content: "\e66a";
+}
+
+.fw-delete:before {
+ content: "\e614";
+}
+
+.fw-settings:before {
+ content: "\e65e";
+}
+
+.fw-share:before {
+ content: "\e65a";
+}
+
+.fw-message:before {
+ content: "\e616";
+}
+
+.fw-bell:before {
+ content: "\e66b";
+}
+
+.fw-lock:before {
+ content: "\e618";
+}
+
+.fw-battery:before {
+ content: "\e619";
+}
+
+.fw-camera:before {
+ content: "\e61a";
+}
+
+.fw-wifi:before {
+ content: "\e606";
+}
+
+.fw-usb-drive:before {
+ content: "\e602";
+}
+
+.fw-hdd:before {
+ content: "\e61c";
+}
+
+.fw-map-location:before {
+ content: "\e61d";
+}
+
+.fw-invitation:before {
+ content: "\e664";
+}
+
+.fw-dial-up:before {
+ content: "\e61f";
+}
+
+.fw-incoming-call:before {
+ content: "\e620";
+}
+
+.fw-clock:before {
+ content: "\e621";
+}
+
+.fw-clean:before {
+ content: "\e622";
+}
+
+.fw-sort:before {
+ content: "\e623";
+}
+
+.fw-list-sort:before {
+ content: "\e624";
+}
+
+.fw-sequence:before {
+ content: "\e625";
+}
+
+.fw-rules:before {
+ content: "\e626";
+}
+
+.fw-padlock:before {
+ content: "\e66c";
+}
+
+.fw-uri:before {
+ content: "\e603";
+}
+
+.fw-pdf:before {
+ content: "\e627";
+}
+
+.fw-ms-document:before {
+ content: "\e629";
+}
+
+.fw-swagger:before {
+ content: "\e62a";
+}
+
+.fw-jquery:before {
+ content: "\e62b";
+}
+
+.fw-java:before {
+ content: "\e62c";
+}
+
+.fw-javaee:before {
+ content: "\e62d";
+}
+
+.fw-javascript:before {
+ content: "\e62e";
+}
+
+.fw-jaggery:before {
+ content: "\e62f";
+}
+
+.fw-uncheck:before {
+ content: "\e630";
+}
+
+.fw-check:before {
+ content: "\e631";
+}
+
+.fw-up-arrow-2:before {
+ content: "\e660";
+}
+
+.fw-down-arrow-2:before {
+ content: "\e661";
+}
+
+.fw-left-arrow-2:before {
+ content: "\e662";
+}
+
+.fw-right-arrow-2:before {
+ content: "\e663";
+}
+
+.fw-up-arrow:before {
+ content: "\e632";
+}
+
+.fw-down-arrow:before {
+ content: "\e63e";
+}
+
+.fw-left-arrow:before {
+ content: "\e633";
+}
+
+.fw-right-arrow:before {
+ content: "\e634";
+}
+
+.fw-cancel:before {
+ content: "\e635";
+}
+
+.fw-add:before {
+ content: "\e636";
+}
+
+.fw-minus:before {
+ content: "\e628";
+}
+
+.fw-refresh:before {
+ content: "\e637";
+}
+
+.fw-ring:before {
+ content: "\e600";
+}
+
+.fw-circle:before {
+ content: "\e638";
+}
+
+.fw-ok:before {
+ content: "\e639";
+}
+
+.fw-error:before {
+ content: "\e63a";
+}
+
+.fw-block:before {
+ content: "\e63b";
+}
+
+.fw-warning:before {
+ content: "\e605";
+}
+
+.fw-deploy:before {
+ content: "\e66d";
+}
+
+.fw-devices:before {
+ content: "\e63c";
+}
+
+.fw-dss:before {
+ content: "\e63d";
+}
+
+.fw-database:before {
+ content: "\e66e";
+}
+
+.fw-computer2:before {
+ content: "\e66f";
+}
+
+.fw-endpoint:before {
+ content: "\e63f";
+}
+
+.fw-bpmn:before {
+ content: "\e640";
+}
+
+.fw-bpel:before {
+ content: "\e641";
+}
+
+.fw-gadget:before {
+ content: "\e642";
+}
+
+.fw-application:before {
+ content: "\e643";
+}
+
+.fw-cloud:before {
+ content: "\e644";
+}
+
+.fw-service:before {
+ content: "\e645";
+}
+
+.fw-rest-service:before {
+ content: "\e646";
+}
+
+.fw-rest-api:before {
+ content: "\e647";
+}
+
+.fw-api:before {
+ content: "\e648";
+}
+
+.fw-service-provider:before {
+ content: "\e649";
+}
+
+.fw-website:before {
+ content: "\e604";
+}
+
+.fw-proxy:before {
+ content: "\e64a";
+}
+
+.fw-policy:before {
+ content: "\e64b";
+}
+
+.fw-security-policy:before {
+ content: "\e64c";
+}
+
+.fw-throttling-policy:before {
+ content: "\e64d";
+}
+
+.fw-blank-document:before {
+ content: "\e64e";
+}
+
+.fw-ebook:before {
+ content: "\e670";
+}
+
+.fw-document:before {
+ content: "\e671";
+}
+
+.fw-text:before {
+ content: "\e64f";
+}
+
+.fw-html:before {
+ content: "\e650";
+}
+
+.fw-wadl:before {
+ content: "\e608";
+}
+
+.fw-wsdl:before {
+ content: "\e609";
+}
+
+.fw-xacml:before {
+ content: "\e60a";
+}
+
+.fw-xsd:before {
+ content: "\e60b";
+}
+
+.fw-xq:before {
+ content: "\e60c";
+}
+
+.fw-xslt:before {
+ content: "\e60d";
+}
+
+.fw-xml:before {
+ content: "\e61e";
+}
+
+.fw-soap:before {
+ content: "\e651";
+}
+
+.fw-wso2:before {
+ content: "\e607";
+}
+
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/main.css b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/main.css
new file mode 100755
index 00000000..2c7dd473
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/css/main.css
@@ -0,0 +1,484 @@
+/* _____________________________________________________________________________
+
+ FONT
+ _____________________________________________________________________________ */
+/* Regular */
+@font-face {
+ font-family: 'Open Sans';
+
+ src: url('../fonts/OpenSans-Regular-webfont.eot');
+ src: url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-Regular-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-Regular-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-Regular-webfont.svg#OpenSansRegular') format('svg');
+ font-weight: normal;
+ font-weight: 400;
+ font-style: normal;
+
+}
+
+/* Italic */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-Italic-webfont.eot');
+ src: url('../fonts/OpenSans-Italic-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-Italic-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-Italic-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-Italic-webfont.svg#OpenSansItalic') format('svg');
+ font-weight: normal;
+ font-weight: 400;
+ font-style: italic;
+
+}
+
+/* Light */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-Light-webfont.eot');
+ src: url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-Light-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-Light-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-Light-webfont.svg#OpenSansLight') format('svg');
+ font-weight: 200;
+ font-style: normal;
+
+}
+
+/* Light Italic */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-LightItalic-webfont.eot');
+ src: url('../fonts/OpenSans-LightItalic-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-LightItalic-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-LightItalic-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-LightItalic-webfont.svg#OpenSansLightItalic') format('svg');
+ font-weight: 200;
+ font-style: italic;
+
+}
+
+/* Semibold */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-Semibold-webfont.eot');
+ src: url('../fonts/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-Semibold-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-Semibold-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-Semibold-webfont.svg#OpenSansSemibold') format('svg');
+ font-weight: 500;
+ font-style: normal;
+
+}
+
+/* Semibold Italic */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-SemiboldItalic-webfont.eot');
+ src: url('../fonts/OpenSans-SemiboldItalic-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-SemiboldItalic-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-SemiboldItalic-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-SemiboldItalic-webfont.svg#OpenSansSemiboldItalic') format('svg');
+ font-weight: 500;
+ font-style: italic;
+
+}
+
+/* Bold */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-Bold-webfont.eot');
+ src: url('../fonts/OpenSans-Bold-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-Bold-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-Bold-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-Bold-webfont.svg#OpenSansBold') format('svg');
+ font-weight: bold;
+ font-weight: 700;
+ font-style: normal;
+
+}
+
+/* Bold Italic */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-BoldItalic-webfont.eot');
+ src: url('../fonts/OpenSans-BoldItalic-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-BoldItalic-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-BoldItalic-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-BoldItalic-webfont.svg#OpenSansBoldItalic') format('svg');
+ font-weight: bold;
+ font-weight: 700;
+ font-style: italic;
+
+}
+
+/* Extra Bold */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-ExtraBold-webfont.eot');
+ src: url('../fonts/OpenSans-ExtraBold-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-ExtraBold-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-ExtraBold-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-ExtraBold-webfont.svg#OpenSansExtrabold') format('svg');
+ font-weight: 900;
+ font-style: normal;
+
+}
+
+/* Extra Bold Italic */
+@font-face {
+ font-family: 'Open Sans';
+ src: url('../fonts/OpenSans-ExtraBoldItalic-webfont.eot');
+ src: url('../fonts/OpenSans-ExtraBoldItalic-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/OpenSans-ExtraBoldItalic-webfont.woff') format('woff'),
+ url('../fonts/OpenSans-ExtraBoldItalic-webfont.ttf') format('truetype'),
+ url('../fonts/OpenSans-ExtraBoldItalic-webfont.svg#OpenSansExtraboldItalic') format('svg');
+ font-weight: 900;
+ font-style: italic;
+
+}
+/* _____________________________________________________________________________
+
+ RESET
+ _____________________________________________________________________________ */
+html, body, div, span, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+abbr, address, cite, code,
+del, dfn, em, img, ins, kbd, q, samp,
+small, strong, sub, sup, var,
+b, i,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section, summary,
+time, mark, audio, video {
+ margin:0;
+ padding:0;
+ border:0;
+ outline:0;
+ font-size:100%;
+ vertical-align:baseline;
+ background:transparent;
+}
+body {
+ font-family: 'Open Sans';
+ line-height:1;
+ color:#141414;
+ line-height:1;
+ background: url("../images/content-bg-1.png")repeat scroll 0 0 #f0f2f4;
+ margin: 15px;
+}
+
+
+article,aside,details,figcaption,figure,
+footer,header,hgroup,menu,nav,section {
+ display:block;
+}
+nav ul {
+ list-style:none;
+}
+blockquote, q {
+ quotes:none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content:'';
+ content:none;
+}
+a {
+ margin:0;
+ padding:0;
+ font-size:100%;
+ vertical-align:baseline;
+ background:transparent;
+}
+/* change colours to suit your needs */
+ins {
+ background-color:#ff9;
+ color:#000;
+ text-decoration:none;
+}
+/* change colours to suit your needs */
+mark {
+ background-color:#ff9;
+ color:#000;
+ font-style:italic;
+ font-weight:bold;
+}
+del {
+ text-decoration: line-through;
+}
+abbr[title], dfn[title] {
+ border-bottom:1px dotted;
+ cursor:help;
+}
+table {
+ border-collapse:collapse;
+ border-spacing:0;
+}
+/* change border colour to suit your needs */
+hr {
+ display:block;
+ height:1px;
+ border:0;
+ border-top:1px solid #7f7f7f;
+ margin:1em 0;
+ padding:0;
+ opacity: 0.2;
+}
+input, select {
+ vertical-align:middle;
+}
+
+/* _____________________________________________________________________________
+
+ TYPOGRAPHY
+ _____________________________________________________________________________ */
+
+
+p{font-size:17px; line-height:24px; color:#c7c7c7;}
+h1{ font-size:39px;font-weight:100; line-height:Auto; font-style: normal;}
+h2{font-size:24px; line-height:Auto; font-weight: 400; }
+h3{font-size:15px; line-height:Auto; font-weight: 400; }
+h4{font-size:20px; line-height:Auto; font-weight: 400; }
+h5{ font-size:30px;font-weight:100; line-height:Auto; font-style: normal; color: #929292;}
+.font-small{font-size:12px;}
+.font-large{font-size:25px;}
+.font-medium{font-size:16px;}
+.medium-weight{font-weight: 100;}
+
+.uppercase{text-transform:uppercase;}
+.capitalize{text-transform:capitalize;}
+.lowercase{text-transform:lowercase;}
+
+
+.light-blue{color:#3a9ecf;}
+.black{color:#000;}
+.grey{color:#333;}
+.light-grey{color:#7c7c7c;}
+.white{color:#FFF;}
+.blue{color: #00979c;} .blue:hover{color: #03c5cb;}
+
+.bold{font-weight:bold;}
+.no-underline{text-decoration:none;}
+.italic{font-style: italic;}
+
+.center{text-align:center;}
+.right{text-align:right;}
+.left{text-align:left;}
+.justify{text-align:justify;}
+
+.middle{vertical-align: middle;}
+
+.top{vertical-align: top;}
+.middle{vertical-align: middle;}
+.bottom{vertical-align: bottom;}
+.rounded{-webkit-border-radius: 0px;
+ -moz-border-radius: 0px;
+ border-radius: 0px;}
+.opacity{opacity: 0.8;}
+
+
+.circle {
+ background: none repeat scroll 0 0 #191919;
+ border-radius: 50px;
+ height: 50px;
+ padding: 10px;
+ width: 50px;
+ color: #fff;
+}
+
+
+/* _____________________________________________________________________________
+
+ BACKGROUND
+ _____________________________________________________________________________ */
+.grey-bg{ background-color:#f6f4f4;}
+.light-grey-bg{ background-color:#f5f5f5 ; }
+.white-bg{background-color:#fff;}
+.off-white{background-color:#3a9ecf;}
+.dark-grey{background-color: #2a2a2a;}
+.blue-bg{background-color: #00979c;}
+
+/* _____________________________________________________________________________
+
+ Main
+ _____________________________________________________________________________ */
+
+body { margin: 0; }
+header{background-color: #2a2a2a;}
+
+
+.product-wrapper{ float: left; width:100%; margin-top: -2px; min-height: 310px; -webkit-box-shadow: 5px 5px 5px 0px rgba(0,0,0,0.14);
+ -moz-box-shadow: 5px 5px 5px 0px rgba(0,0,0,0.14);
+ box-shadow: 5px 5px 5px 0px rgba(0,0,0,0.14);}
+.icon-wrapper{min-height: 110px; padding-top: 12px; text-align: center; }
+.text-wrapper{margin:15px; font-size:14px; border-top: 1px solid #404040; padding-top: 20px; color:#c6c5c5; line-height: 18px; text-align: center }
+.sign-panel {background-color: #191919; width: 100%; height: auto; min-height: 150px; display: none; padding:15px; color: #a5a5a5; cursor:pointer;}
+#flip-login{ font-weight: 500; font-size: 14px; padding: 20px; cursor:pointer; margin-top:-2px; }
+.chart1,.chart2,.chart3 {width: 100%; height: 350px;}
+/* ____________________________________________________________________________
+
+ COLUMNS
+ _____________________________________________________________________________ */
+
+.col-row{width:100%; height: 100px;}
+.col-x-large{width:100%; }
+.col-large{width:75%; }
+.col-medium{width:50%; }
+.col-small{width:24%; }
+.col-x-small{width:223px; }
+
+.opacity{opacity: 0.2;}
+/* ____________________________________________________________________________
+
+ padding-margin
+ _____________________________________________________________________________ */
+.padding-left {padding-left:10px;}
+.padding-right {padding-right:10px;}
+.padding-top {padding-top:10px;}
+.padding-bottom {padding-bottom:10px;}
+.padding{padding:10px;}
+.padding-none{ padding:0px;}
+.padding-left-none{ padding-left:0px;}
+
+
+.padding-left-double {padding-left:20px;}
+.padding-right-double {padding-right:20px;}
+.padding-top-double {padding-top:20px;}
+.padding-bottom-double {padding-bottom:20px;}
+.padding-double{padding:20px;}
+.padding-top-large{padding-top:60px;}
+.padding-bottom-large{padding-bottom:60px;}
+
+.margin-left {margin-left:10px;}
+.margin-right {margin-right:10px;}
+.margin-top {margin-top:10px;}
+.margin-bottom {margin-bottom:10px;}
+.margin{margin:10px;}
+.margin-top-minus{margin-top:-123px;}
+
+.margin-left-double {margin-left:20px;}
+.margin-right-double {margin-right:20px;}
+.margin-top-double {margin-top:20px;}
+.margin-bottom-double {margin-bottom:20px;}
+.margin-double{margin:20px;}
+.margin-left-none{margin-left: 0px;}
+.margin-right-none{margin-right: 0px;}
+
+.float-left{float:left;}
+.float-right{float:right;}
+.clear{ clear:both;}
+
+.inline-block{display: inline-block;}
+.relative{ position:relative;}
+.border{border: 2px solid #dddddd;}
+.border-bottom{border-bottom: 1px solid #efefef;}
+.border-radius{border-radius: 5px;}
+.border-top{border-top: 1px solid #ddd;}
+
+/* ____________________________________________________________________________
+
+ button
+ _____________________________________________________________________________ */
+.blue-btn {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0px;
+ color: #ffffff;
+ font-size: 15px;
+ font-weight: 500;
+ background: #00979c;
+ padding: 10px 20px 10px 20px;
+ text-decoration: none;
+ border: 0px; cursor:pointer;
+}
+
+.blue-btn:hover {
+ background: #00787a;
+ text-decoration: none;
+}
+.blue-action-btn {
+ -webkit-border-radius: 0px;
+ -moz-border-radius: 0px;
+ border-radius: 0px;
+ color: #ffffff;
+ font-size: 15px;
+ font-weight: 500;
+ background: #00979c;
+ padding: 10px 30px;
+ height: 45px;
+ text-decoration: none;
+ border: 0px; cursor:pointer;
+}
+
+.blue-action-btn:hover {
+ background: #00787a;
+ text-decoration: none;
+}
+.black-btn {
+ -webkit-border-radius: 0px;
+ -moz-border-radius: 0px;
+ border-radius: 0px;
+ color: #ccc;
+ font-size: 15px;
+ font-weight: 500;
+ background: #333333;
+ padding: 10px 30px;
+ height: 45px;
+ text-decoration: none;
+ border: 0px; cursor:pointer;
+}
+
+.black-btn:hover {
+ background: #2a2a2a;
+ text-decoration: none; ;
+}
+.grey-btn {
+ background: none repeat scroll 0 0 #00979c;
+ border: 0 none;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ color: #ffffff;
+ cursor: pointer;
+ font-size: 18px;
+ font-weight: 300;
+ padding: 10px 35px;
+ text-decoration: none;
+}
+.grey-btn:hover {
+ background: none repeat scroll 0 0 #006a6d;
+ border: 0 none;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ color: #ffffff;
+ cursor: pointer;
+ font-size: 18px;
+ font-weight: 300;
+ padding: 10px 35px;
+ text-decoration: none;
+}
+.grey-btn a{color: #fff; text-decoration: none;}
+.btn-black{color: #ffffff; margin-right: 5px; padding: 10px; text-decoration: none; overflow: hidden; background:#5c5c5c; text-align: center; float: left; cursor: pointer; }
+.btn-black:hover{opacity: 0.8; }
+
+.menu-button-wrapper-temp{float: left; font-size: 13px; width: 125px;}
+.menu-button-wrapper{float: left; font-size: 13px; width: 100px;}
+.tick { opacity: 0 !important; }
+.nv-discreteBarWithAxes>g>.nv-x>g>g>g{opacity: 1 !important;}
+#date-range1{
+ background: #fff none repeat scroll 0 0;
+ border: 1px solid #ccc;
+ width: 300px;
+}
+
+#device_id{
+ background: #fff none repeat scroll 0 0;
+ border: 1px solid #ccc;
+}
+
+.hour-range,.minute-range{background: #fff none repeat scroll 0 0; border: 1px solid #ccc;}
+
+.btn-black-action{color: #ffffff; padding: 5px 20px; text-decoration: none; overflow: hidden; background:#5c5c5c; text-align: center; margin: 0 10px; float: left; cursor: pointer; border: none; outline:none;}
+.btn-black-action:hover{background-color: #0076a3; }
+.date-picker-wrapper {box-shadow:none;}
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.eot
new file mode 100755
index 00000000..5d4a1c47
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.svg
new file mode 100755
index 00000000..7cda1026
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.svg
@@ -0,0 +1,251 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 20102011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.ttf
new file mode 100755
index 00000000..7ab5d85b
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.woff
new file mode 100755
index 00000000..869a9ed8
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Bold-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.eot
new file mode 100755
index 00000000..ad651807
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.svg
new file mode 100755
index 00000000..91491201
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.svg
@@ -0,0 +1,251 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 20102011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.ttf
new file mode 100755
index 00000000..6a30fa9d
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.woff
new file mode 100755
index 00000000..46778a21
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-BoldItalic-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.eot
new file mode 100755
index 00000000..2f7ae28d
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.svg
new file mode 100755
index 00000000..009e1d7b
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.svg
@@ -0,0 +1,251 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 2011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.ttf
new file mode 100755
index 00000000..dacc5bbb
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.woff
new file mode 100755
index 00000000..de4f8e77
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBold-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.eot
new file mode 100755
index 00000000..e4f4ab0d
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.svg
new file mode 100755
index 00000000..a0612586
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.svg
@@ -0,0 +1,251 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 20102011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.ttf
new file mode 100755
index 00000000..7e636eb4
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.woff
new file mode 100755
index 00000000..f81b2161
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-ExtraBoldItalic-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.eot
new file mode 100755
index 00000000..c3159521
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.svg
new file mode 100755
index 00000000..be508574
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.svg
@@ -0,0 +1,251 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 20102011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.ttf
new file mode 100755
index 00000000..cb3fda65
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.woff
new file mode 100755
index 00000000..03eaf586
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Italic-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.eot
new file mode 100755
index 00000000..f17617e0
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.svg
new file mode 100755
index 00000000..deadc3ef
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.svg
@@ -0,0 +1,252 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 20102011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.ttf
new file mode 100755
index 00000000..b83078a6
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.woff
new file mode 100755
index 00000000..ff882b6a
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Light-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.eot
new file mode 100755
index 00000000..95c6c619
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.svg
new file mode 100755
index 00000000..1ac169c0
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.svg
@@ -0,0 +1,252 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 20102011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.ttf
new file mode 100755
index 00000000..3162ff8e
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.woff
new file mode 100755
index 00000000..f6e97d5a
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-LightItalic-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.eot
new file mode 100755
index 00000000..545b7c15
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.svg
new file mode 100755
index 00000000..46a8f4c6
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.svg
@@ -0,0 +1,252 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 20102011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.ttf
new file mode 100755
index 00000000..a5b2378e
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.woff
new file mode 100755
index 00000000..11698afc
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Regular-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.eot
new file mode 100755
index 00000000..acc32c42
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.svg
new file mode 100755
index 00000000..ef8b7a2b
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.svg
@@ -0,0 +1,251 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 2011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.ttf
new file mode 100755
index 00000000..a5b9691c
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.woff
new file mode 100755
index 00000000..17fb5dc3
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-Semibold-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.eot
new file mode 100755
index 00000000..0048da00
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.svg
new file mode 100755
index 00000000..46cfd5c8
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.svg
@@ -0,0 +1,251 @@
+
+
+
+
+This is a custom SVG webfont generated by Font Squirrel.
+Copyright : Digitized data copyright 20102011 Google Corporation
+Foundry : Ascender Corporation
+Foundry URL : httpwwwascendercorpcom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.ttf
new file mode 100755
index 00000000..61d58bfa
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.woff
new file mode 100755
index 00000000..611b3902
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/OpenSans-SemiboldItalic-webfont.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.eot
new file mode 100755
index 00000000..5bc87bdb
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.svg
new file mode 100755
index 00000000..40683e9e
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.svg
@@ -0,0 +1,124 @@
+
+
+
+Generated by IcoMoon
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.ttf
new file mode 100755
index 00000000..787e513d
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.woff
new file mode 100755
index 00000000..669b1a42
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/fontwso2.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.eot b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.eot
new file mode 100755
index 00000000..4a4ca865
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.eot differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.svg b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.svg
new file mode 100755
index 00000000..25691af8
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.ttf b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.ttf
new file mode 100755
index 00000000..67fa00bf
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.woff b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.woff
new file mode 100755
index 00000000..8c54182a
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/fonts/glyphicons-halflings-regular.woff differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/images/content-bg-1.png b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/images/content-bg-1.png
new file mode 100755
index 00000000..288797ce
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/images/content-bg-1.png differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/images/content-bg.png b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/images/content-bg.png
new file mode 100755
index 00000000..cf4e63e7
Binary files /dev/null and b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/images/content-bg.png differ
diff --git a/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/js/bootstrap.min.js b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/js/bootstrap.min.js
new file mode 100755
index 00000000..c8f82e59
--- /dev/null
+++ b/modules/distribution/src/repository/jaggeryapps/sensebot/units/theme/public/js/bootstrap.min.js
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.3.4 (http://getbootstrap.com)
+ * Copyright 2011-2015 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.4",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.4",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.4",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.4",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('
').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j
').appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport),this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-m