5814 lines
197 KiB
JavaScript
Executable File
5814 lines
197 KiB
JavaScript
Executable File
window.Rubix = window.Rubix || {};
|
|
|
|
/*
|
|
* debouncedresize: special jQuery event that happens once after a window resize
|
|
*
|
|
* latest version and complete README available on Github:
|
|
* https://github.com/louisremi/jquery-smartresize
|
|
*
|
|
* Copyright 2012 @louis_remi
|
|
* Licensed under the MIT license.
|
|
*
|
|
* This saved you an hour of work?
|
|
* Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON
|
|
*/
|
|
(function($) {
|
|
|
|
var $event = $.event,
|
|
$special,
|
|
resizeTimeout;
|
|
|
|
$special = $event.special.debouncedresize = {
|
|
setup: function() {
|
|
$( this ).on( "resize", $special.handler );
|
|
},
|
|
teardown: function() {
|
|
$( this ).off( "resize", $special.handler );
|
|
},
|
|
handler: function( event, execAsap ) {
|
|
// Save the context
|
|
var context = this,
|
|
args = arguments,
|
|
dispatch = function() {
|
|
// set correct event type
|
|
event.type = "debouncedresize";
|
|
$event.dispatch.apply( context, args );
|
|
};
|
|
|
|
if ( resizeTimeout ) {
|
|
clearTimeout( resizeTimeout );
|
|
}
|
|
|
|
execAsap ?
|
|
dispatch() :
|
|
resizeTimeout = setTimeout( dispatch, $special.threshold );
|
|
},
|
|
threshold: 150
|
|
};
|
|
|
|
})(jQuery);
|
|
|
|
/*
|
|
* throttledresize: special jQuery event that happens at a reduced rate compared to "resize"
|
|
*
|
|
* latest version and complete README available on Github:
|
|
* https://github.com/louisremi/jquery-smartresize
|
|
*
|
|
* Copyright 2012 @louis_remi
|
|
* Licensed under the MIT license.
|
|
*
|
|
* This saved you an hour of work?
|
|
* Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON
|
|
*/
|
|
(function($) {
|
|
|
|
var $event = $.event,
|
|
$special,
|
|
dummy = {_:0},
|
|
frame = 0,
|
|
wasResized, animRunning;
|
|
|
|
$special = $event.special.throttledresize = {
|
|
setup: function() {
|
|
$( this ).on( "resize", $special.handler );
|
|
},
|
|
teardown: function() {
|
|
$( this ).off( "resize", $special.handler );
|
|
},
|
|
handler: function( event, execAsap ) {
|
|
// Save the context
|
|
var context = this,
|
|
args = arguments;
|
|
|
|
wasResized = true;
|
|
|
|
if ( !animRunning ) {
|
|
setInterval(function(){
|
|
frame++;
|
|
|
|
if ( frame > $special.threshold && wasResized || execAsap ) {
|
|
// set correct event type
|
|
event.type = "throttledresize";
|
|
$event.dispatch.apply( context, args );
|
|
wasResized = false;
|
|
frame = 0;
|
|
}
|
|
if ( frame > 9 ) {
|
|
$(dummy).stop();
|
|
animRunning = false;
|
|
frame = 0;
|
|
}
|
|
}, 30);
|
|
animRunning = true;
|
|
}
|
|
},
|
|
threshold: 0
|
|
};
|
|
|
|
})(jQuery);
|
|
|
|
$.fn.serializeObject=function(){"use strict";var a={},b=function(b,c){var d=a[c.name];"undefined"!=typeof d&&d!==null?$.isArray(d)?d.push(c.value):a[c.name]=[d,c.value]:a[c.name]=c.value};return $.each(this.serializeArray(),b),a};
|
|
|
|
|
|
var RubixListeners = [];
|
|
|
|
var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
|
|
var useTable = isSafari ? 'inline' : 'table';
|
|
|
|
/**
|
|
* @param {string} id
|
|
* @param {?Object} opts
|
|
* @constructor
|
|
*/
|
|
Rubix = function(id, opts) {
|
|
this.chart_counter = 0;
|
|
this.master_id = this.uid('master_id');
|
|
RubixListeners.push(this.master_id);
|
|
|
|
this.data_stack = {};
|
|
this.chart_stack = {};
|
|
|
|
this.area_stack_data_stack = [];
|
|
this.column_stack_data_stack = [];
|
|
|
|
this.root_elem = $(id);
|
|
this.root_elem.css('position', 'relative');
|
|
this.root_elem.addClass('rubixcc-main-chart');
|
|
this.root_elem.append('<div class="rubixcc-tooltip"></div>');
|
|
this.root_elem.append('<div class="rubixcc-title"></div>');
|
|
this.root_elem.append('<div class="rubixcc-subtitle"></div>');
|
|
this.root_elem.append('<div class="rubixcc-chart text-center"><div style="margin-top:5px">Loading...</div></div>');
|
|
this.root_elem.append('<div class="rubixcc-legend"></div>');
|
|
|
|
var width = opts.width || '100%';
|
|
var height = opts.height || 150;
|
|
this.root_elem.width(width).height(height);
|
|
|
|
this.elem = this.root_elem.find('.rubixcc-chart');
|
|
|
|
opts.tooltip = opts.tooltip || {};
|
|
this.tooltip = this.root_elem.find('.rubixcc-tooltip');
|
|
this.tooltip.hide();
|
|
|
|
if(opts && opts.tooltip && opts.tooltip.animate) {
|
|
this.tooltip.addClass('animate');
|
|
}
|
|
|
|
this.tooltip.html("");
|
|
this.tooltip.css({
|
|
'font-family': 'Lato, "Lucida Grande", Arial, Helvetica, sans-serif',
|
|
'font-size': '12px',
|
|
'position': 'absolute',
|
|
'background': 'white',
|
|
'color': '#89949B',
|
|
'padding': '10px 15px',
|
|
'display': 'none',
|
|
'pointer-events': 'none',
|
|
'border-radius': '5px',
|
|
'z-index': 100,
|
|
'min-height': 50,
|
|
'user-select': 'none',
|
|
'cursor': 'default',
|
|
'border': '3px solid ' + (opts.tooltip.color ? opts.tooltip.color : '#89949B'),
|
|
'box-shadow': 'rgba(0, 0, 0, 0.2) 2px 4px 8px',
|
|
'background': 'white'
|
|
});
|
|
|
|
opts.legend = opts.legend || {};
|
|
|
|
this.legend = this.root_elem.find('.rubixcc-legend');
|
|
this.legend.css({
|
|
'font-family': "Lato, 'Lucida Grande', Arial, Helvetica, sans-serif",
|
|
'text-align': 'center',
|
|
'margin-top': opts.legend.top || '-10px',
|
|
'margin-bottom': opts.legend.bottom || '5px',
|
|
'user-select': 'none',
|
|
'display': opts.hideLegend ? 'none' : 'block'
|
|
});
|
|
|
|
this.title = this.root_elem.find('.rubixcc-title');
|
|
this.subtitle = this.root_elem.find('.rubixcc-subtitle');
|
|
|
|
this.title.css({
|
|
'font-family': "Lato, 'Lucida Grande', Arial, Helvetica, sans-serif",
|
|
'text-align': 'center',
|
|
'user-select': 'none',
|
|
'font-weight': 'bold',
|
|
'font-size': '16px',
|
|
'color': 'steelblue',
|
|
'margin-top': '10px',
|
|
'cursor': 'default'
|
|
});
|
|
|
|
this.subtitle.css({
|
|
'font-family': "Lato, 'Lucida Grande', Arial, Helvetica, sans-serif",
|
|
'text-align': 'center',
|
|
'user-select': 'none',
|
|
'font-size': '12px',
|
|
'color': 'steelblue',
|
|
'margin-top': '5px',
|
|
'cursor': 'default',
|
|
'opacity': 0.8,
|
|
'font-weight': 'bold'
|
|
});
|
|
|
|
var self = this;
|
|
this.tooltip.on({
|
|
'mouseover': function(e) {
|
|
d3.select(self.elem.find('.overlay').get(0)).node().__onmousemove(e);
|
|
},
|
|
'mousemove': function(e) {
|
|
d3.select(self.elem.find('.overlay').get(0)).node().__onmousemove(e);
|
|
},
|
|
'mouseout': function(e) {
|
|
$(this).hide();
|
|
}
|
|
});
|
|
|
|
this.elem.css({
|
|
'width': '100%',
|
|
'height': parseInt(this.root_elem.get(0).style.height),
|
|
'user-select': 'none',
|
|
'cursor': 'default',
|
|
'position': 'relative'
|
|
});
|
|
this.root_elem.css('height', '100%');
|
|
this.opts = opts || {};
|
|
|
|
this.data = {};
|
|
this.charts = {};
|
|
this.extent = [];
|
|
this.extent_coordinates = [];
|
|
this.is_touch_device = 'ontouchstart' in document.documentElement;
|
|
this.d3_eventSource = function () {
|
|
var e = d3.event, s;
|
|
while (s = e.sourceEvent) e = s;
|
|
return e;
|
|
}
|
|
|
|
this.custom_interpolations = {
|
|
sankey: function(points) {
|
|
var x0 = points[0][0], y0 = points[0][1], x1, y1, x2,
|
|
path = [x0, ",", y0],
|
|
i = 0,
|
|
n = points.length;
|
|
while (++i < n) {
|
|
x1 = points[i][0], y1 = points[i][1], x2 = (x0 + x1) / 2;
|
|
path.push("C", x2, ",", y0, " ", x2, ",", y1, " ", x1, ",", y1);
|
|
x0 = x1, y0 = y1;
|
|
}
|
|
return path.join("");
|
|
}
|
|
};
|
|
|
|
this.xlabelcolor = 'steelblue';
|
|
this.ylabelcolor = 'steelblue';
|
|
|
|
this.first_time = true;
|
|
this.last_render = null;
|
|
|
|
this.data_changed = false;
|
|
|
|
this.setup();
|
|
};
|
|
|
|
Rubix.prototype.setup = function() {
|
|
this._setupOpts();
|
|
this._setupOnce();
|
|
this._setupRedraw();
|
|
|
|
this.draw();
|
|
};
|
|
|
|
Rubix.prototype.draw = function() {
|
|
if(!this.data_changed) {
|
|
if(this.last_render !== null)
|
|
if(Date.now() - this.last_render < 300) return;
|
|
this.last_render = Date.now();
|
|
}
|
|
this.data_changed = false;
|
|
if(!this.root_elem.is(':visible')) return;
|
|
this._draw();
|
|
};
|
|
|
|
Rubix.prototype.uid = function(type) {
|
|
return 'rubixcc-' + type +'-' + Math.floor(2147483648*Math.random()).toString(36);
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.prototype._setupOpts = function() {
|
|
this.opts.theme_style = this.opts.theme_style || $('body').attr('data-theme') || 'light';
|
|
this.opts.theme_style_color = this.opts.gridColor || ((this.opts.theme_style === 'light') ? '#C0D0E0' : '#555');
|
|
this.opts.theme_focus_line_color = this.opts.focusLineColor || ((this.opts.theme_style === 'light') ? '#C0D0E0' : '#888');
|
|
this.opts.tickColor = this.opts.tickColor || ((this.opts.theme_style === 'light') ? '#666' : '#999');
|
|
|
|
this.opts.titleColor = this.opts.titleColor || 'steelblue';
|
|
this.opts.subtitleColor = this.opts.subtitleColor || 'steelblue';
|
|
|
|
this.opts.legend_color_brightness = this.opts.legend_color_brightness || 0.5;
|
|
|
|
this.title.css('color', this.opts.titleColor);
|
|
this.subtitle.css('color', this.opts.subtitleColor);
|
|
|
|
if(this.opts.theme_style === 'dark' || this.opts.tooltip.theme_style === 'dark') {
|
|
this.tooltip.css({
|
|
"color": "#aaa",
|
|
"font-weight": "bold",
|
|
"border": "1px solid #222",
|
|
"background-color": "#303030"
|
|
});
|
|
}
|
|
|
|
this.opts.margin = this.opts.margin || {};
|
|
this.margin = {
|
|
top : (this.opts.margin.top >= 0) ? this.opts.margin.top : 25,
|
|
left : (this.opts.margin.left >= 0) ? this.opts.margin.left : 25,
|
|
right : (this.opts.margin.right >= 0) ? this.opts.margin.right : 12.5,
|
|
bottom : (this.opts.margin.bottom >= 0) ? this.opts.margin.bottom : 25
|
|
};
|
|
|
|
this.opts.draw = this.opts.draw || {};
|
|
this.opts.draw = {
|
|
grid: (this.opts.draw.grid === false) ? false : true
|
|
};
|
|
|
|
this.opts.invertAxes = this.opts.invertAxes || false;
|
|
this.opts.axis = this.opts.axis || {};
|
|
this.opts.axis.x = this.opts.axis.x || {};
|
|
this.opts.axis.y = this.opts.axis.y || {};
|
|
this.axis = {
|
|
x: {
|
|
type: this.opts.axis.x.type || 'linear',
|
|
range: this.opts.axis.x.range || '',
|
|
tickCount : this.opts.axis.x.tickCount,
|
|
tickFormat: this.opts.axis.x.tickFormat || '',
|
|
label: this.opts.axis.x.label || '',
|
|
labelColor: this.opts.axis.x.labelColor || 'steelblue'
|
|
},
|
|
y: {
|
|
type: this.opts.axis.y.type || 'linear',
|
|
range: this.opts.axis.y.range || '',
|
|
tickCount : this.opts.axis.y.tickCount,
|
|
tickFormat: this.opts.axis.y.tickFormat || '',
|
|
label: this.opts.axis.y.label || '',
|
|
labelColor: this.opts.axis.y.labelColor || 'steelblue'
|
|
}
|
|
};
|
|
|
|
this.xlabelcolor = this.opts.axis.x.labelColor;
|
|
this.ylabelcolor = this.opts.axis.y.labelColor;
|
|
|
|
if(this.axis.x.label.length) {
|
|
if(this.opts.invertAxes) {
|
|
this.margin.left += 15;
|
|
} else {
|
|
this.margin.bottom += 15;
|
|
}
|
|
}
|
|
|
|
if(this.axis.y.label.length) {
|
|
if(this.opts.invertAxes) {
|
|
this.margin.bottom += 15;
|
|
} else {
|
|
this.margin.left += 15;
|
|
}
|
|
}
|
|
|
|
this.margin.defaultLeft = this.margin.left;
|
|
|
|
this.opts.tooltip = this.opts.tooltip || {};
|
|
this.opts.tooltip.format = this.opts.tooltip.format || {};
|
|
this.opts.tooltip.abs = this.opts.tooltip.abs || {};
|
|
this.tooltipFormatter = {
|
|
format: {
|
|
x: this.opts.tooltip.format.x || '',
|
|
y: this.opts.tooltip.format.y || ''
|
|
},
|
|
abs: {
|
|
x: (this.opts.tooltip.abs.hasOwnProperty('x')) ? this.opts.tooltip.abs.x : false,
|
|
y: (this.opts.tooltip.abs.hasOwnProperty('y')) ? this.opts.tooltip.abs.y : false
|
|
}
|
|
}
|
|
|
|
if(this.opts.invertAxes) {
|
|
var temp = this.axis.x;
|
|
this.axis.x = this.axis.y;
|
|
this.axis.y = temp;
|
|
|
|
var temp2 = this.tooltipFormatter.format.x;
|
|
this.tooltipFormatter.format.x = this.tooltipFormatter.format.y;
|
|
this.tooltipFormatter.format.y = temp2;
|
|
}
|
|
|
|
if(this.opts.animationSpeed !== 0) {
|
|
this.animationSpeed = this.opts.animationSpeed || 750;
|
|
}
|
|
|
|
this.opts.interval = this.opts.start_interval || 0;
|
|
|
|
this.stacked = this.opts.stacked || false;
|
|
this.grouped = this.opts.grouped || false;
|
|
this.offset = this.opts.offset || 'zero';
|
|
this.order = this.opts.order || 'default';
|
|
this.show_markers = this.opts.show_markers || false;
|
|
|
|
this.resize = this.opts.resize || 'debounced';
|
|
|
|
this.interpolate = this.opts.interpolate || 'linear';
|
|
switch(this.interpolate) {
|
|
case 'sankey':
|
|
this.interpolate = this.custom_interpolations[this.interpolate];
|
|
break;
|
|
default:
|
|
// do nothing
|
|
break;
|
|
}
|
|
|
|
this.master_detail = this.opts.master_detail || false;
|
|
|
|
this.master_detail_height = this.opts.master_detail_height || 50;
|
|
if(this.master_detail) {
|
|
this.master_detail_margin_bottom = this.margin.bottom;
|
|
this.margin.bottom = (2 * this.margin.bottom) + this.master_detail_height;
|
|
}
|
|
|
|
this.opts.title = this.opts.title || '';
|
|
this.opts.subtitle = this.opts.subtitle || '';
|
|
|
|
this.title.html(this.opts.title);
|
|
this.subtitle.html(this.opts.subtitle);
|
|
if(this.opts.title.length || this.opts.subtitle.length) {
|
|
this.elem.css('margin-top', '-20px');
|
|
}
|
|
(this.opts.title.length) ? this.title.show() : this.title.hide();
|
|
(this.opts.subtitle.length) ? this.subtitle.show() : this.subtitle.hide();
|
|
};
|
|
|
|
function d3_scale_ordinal_invert(x) {
|
|
// TODO take into account current ranger
|
|
|
|
var _range = this.range().concat();
|
|
var _domain = this.domain().concat();
|
|
|
|
_range.sort(function(a, b) {
|
|
if(a > b) {
|
|
return 1;
|
|
} else if(a === b) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
})
|
|
_domain.sort(function(a, b) {
|
|
if(a > b) {
|
|
return 1;
|
|
} else if(a === b) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
})
|
|
return _domain[d3.bisect(_range, x) - 1];
|
|
}
|
|
|
|
/** @private */
|
|
Rubix.prototype._setupOnce = function() {
|
|
var self = this;
|
|
this.area_stack_data = [];
|
|
this.area_stack = d3.layout.stack();
|
|
this.area_stack.offset(this.offset);
|
|
this.area_stack.order(this.order);
|
|
this.area_stack.values(function(d) {
|
|
return d.values;
|
|
});
|
|
this.area_stack.x(function(d) {
|
|
return d.x;
|
|
});
|
|
this.area_stack.y(function(d) {
|
|
return d.y;
|
|
});
|
|
this.area_stack.out(function(d, y0, y) {
|
|
d.y0 = y0;
|
|
d.y_new = y;
|
|
});
|
|
this.column_stack_data = [];
|
|
this.column_stack = d3.layout.stack();
|
|
this.column_stack.offset('zero');
|
|
this.column_stack.order('default');
|
|
this.column_stack.values(function(d) {
|
|
return d.values;
|
|
});
|
|
this.column_stack.x(function(d) {
|
|
return d.x;
|
|
});
|
|
this.column_stack.y(function(d) {
|
|
return d.y;
|
|
});
|
|
this.column_stack.out(function(d, y0, y) {
|
|
d.y0 = y0;
|
|
d.y_new = y;
|
|
});
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.prototype._setupAxis = function(animate) {
|
|
switch(this.axis.x.type) {
|
|
case 'linear':
|
|
this.x = d3.scale.linear();
|
|
this.x.tickFormat(d3.format(this.axis.x.tickFormat));
|
|
|
|
var range = [0, this.width];
|
|
var masterRange = [0, this.width];
|
|
if(this.master_detail) {
|
|
this.x2 = d3.scale.linear();
|
|
}
|
|
|
|
if(this.axis.x.range === 'round') {
|
|
this.x.rangeRound(range);
|
|
|
|
if(this.master_detail) {
|
|
this.x2.rangeRound(masterRange);
|
|
}
|
|
} else {
|
|
this.x.range(range);
|
|
|
|
if(this.master_detail) {
|
|
this.x2.range(masterRange);
|
|
}
|
|
}
|
|
break;
|
|
case 'ordinal':
|
|
this.x = d3.scale.ordinal();
|
|
this.x.invert = d3_scale_ordinal_invert.bind(this.x);
|
|
|
|
var range = [0, this.width];
|
|
var masterRange = [0, this.width];
|
|
if(this.master_detail) {
|
|
this.canvas.select('.noData').text('Master-Detail chart only for quantitative scales.');
|
|
throw new Error('Master-Detail chart only for quantitative scales.');
|
|
}
|
|
|
|
if(this.axis.x.range === 'round') {
|
|
this.x.rangeRoundBands(range);
|
|
} else if(this.axis.x.range == 'column' || this.axis.x.range == 'bar') {
|
|
this.x.rangeRoundBands(range, 0.35);
|
|
} else {
|
|
this.x.rangePoints(range);
|
|
|
|
if(this.master_detail) {
|
|
this.x2.rangePoints(range);
|
|
}
|
|
}
|
|
break;
|
|
case 'datetime':
|
|
this.x = d3.time.scale();
|
|
this.x.ticks(d3.time.year, 50);
|
|
this.x.tickFormat(d3.format(this.axis.x.tickFormat));
|
|
|
|
var range = [0, this.width];
|
|
var masterRange = [0, this.width];
|
|
if(this.master_detail) {
|
|
this.x2 = d3.time.scale();
|
|
}
|
|
|
|
if(this.axis.x.range === 'round') {
|
|
this.x.rangeRound(range);
|
|
|
|
if(this.master_detail) {
|
|
this.x2.rangeRound(masterRange);
|
|
}
|
|
} else {
|
|
this.x.range(range);
|
|
|
|
if(this.master_detail) {
|
|
this.x2.range(masterRange);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error('Unknown Scale for X Axis: ' + this.axis.x.type);
|
|
break;
|
|
}
|
|
|
|
switch(this.axis.y.type) {
|
|
case 'linear':
|
|
this.y = d3.scale.linear();
|
|
this.y.clamp(true);
|
|
this.y.tickFormat(d3.format(this.axis.y.tickFormat));
|
|
|
|
var range = [this.height, 0];
|
|
var masterRange = [this.master_detail_height, 0];
|
|
if(this.master_detail) {
|
|
this.y2 = d3.scale.linear();
|
|
this.y2.clamp(true);
|
|
}
|
|
|
|
if(this.axis.y.range === 'round') {
|
|
this.y.rangeRound(range);
|
|
|
|
if(this.master_detail) {
|
|
this.y2.rangeRound(masterRange);
|
|
}
|
|
} else {
|
|
this.y.range(range);
|
|
|
|
if(this.master_detail) {
|
|
this.y2.range(masterRange);
|
|
}
|
|
}
|
|
break;
|
|
case 'ordinal':
|
|
this.y = d3.scale.ordinal();
|
|
this.y.invert = d3_scale_ordinal_invert.bind(this.y);
|
|
|
|
var range = [this.height, 0];
|
|
var masterRange = [this.master_detail_height, 0];
|
|
if(this.master_detail) {
|
|
this.y2 = d3.scale.ordinal();
|
|
this.y2.invert = d3_scale_ordinal_invert.bind(this.y2);
|
|
}
|
|
|
|
if(this.axis.y.range === 'round') {
|
|
this.y.rangeRoundBands(range);
|
|
|
|
if(this.master_detail) {
|
|
this.y2.rangeRoundBands(masterRange);
|
|
}
|
|
} else if(this.axis.y.range == 'column' || this.axis.y.range == 'bar') {
|
|
this.y.rangeRoundBands(range, 0.35);
|
|
} else {
|
|
this.y.rangePoints(range);
|
|
|
|
if(this.master_detail) {
|
|
this.y2.rangePoints(masterRange);
|
|
}
|
|
}
|
|
break;
|
|
case 'datetime':
|
|
this.y = d3.time.scale();
|
|
this.y.tickFormat(d3.format(this.axis.y.tickFormat));
|
|
this.y.clamp(true);
|
|
|
|
var range = [this.height, 0];
|
|
var masterRange = [this.master_detail_height, 0];
|
|
if(this.master_detail) {
|
|
this.y2 = d3.time.scale();
|
|
this.y2.clamp(true);
|
|
}
|
|
|
|
if(this.axis.y.range === 'round') {
|
|
this.y.rangeRound(range);
|
|
|
|
if(this.master_detail) {
|
|
this.y2.rangeRound(masterRange);
|
|
}
|
|
} else {
|
|
this.y.range(range);
|
|
|
|
if(this.master_detail) {
|
|
this.y2.range(masterRange);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error('Unknown Scale for X Axis: ' + this.axis.y.type);
|
|
break;
|
|
}
|
|
|
|
this.fontSize = 10;
|
|
var yTicks = Math.floor((this.height / (this.fontSize + 3)) / 2);
|
|
|
|
this.xAxis = d3.svg.axis();
|
|
this.xAxis.scale(this.x).orient('bottom');
|
|
|
|
if(this.master_detail) {
|
|
this.xAxis2 = d3.svg.axis();
|
|
this.xAxis2.scale(this.x2).orient('bottom');
|
|
}
|
|
|
|
if(this.axis.x.range === 'round' && (this.axis.x.type === 'linear' || this.axis.x.type === 'datetime')) {
|
|
if(this.axis.x.type === 'linear') {
|
|
this.xAxis.tickFormat(d3.format(this.axis.x.tickFormat));
|
|
} else if(this.axis.x.type === 'datetime') {
|
|
this.xAxis.tickFormat(d3.time.format(this.axis.x.tickFormat));
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
if(this.axis.x.type === 'linear') {
|
|
this.xAxis2.tickFormat(d3.format(this.axis.x.tickFormat));
|
|
} else {
|
|
this.xAxis2.tickFormat(d3.time.format(this.axis.x.tickFormat));
|
|
}
|
|
}
|
|
}
|
|
|
|
if(this.axis.x.tickFormat) {
|
|
if(this.axis.x.type === 'linear') {
|
|
this.xAxis.tickFormat(d3.format(this.axis.x.tickFormat));
|
|
} else if(this.axis.x.type === 'datetime') {
|
|
this.xAxis.tickFormat(d3.time.format(this.axis.x.tickFormat));
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
if(this.axis.x.type === 'linear') {
|
|
this.xAxis2.tickFormat(d3.format(this.axis.x.tickFormat));
|
|
} else {
|
|
this.xAxis2.tickFormat(d3.time.format(this.axis.x.tickFormat));
|
|
}
|
|
}
|
|
}
|
|
|
|
this.xGrid = d3.svg.axis();
|
|
this.xGrid.scale(this.x).orient('bottom').ticks(5).tickSize(-this.height, 0, 0).tickFormat('');
|
|
|
|
if(this.axis.x.tickCount !== undefined) {
|
|
this.xGrid.ticks(this.axis.x.tickCount);
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.xGrid2 = d3.svg.axis();
|
|
this.xGrid2.scale(this.x2).orient('bottom').ticks(5).tickSize(-this.master_detail_height, 0, 0).tickFormat('');
|
|
}
|
|
|
|
this.yAxis = d3.svg.axis();
|
|
this.yAxis.scale(this.y).orient('left');
|
|
|
|
this.yAxis.ticks(yTicks);
|
|
|
|
if(this.axis.y.range === 'round' && (this.axis.y.type === 'linear' || this.axis.y.type === 'datetime')) {
|
|
if(this.axis.y.type === 'linear') {
|
|
this.yAxis.tickFormat(d3.format(this.axis.y.tickFormat));
|
|
} else if(this.axis.y.type === 'datetime') {
|
|
this.yAxis.tickFormat(d3.time.format(this.axis.y.tickFormat));
|
|
}
|
|
}
|
|
|
|
if(this.axis.y.tickFormat) {
|
|
if(this.axis.y.type === 'linear') {
|
|
this.yAxis.tickFormat(d3.format(this.axis.y.tickFormat));
|
|
} else if(this.axis.y.type === 'datetime') {
|
|
this.yAxis.tickFormat(d3.time.format(this.axis.y.tickFormat));
|
|
}
|
|
}
|
|
|
|
this.yGrid = d3.svg.axis();
|
|
this.yGrid.scale(this.y).orient('left').ticks(5).tickSize(-this.width, 0, 0).tickFormat('');
|
|
if(this.axis.y.tickCount !== undefined) {
|
|
this.yGrid.ticks(this.axis.y.tickCount);
|
|
}
|
|
this.xAxisGroup = this.axis_group.append('g');
|
|
this.xAxisGroup.attr('class', 'x axis');
|
|
this.xAxisGroup.attr('transform', 'translate(0, '+this.height+')');
|
|
this.xAxisGroup.call(this.xAxis);
|
|
this.xAxisGroup.select('path').style('fill', 'none').style('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
this.xAxisGroup.selectAll('line').attr('fill', 'none').attr('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
|
|
if(this.master_detail) {
|
|
this.xAxisGroup2 = this.md_root.append('g');
|
|
this.xAxisGroup2.attr('class', 'x axis');
|
|
// this.xAxisGroup2.attr('transform', 'translate(0, '+ this.master_detail_height +')');
|
|
this.xAxisGroup2.call(this.xAxis2);
|
|
this.xAxisGroup2.select('path').style('fill', 'none');
|
|
this.xAxisGroup2.select('line').style('fill', 'none').style('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
this.xAxisGroup2.selectAll('line').attr('fill', 'none').attr('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
}
|
|
|
|
this.yAxisGroup = this.axis_group.append('g');
|
|
this.yAxisGroup.attr('class', 'y axis');
|
|
this.yAxisGroup.call(this.yAxis);
|
|
this.yAxisGroup.select('path').style('fill', 'none').style('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
this.yAxisGroup.selectAll('line').attr('fill', 'none').attr('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
|
|
if(this.opts.draw.grid) {
|
|
this.xGridGroup = this.grid_group.append('g');
|
|
this.xGridGroup.attr('class', 'x grid');
|
|
this.xGridGroup.attr('transform', 'translate(0,'+this.height+')');
|
|
this.xGridGroup.call(this.xGrid);
|
|
this.xGridGroup.selectAll('path').style('stroke-width', 0);
|
|
this.xGridGroup.selectAll('.tick').style('stroke', this.opts.theme_style_color).style('opacity', 0.7);
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.xGridGroup2 = this.md_root.select('.md-grid').append('g');
|
|
this.xGridGroup2.attr('class', 'x grid');
|
|
this.xGridGroup2.attr('transform', 'translate(0,'+this.master_detail_height+')');
|
|
this.xGridGroup2.call(this.xGrid2);
|
|
this.xGridGroup2.selectAll('path').style('stroke-width', 0);
|
|
this.xGridGroup2.selectAll('.tick').style('stroke', 'lightgray').style('opacity', 0.7);
|
|
}
|
|
|
|
if(this.opts.draw.grid) {
|
|
this.yGridGroup = this.grid_group.append('g');
|
|
this.yGridGroup.attr('class', 'y grid');
|
|
this.yGridGroup.call(this.yGrid);
|
|
this.yGridGroup.selectAll('path').style('stroke-width', 0);
|
|
this.yGridGroup.selectAll('.tick').style('stroke', this.opts.theme_style_color).style('opacity', 0.7);
|
|
}
|
|
|
|
this.resetAxis(animate);
|
|
|
|
if(this.master_detail) {
|
|
if(this.axis.x.type === 'ordinal') {
|
|
this.canvas.select('.noData').text('Master-Detail chart only for quantitative scales.');
|
|
throw new Error('Master-Detail chart only for quantitative scales.');
|
|
}
|
|
this.brush = d3.svg.brush();
|
|
this.brush.x(this.x2);
|
|
var self = this;
|
|
if(this.axis.x.type === 'datetime') {
|
|
this.brush.on('brushend', function() {
|
|
if(self.is_touch_device) {
|
|
var coordinates = d3.touches(this, self.d3_eventSource().changedTouches)[0];
|
|
} else {
|
|
var coordinates = d3.mouse(this);
|
|
}
|
|
var x = coordinates[0];
|
|
|
|
if(self.brush.empty()) {
|
|
var current_x0 = self.x2(+new Date(self.extent[0]));
|
|
var current_x1 = self.x2(+new Date(self.extent[1]));
|
|
|
|
var distance_x0_x = current_x0 - x;
|
|
|
|
var final_extent = [];
|
|
if(distance_x0_x < 0) {
|
|
var start = current_x0 + Math.abs(distance_x0_x);
|
|
var finish = current_x1 + Math.abs(distance_x0_x);
|
|
var max_extent = self.x2(+new Date(self.x2.domain()[1]));
|
|
if(finish > max_extent) {
|
|
start -= (finish - max_extent);
|
|
finish = max_extent;
|
|
final_extent = [
|
|
self.x2.invert(start),
|
|
self.x2.invert(finish)
|
|
];
|
|
} else {
|
|
var diff = Math.abs(start - finish);
|
|
final_extent = [
|
|
self.x2.invert(start - diff/2),
|
|
self.x2.invert(finish - diff/2)
|
|
];
|
|
}
|
|
} else {
|
|
var start = current_x0 - Math.abs(distance_x0_x);
|
|
var finish = current_x1 - Math.abs(distance_x0_x);
|
|
|
|
var diff = Math.abs(start - finish);
|
|
var final_start = start - diff/2;
|
|
var final_finish = finish - diff/2;
|
|
|
|
var min_extent = self.x2(+new Date(self.x2.domain()[0]));
|
|
|
|
if(final_start < min_extent) {
|
|
final_finish += (min_extent - final_start);
|
|
final_start = min_extent;
|
|
}
|
|
final_extent = [
|
|
self.x2.invert(final_start),
|
|
self.x2.invert(final_finish)
|
|
];
|
|
}
|
|
|
|
self.extent = final_extent;
|
|
self.brush.extent(self.extent);
|
|
self.brush_path.call(self.brush);
|
|
self._brush();
|
|
}
|
|
|
|
|
|
var brush_x_pos = self.brush_path.select('.extent').attr('x');
|
|
var brush_width = self.brush_path.select('.extent').attr('width');
|
|
var brush_height = self.brush_path.select('.extent').attr('height');
|
|
|
|
self.brush_path.select('.left-extent').attr('height', brush_height).attr('width', brush_x_pos).attr('x', 0);
|
|
var brush_r_x_pos = parseFloat(brush_x_pos)+parseFloat(brush_width);
|
|
self.brush_path.select('.right-extent').attr('height', brush_height).attr('x', brush_r_x_pos).attr('width', self.md_root.attr('width') - brush_r_x_pos);
|
|
self.brush_path.select('.left-border').attr('width', 1).attr('x', brush_x_pos);
|
|
self.brush_path.select('.left-top-border').attr('width', brush_x_pos);
|
|
self.brush_path.select('.right-border').attr('width', 1).attr('x', brush_r_x_pos);
|
|
self.brush_path.select('.right-top-border').attr('x', brush_r_x_pos).attr('width', self.md_root.attr('width') - brush_r_x_pos);
|
|
self.brush_path.select('.bottom-border').attr('x', brush_x_pos).attr('width', brush_width).attr('transform', 'translate(0, ' + (self.master_detail_height+5) + ')');
|
|
});
|
|
} else {
|
|
this.brush.on('brushend', function() {
|
|
if(self.is_touch_device) {
|
|
var coordinates = d3.touches(this, self.d3_eventSource().changedTouches)[0];
|
|
} else {
|
|
var coordinates = d3.mouse(this);
|
|
}
|
|
var x = self.x2.invert(coordinates[0]);
|
|
|
|
if(self.brush.empty()) {
|
|
var current_x0 = self.extent[0];
|
|
var current_x1 = self.extent[1];
|
|
|
|
var distance_x0_x = current_x0 - x;
|
|
|
|
var final_extent = [];
|
|
if(distance_x0_x < 0) {
|
|
var start = current_x0 + Math.abs(distance_x0_x);
|
|
var finish = current_x1 + Math.abs(distance_x0_x);
|
|
var max_extent = self.x2.domain()[1];
|
|
if(finish > max_extent) {
|
|
start -= (finish - max_extent);
|
|
finish = max_extent;
|
|
final_extent = [
|
|
start,
|
|
finish
|
|
];
|
|
} else {
|
|
var diff = Math.abs(start - finish);
|
|
final_extent = [
|
|
start - diff/2,
|
|
finish - diff/2
|
|
];
|
|
}
|
|
} else {
|
|
var start = current_x0 - Math.abs(distance_x0_x);
|
|
var finish = current_x1 - Math.abs(distance_x0_x);
|
|
|
|
var diff = Math.abs(start - finish);
|
|
var final_start = start - diff/2;
|
|
var final_finish = finish - diff/2;
|
|
|
|
var min_extent = self.x2.domain()[0];
|
|
|
|
if(final_start < min_extent) {
|
|
final_finish += (min_extent - final_start);
|
|
final_start = min_extent;
|
|
}
|
|
final_extent = [
|
|
final_start,
|
|
final_finish
|
|
];
|
|
}
|
|
|
|
self.extent = final_extent;
|
|
self.brush.extent(self.extent);
|
|
self.brush_path.call(self.brush);
|
|
self._brush();
|
|
}
|
|
|
|
var brush_x_pos = self.brush_path.select('.extent').attr('x');
|
|
var brush_width = self.brush_path.select('.extent').attr('width');
|
|
var brush_height = self.brush_path.select('.extent').attr('height');
|
|
|
|
self.brush_path.select('.left-extent').attr('height', brush_height).attr('width', brush_x_pos).attr('x', 0);
|
|
var brush_r_x_pos = parseFloat(brush_x_pos)+parseFloat(brush_width);
|
|
self.brush_path.select('.right-extent').attr('height', brush_height).attr('x', brush_r_x_pos).attr('width', self.md_root.attr('width') - brush_r_x_pos);
|
|
self.brush_path.select('.left-border').attr('width', 1).attr('x', brush_x_pos);
|
|
self.brush_path.select('.left-top-border').attr('width', brush_x_pos);
|
|
self.brush_path.select('.right-border').attr('width', 1).attr('x', brush_r_x_pos);
|
|
self.brush_path.select('.right-top-border').attr('x', brush_r_x_pos).attr('width', self.md_root.attr('width') - brush_r_x_pos);
|
|
self.brush_path.select('.bottom-border').attr('x', brush_x_pos).attr('width', brush_width).attr('transform', 'translate(0, ' + (self.master_detail_height+5) + ')');
|
|
});
|
|
}
|
|
this.brush.on('brush', function() {
|
|
var type = self.d3_eventSource().type;
|
|
if(type === 'mousemove' || type === 'touchmove') {
|
|
$(window).trigger('rubix.sidebar.off');
|
|
self._brush(true, 'root');
|
|
}
|
|
var brush_x_pos = self.brush_path.select('.extent').attr('x');
|
|
var brush_width = self.brush_path.select('.extent').attr('width');
|
|
var brush_height = self.brush_path.select('.extent').attr('height');
|
|
|
|
self.brush_path.select('.left-extent').attr('height', brush_height).attr('width', brush_x_pos).attr('x', 0);
|
|
var brush_r_x_pos = parseFloat(brush_x_pos)+parseFloat(brush_width);
|
|
self.brush_path.select('.right-extent').attr('height', brush_height).attr('x', brush_r_x_pos).attr('width', self.md_root.attr('width') - brush_r_x_pos);
|
|
self.brush_path.select('.left-border').attr('width', 1).attr('x', brush_x_pos);
|
|
self.brush_path.select('.left-top-border').attr('width', brush_x_pos);
|
|
self.brush_path.select('.right-border').attr('width', 1).attr('x', brush_r_x_pos);
|
|
self.brush_path.select('.right-top-border').attr('x', brush_r_x_pos).attr('width', self.md_root.attr('width') - brush_r_x_pos);
|
|
self.brush_path.select('.bottom-border').attr('x', brush_x_pos).attr('width', brush_width).attr('transform', 'translate(0, ' + (self.master_detail_height+5) + ')');
|
|
});
|
|
if(this.extent.length) {
|
|
this.brush.extent(this.extent);
|
|
if(!this.extent_coordinates.length) {
|
|
if(this.axis.x.type === 'datetime' || this.axis.y.type === 'datetime') {
|
|
this.extent_coordinates = [
|
|
this.x2(+new Date(this.extent[0])),
|
|
this.x2(+new Date(this.extent[1]))
|
|
];
|
|
} else {
|
|
this.extent_coordinates = [
|
|
this.x2(this.extent[0]),
|
|
this.x2(this.extent[1])
|
|
];
|
|
}
|
|
}
|
|
this._brush();
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.prototype._setupSeries = function() {
|
|
this.grid_group = this.root.append('g').attr('class', 'grid_group');
|
|
this.axis_group = this.root.append('g').attr('class', 'axis_group');
|
|
|
|
if(this.opts.hideGrid) {
|
|
this.grid_group.style('display', 'none');
|
|
}
|
|
if(this.opts.hideAxisAndGrid) {
|
|
this.grid_group.style('display', 'none');
|
|
this.axis_group.style('display', 'none');
|
|
}
|
|
|
|
this.root_cb_series = this.root.append('g').attr('class', 'cb_series');
|
|
this.root_stacked_area_series = this.root.append('g').attr('class', 'stacked_area_series');
|
|
this.root_area_series = this.root.append('g').attr('class', 'area_series');
|
|
this.root_line_series = this.root.append('g').attr('class', 'line_series');
|
|
|
|
this.focus_line_group = this.root.append('g').attr('class', 'focus_line_group');
|
|
|
|
this.symbols_group = this.root.append('g').attr('class', 'symbols');
|
|
this.focus_group = this.root.append('g').attr('class', 'focus_group');
|
|
|
|
if(this.master_detail) {
|
|
this.md_root_stacked_area_series = this.md_layers.append('g').attr('class', 'md_stacked_area_series');
|
|
this.md_root_area_series = this.md_layers.append('g').attr('class', 'md_area_series');
|
|
this.md_root_line_series = this.md_layers.append('g').attr('class', 'md_line_series');
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.resetExtent = function() {
|
|
var curr_extent = this.brush.extent();
|
|
if(curr_extent.length) {
|
|
var start = this.extent_coordinates[0];
|
|
var finish = this.extent_coordinates[1];
|
|
if(this.axis.x.type === 'datetime' || this.axis.y.type === 'datetime') {
|
|
this.extent = [this.x2.invert(+new Date(start)), this.x2.invert(+new Date(finish))];
|
|
} else {
|
|
this.extent = [this.x2.invert(start), this.x2.invert(finish)];
|
|
}
|
|
this.brush.extent(this.extent);
|
|
this._brush_nochange();
|
|
}
|
|
};
|
|
|
|
Rubix.prototype._brush_nochange = function() {
|
|
try {
|
|
this.x.domain(this.extent);
|
|
this.callAxis();
|
|
for(var i in this.charts) {
|
|
this.charts[i].noRedraw(this);
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
Rubix.prototype._brush = function(record, type) {
|
|
var extent = this.brush.empty() ? this.x2.domain() : this.brush.extent();
|
|
if(this.axis.x.type === 'datetime' || this.axis.y.type === 'datetime') {
|
|
this.extent = extent;
|
|
if(record && type === 'root') {
|
|
this.extent_coordinates = [
|
|
this.x2(+new Date(this.extent[0])),
|
|
this.x2(+new Date(this.extent[1]))
|
|
];
|
|
}
|
|
} else {
|
|
this.extent = extent;
|
|
if(record && type === 'root') {
|
|
this.extent_coordinates = [
|
|
this.x2(this.extent[0]),
|
|
this.x2(this.extent[1])
|
|
];
|
|
}
|
|
}
|
|
this._brush_nochange();
|
|
};
|
|
|
|
Rubix.prototype._setupOrdinalAxis = function(forced) {
|
|
var data = {}, o_data = {};
|
|
var _data = [], _data1 = [], extentX, extentY, rdata;
|
|
this.crosshair_data = [], _o_data = [];
|
|
var temp = {}, others = {};
|
|
if(this.axis.x.type === 'ordinal' || this.axis.y.type === 'ordinal') {
|
|
for(var series in this.data) {
|
|
rdata = this.data[series];
|
|
var chart = this.charts[series];
|
|
for(var i=0; i<rdata.length; i++) {
|
|
var x = rdata[i].x;
|
|
var y0 = 0;
|
|
if(this.stacked) {
|
|
if(rdata[i].hasOwnProperty('y0')) {
|
|
var y0 = rdata[i].y0;
|
|
}
|
|
if(rdata[i].hasOwnProperty('y_new')) {
|
|
if(this.grouped && chart.type === 'column_series') {
|
|
var y = rdata[i].y_new;
|
|
} else {
|
|
var y = rdata[i].y_new + y0;
|
|
}
|
|
} else {
|
|
var y = rdata[i].y;
|
|
}
|
|
} else {
|
|
var y = rdata[i].y;
|
|
}
|
|
_data1.push(y);
|
|
if(!data.hasOwnProperty(x)) {
|
|
data[x] = [];
|
|
}
|
|
data[x].push(y);
|
|
if(forced) {
|
|
if(!o_data.hasOwnProperty(y)) {
|
|
o_data[y] = [];
|
|
}
|
|
o_data[y].push(x);
|
|
}
|
|
|
|
if(!temp.hasOwnProperty(x)) {
|
|
temp[x] = {};
|
|
}
|
|
temp[x][series] = y;
|
|
if(!others.hasOwnProperty(x)) {
|
|
others[x] = {};
|
|
}
|
|
others[x][series] = {
|
|
y0: y0
|
|
};
|
|
}
|
|
}
|
|
|
|
for(var point in data) {
|
|
_data.push(point);
|
|
}
|
|
if(forced) {
|
|
for(var point in o_data) {
|
|
_o_data.push(point);
|
|
}
|
|
}
|
|
|
|
if(this.stacked) {
|
|
var _minY = d3.min(_data1, function(d) {
|
|
if(isNaN(d)) {
|
|
return 0;
|
|
}
|
|
return d;
|
|
});
|
|
|
|
_minY = _minY < 0 ? _minY : 0;
|
|
|
|
var extentY = [_minY, d3.max(_data1, function(d) {
|
|
if(isNaN(d)) {
|
|
return 0;
|
|
}
|
|
return d;
|
|
})];
|
|
} else {
|
|
var extentY = d3.extent(_data1);
|
|
}
|
|
var yMin = extentY[0];
|
|
var yMax = extentY[1];
|
|
|
|
if(this.opts.invertAxes) {
|
|
if(this.axis.y.tickFormat.length) {
|
|
for(var i=0; i<_data.length; i++) {
|
|
_data[i] = +_data[i];
|
|
}
|
|
_data.sort(function(a, b) {
|
|
if(a > b) {
|
|
return 1;
|
|
} else if(a === b) {
|
|
return 0
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
if(forced) {
|
|
if(this.axis.x.tickFormat.length) {
|
|
for(var i=0; i<_o_data.length; i++) {
|
|
_o_data[i] = +_o_data[i];
|
|
}
|
|
_o_data.sort(function(a, b) {
|
|
if(a > b) {
|
|
return 1;
|
|
} else if(a === b) {
|
|
return 0
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.x.domain(_o_data);
|
|
this.y.domain(_data.reverse());
|
|
|
|
if(this.master_detail) {
|
|
this.x2.domain(_o_data);
|
|
this.y2.domain(_data);
|
|
}
|
|
} else {
|
|
this.x.domain([yMin, yMax]);
|
|
this.y.domain(_data.reverse());
|
|
|
|
if(this.master_detail) {
|
|
this.x2.domain([yMin, yMax]);
|
|
this.y2.domain(_data);
|
|
}
|
|
}
|
|
} else {
|
|
if(this.axis.x.tickFormat.length) {
|
|
for(var i=0; i<_data.length; i++) {
|
|
_data[i] = +_data[i];
|
|
}
|
|
_data.sort(function(a, b) {
|
|
if(a > b) {
|
|
return 1;
|
|
} else if(a === b) {
|
|
return 0
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
if(forced) {
|
|
if(this.axis.y.tickFormat.length) {
|
|
for(var i=0; i<_o_data.length; i++) {
|
|
_o_data[i] = +_o_data[i];
|
|
}
|
|
_o_data.sort(function(a, b) {
|
|
if(a > b) {
|
|
return 1;
|
|
} else if(a === b) {
|
|
return 0
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.x.domain(_data);
|
|
this.y.domain(_o_data);
|
|
|
|
if(this.master_detail) {
|
|
this.x2.domain(_data);
|
|
this.y2.domain(_o_data);
|
|
}
|
|
} else {
|
|
this.x.domain(_data);
|
|
this.y.domain([yMin, yMax]);
|
|
|
|
if(this.master_detail) {
|
|
this.x2.domain(_data);
|
|
this.y2.domain([yMin, yMax]);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(var x in temp) {
|
|
if(this.opts.invertAxes) {
|
|
var _x = this.y(x);
|
|
this.crosshair_data.push({
|
|
x: +_x,
|
|
y: temp[x],
|
|
others: others[x]
|
|
});
|
|
} else {
|
|
var _x = this.x(x);
|
|
this.crosshair_data.push({
|
|
x: +_x,
|
|
y: temp[x],
|
|
others: others[x]
|
|
});
|
|
}
|
|
}
|
|
|
|
this.crosshair_data.sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
Rubix.prototype._setupLinearAxis = function() {
|
|
var xMin=null, xMax=null, yMin=null, yMax=null;
|
|
var data, extentX, extentY;
|
|
this.crosshair_data = [];
|
|
var temp = {}, others = {};
|
|
for(var series in this.data) {
|
|
data = this.data[series];
|
|
extentX = d3.extent(data, function(d) {return d.x});
|
|
if(this.stacked) {
|
|
var _minY = d3.min(data, function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
if(d.y) return d.y;
|
|
return 0;
|
|
}
|
|
return val;
|
|
});
|
|
|
|
_minY = _minY < 0 ? _minY : 0;
|
|
|
|
extentY = [_minY, d3.max(data, function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
if(d.y) return d.y;
|
|
return 0;
|
|
}
|
|
return val;
|
|
})];
|
|
} else {
|
|
extentY = d3.extent(data, function(d) {return d.y;});
|
|
}
|
|
|
|
if(xMin === null) {
|
|
xMin = extentX[0];
|
|
xMax = extentX[1];
|
|
} else {
|
|
if(xMin >= extentX[0]) xMin = extentX[0];
|
|
if(xMax <= extentX[1]) xMax = extentX[1];
|
|
}
|
|
if(yMin === null) {
|
|
yMin = extentY[0];
|
|
yMax = extentY[1];
|
|
} else {
|
|
if(yMin >= extentY[0]) yMin = extentY[0];
|
|
if(yMax <= extentY[1]) yMax = extentY[1];
|
|
}
|
|
|
|
for(var i=0; i<data.length; i++) {
|
|
var x = data[i].x;
|
|
var y0 = 0;
|
|
if(this.stacked) {
|
|
if(data[i].hasOwnProperty('y0')) {
|
|
var y0 = data[i].y0;
|
|
}
|
|
if(data[i].hasOwnProperty('y_new')) {
|
|
var y = data[i].y_new + y0;
|
|
} else {
|
|
var y = data[i].y;
|
|
}
|
|
} else {
|
|
var y = data[i].y;
|
|
}
|
|
|
|
if(!temp.hasOwnProperty(x)) {
|
|
temp[x] = {};
|
|
}
|
|
temp[x][series] = y;
|
|
if(!others.hasOwnProperty(x)) {
|
|
others[x] = {};
|
|
}
|
|
others[x][series] = {
|
|
y0: y0
|
|
};
|
|
}
|
|
}
|
|
|
|
if(this.opts.invertAxes) {
|
|
this.x.domain([yMin, yMax]);
|
|
this.y.domain([xMax, xMin]);
|
|
if(this.master_detail) {
|
|
this.x2.domain([yMin, yMax]);
|
|
this.y2.domain([xMax, xMin]);
|
|
}
|
|
} else {
|
|
this.x.domain([xMin, xMax]);
|
|
this.y.domain([yMin, yMax]);
|
|
if(this.master_detail) {
|
|
this.x2.domain([xMin, xMax]);
|
|
this.y2.domain([yMin, yMax]);
|
|
}
|
|
}
|
|
|
|
for(var x in temp) {
|
|
this.crosshair_data.push({
|
|
x: +x,
|
|
y: temp[x],
|
|
others: others[x]
|
|
});
|
|
}
|
|
|
|
this.crosshair_data.sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
};
|
|
|
|
Rubix.prototype.resetAxis = function(animate) {
|
|
try {
|
|
if(this.axis.x.type === 'linear' && this.axis.y.type === 'linear') {
|
|
this._setupLinearAxis();
|
|
} else if(
|
|
(this.axis.x.type === 'datetime' && this.axis.y.type === 'linear')
|
|
|| (this.axis.x.type === 'linear' && this.axis.y.type === 'datetime')
|
|
) {
|
|
this._setupLinearAxis();
|
|
} else if(this.axis.x.type === 'ordinal' && this.axis.y.type === 'ordinal') {
|
|
this._setupOrdinalAxis(true);
|
|
} else if(this.axis.x.type === 'ordinal' || this.axis.y.type === 'ordinal') {
|
|
this._setupOrdinalAxis();
|
|
}
|
|
|
|
this.callAxis(animate);
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.callAxis = function(animate) {
|
|
if(animate) {
|
|
var t = this.root.transition().duration(this.animationSpeed);
|
|
if(this.master_detail) {
|
|
var z = this.md_root.transition().duration(this.animationSpeed);
|
|
}
|
|
} else {
|
|
var t = this.root;
|
|
if(this.master_detail) {
|
|
var z = this.md_root;
|
|
}
|
|
}
|
|
|
|
if(this.hasData) {
|
|
if(this.axis.x.type === 'ordinal') {
|
|
var domain = this.x.domain();
|
|
if((this.axis.x.tickCount !== undefined) && (domain.length > this.axis.x.tickCount)) {
|
|
var self = this;
|
|
var feed = d3.range(domain.length).map(function(d) {
|
|
if(d % self.axis.x.tickCount === 0)
|
|
return domain[d];
|
|
return null;
|
|
});
|
|
|
|
var cleanedFeed = [];
|
|
for(var i=0; i<feed.length; i++) {
|
|
if(feed[i] === null) continue;
|
|
cleanedFeed.push(feed[i]);
|
|
}
|
|
this.xAxis.tickValues(cleanedFeed);
|
|
}
|
|
} else {
|
|
if(this.axis.x.tickCount !== undefined) {
|
|
this.xAxis.ticks(this.axis.x.tickCount);
|
|
|
|
if(this.master_detail) {
|
|
this.xAxis2.ticks(this.axis.x.tickCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(this.axis.y.type === 'ordinal' || this.axis.x.type === 'linear' || this.axis.x.type === 'datetime') {
|
|
var domain = this.y.domain();
|
|
if((this.axis.y.tickCount !== undefined) && (domain.length > this.axis.y.tickCount)) {
|
|
var self = this;
|
|
var feed = d3.range(domain.length).map(function(d) {
|
|
if(d % self.axis.y.tickCount === 0) {
|
|
return domain[d];
|
|
}
|
|
return '';
|
|
});
|
|
|
|
this.yAxis.tickValues(feed);
|
|
}
|
|
} else {
|
|
if(this.axis.y.tickCount !== undefined) {
|
|
this.yAxis.ticks(this.axis.y.tickCount);
|
|
}
|
|
}
|
|
|
|
this.canvas.select('.noData').style('display', 'none');
|
|
try {
|
|
t.selectAll('.x.axis').style('display', null).call(this.xAxis);
|
|
t.selectAll('.y.axis').style('display', null).call(this.yAxis);
|
|
t.selectAll('.x.grid').style('display', null).call(this.xGrid);
|
|
t.selectAll('.y.grid').style('display', null).call(this.yGrid);
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
this.xAxisGroup.selectAll('line').attr('fill', 'none').attr('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
this.yAxisGroup.selectAll('line').attr('fill', 'none').attr('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
|
|
if(this.opts.draw.grid) {
|
|
this.xGridGroup.selectAll('path').style('stroke-width', 0);
|
|
this.xGridGroup.selectAll('.tick').style('stroke', this.opts.theme_style_color).style('opacity', 0.7);
|
|
this.yGridGroup.selectAll('path').style('stroke-width', 0);
|
|
this.yGridGroup.selectAll('.tick').style('stroke', this.opts.theme_style_color).style('opacity', 0.7);
|
|
}
|
|
|
|
this.xAxisGroup.selectAll('text').style('font', (this.fontSize+1)+'px Lato, "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif').style('direction', 'ltr').style('fill', this.opts.tickColor).style('color', this.opts.tickColor).style('font-style', '').style('font-variant', '').style('font-weight', '').style('line-height', '');
|
|
this.yAxisGroup.selectAll('text').style('font', (this.fontSize+1)+'px Lato, "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif').style('direction', 'ltr').style('fill', this.opts.tickColor).style('color', this.opts.tickColor).style('font-style', '').style('font-variant', '').style('font-weight', '').style('line-height', '');
|
|
if(this.opts.theme_style === 'dark') {
|
|
this.xAxisGroup.selectAll('text').style('font-weight', 'bold');
|
|
this.yAxisGroup.selectAll('text').style('font-weight', 'bold');
|
|
}
|
|
} else {
|
|
this.canvas.select('.noData').style('display', null);
|
|
t.selectAll('.x.axis').style('display', 'none');
|
|
t.selectAll('.y.axis').style('display', 'none');
|
|
t.selectAll('.x.grid').style('display', 'none');
|
|
t.selectAll('.y.grid').style('display', 'none');
|
|
}
|
|
if(this.master_detail) {
|
|
if(this.hasData) {
|
|
this.canvas.select('.noData').style('display', 'none');
|
|
z.selectAll('.x.axis').style('display', null).call(this.xAxis2);
|
|
z.selectAll('.x.grid').style('display', null).call(this.xGrid2);
|
|
if(this.xAxisGroup2) {
|
|
this.xAxisGroup2.selectAll('line').attr('fill', 'none').attr('stroke', this.opts.theme_style_color).style('shape-rendering', 'crispEdges');
|
|
this.xGridGroup2.selectAll('path').style('stroke-width', 0);
|
|
this.xGridGroup2.selectAll('.tick').style('stroke', this.opts.theme_style_color).style('opacity', 0.7);
|
|
this.xAxisGroup2.selectAll('text').style('font', (this.fontSize+1)+'px Lato, "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif').style('direction', 'ltr').style('fill', this.opts.tickColor).style('color', this.opts.tickColor).style('font-style', '').style('font-variant', '').style('font-weight', '').style('line-height', '');
|
|
if(this.opts.theme_style === 'dark') {
|
|
this.xAxisGroup2.selectAll('text').style('font-weight', 'bold');
|
|
}
|
|
}
|
|
if(this.yAxisGroup2) {
|
|
this.yAxisGroup2.selectAll('text').style('font', (this.fontSize+1)+'px Lato, "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif').style('direction', 'ltr').style('fill', this.opts.tickColor).style('color', this.opts.tickColor).style('font-style', '').style('font-variant', '').style('font-weight', '').style('line-height', '');
|
|
if(this.opts.theme_style === 'dark') {
|
|
this.yAxisGroup.selectAll('text').style('font-weight', 'bold');
|
|
}
|
|
}
|
|
} else {
|
|
this.canvas.select('.noData').style('display', null);
|
|
z.selectAll('.x.axis').style('display', 'none');
|
|
z.selectAll('.y.axis').style('display', 'none');
|
|
z.selectAll('.x.grid').style('display', 'none');
|
|
z.selectAll('.y.grid').style('display', 'none');
|
|
}
|
|
}
|
|
|
|
|
|
if(this.opts.hideYAxis) {
|
|
this.yAxisGroup.style('display', 'none');
|
|
}
|
|
if(this.opts.hideXAxis) {
|
|
this.xAxisGroup.style('display', 'none');
|
|
}
|
|
if(this.opts.hideXAxisTickLines) {
|
|
this.xGridGroup.style('display', 'none');
|
|
}
|
|
if(this.opts.hideYAxisTickLines) {
|
|
this.yGridGroup.style('display', 'none');
|
|
}
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.prototype._setupRedraw = function() {
|
|
var self = this;
|
|
|
|
$(window).on('orientationchange.'+this.master_id, function() {
|
|
self.draw();
|
|
});
|
|
|
|
$(window).on('rubix.redraw.'+this.master_id, function() {
|
|
self.draw();
|
|
});
|
|
|
|
switch(this.resize) {
|
|
case 'always':
|
|
$(window).on('resize.rubix.'+this.master_id, function() {
|
|
self.draw();
|
|
});
|
|
break;
|
|
case 'debounced':
|
|
$(window).on('debouncedresize.rubix.'+this.master_id, function() {
|
|
self.draw();
|
|
});
|
|
break;
|
|
case 'throttled':
|
|
$(window).on('throttledresize.rubix.'+this.master_id, function() {
|
|
self.draw();
|
|
});
|
|
break;
|
|
default:
|
|
throw new Error('Unknown resize type!');
|
|
break;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
Rubix.prototype._draw = function() {
|
|
this._cleanElement();
|
|
this._setSize();
|
|
this._setupCanvas();
|
|
this._setupRoot();
|
|
this._setupSeries();
|
|
this._setupAxis();
|
|
|
|
for(var i in this.charts) {
|
|
this.charts[i].redraw(this);
|
|
}
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.prototype._cleanElement = function() {
|
|
this.elem.children().remove();
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.prototype._setSize = function() {
|
|
this.outerWidth = this.elem.width();
|
|
this.outerHeight = this.elem.height();
|
|
|
|
if(this.opts.hideYAxis) {
|
|
this.margin.left = 0;
|
|
this.margin.right = 0;
|
|
}
|
|
|
|
if(this.opts.hideAxisAndGrid) {
|
|
this.margin.left = 0;
|
|
this.margin.right = 0;
|
|
}
|
|
|
|
this.width = this.outerWidth - this.margin.left - this.margin.right;
|
|
this.height = this.outerHeight - this.margin.top - this.margin.bottom;
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.prototype._setupCanvas = function() {
|
|
this.elem.html('');
|
|
this.canvas = d3.select(this.elem.get(0)).append('svg');
|
|
this.canvas.attr('width', this.outerWidth);
|
|
this.canvas.attr('height', this.outerHeight);
|
|
|
|
this.canvas.append('desc').text("Powered by Rubix");
|
|
|
|
this.defs = this.canvas.append('defs');
|
|
|
|
this.defs.append('clipPath')
|
|
.attr('id', this.master_id+'-clip')
|
|
.append('rect')
|
|
.attr('width', this.width)
|
|
.attr('height', this.height+20)
|
|
.attr('transform', 'translate(0, -20)');
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.prototype._setupRoot = function() {
|
|
this.root = this.canvas.append('g');
|
|
this.root.attr('width', this.width);
|
|
this.root.attr('height', this.height);
|
|
if(!this.opts.hideAxisAndGrid) {
|
|
this.root.attr('transform', 'translate('+this.margin.left+','+this.margin.top+')');
|
|
} else {
|
|
this.root.attr('transform', 'translate(0,'+this.margin.top+')');
|
|
}
|
|
|
|
if(!this.hasData) {
|
|
this.canvas.append('text').attr('class', 'noData').attr('font-size', '30px').attr('text-anchor', 'middle').attr('transform', 'translate('+this.outerWidth/2+','+this.outerHeight/2+')').attr('font-family', 'Lato, "Lucida Grande", Arial, Helvetica, sans-serif').text("No Data");
|
|
} else {
|
|
this.canvas.select('.noData').style('display', 'none');
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.md_root = this.canvas.append('g');
|
|
this.md_root.attr('width', this.width);
|
|
this.md_root.attr('height', this.master_detail_height);
|
|
if(!this.opts.hideAxisAndGrid) {
|
|
this.md_root.attr('transform', 'translate('+this.margin.left+','+(this.margin.top+this.height+this.master_detail_margin_bottom)+')');
|
|
}
|
|
var rect = this.md_root.append('rect');
|
|
rect.attr('width', this.md_root.attr('width'));
|
|
rect.attr('height', this.md_root.attr('height'));
|
|
rect.attr('fill', 'none');
|
|
rect.attr('shape-rendering', 'crispEdges');
|
|
this.md_root.append('g').attr('class', 'md-grid').attr('width', this.md_root.attr('width'));
|
|
this.md_layers = this.md_root.append('g').attr('class', 'md-layers').attr('clip-path', 'url(#'+this.master_id+'-clip)');
|
|
|
|
this.defs.append('clipPath')
|
|
.attr('id', this.master_id+'-clip-symbols')
|
|
.append('rect')
|
|
.attr('width', this.md_root.attr('width'))
|
|
.attr('height', this.height+20)
|
|
.attr('transform', 'translate(0, -10)');
|
|
|
|
this.defs.append('clipPath')
|
|
.attr('id', this.master_id+'-clip-brush')
|
|
.append('rect')
|
|
.attr('width', this.md_root.attr('width')+20)
|
|
.attr('height', this.master_detail_height)
|
|
.attr('transform', 'translate(-10, 1)');
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.marginLeft = function(_left_) {
|
|
if(!arguments.length) return this.margin.left;
|
|
|
|
if(10 + _left_ + 10 < this.margin.defaultLeft) {
|
|
this.margin.left = this.margin.defaultLeft;
|
|
} else {
|
|
this.margin.left = 10 + _left_ + 10;
|
|
}
|
|
if(this.axis.x.label.length) {
|
|
if(this.opts.invertAxes) {
|
|
this.margin.left += 15;
|
|
}
|
|
}
|
|
if(this.axis.y.label.length) {
|
|
if(!this.opts.invertAxes) {
|
|
this.margin.left += 15;
|
|
}
|
|
}
|
|
this.draw();
|
|
};
|
|
|
|
Rubix.prototype.forceRedraw = function() {
|
|
try {
|
|
this.hasData = false;
|
|
for(var i in this.charts) {
|
|
this.hasData = true;
|
|
this.charts[i].forceRedraw(this);
|
|
}
|
|
if(this.hasData) {
|
|
this.canvas.select('.noData').style('display', 'none');
|
|
this.root.selectAll('.axis').style('display', null);
|
|
} else {
|
|
this.canvas.select('.noData').style('display', null);
|
|
this.root.selectAll('.axis').style('display', 'none');
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
// flicked from crossfilter
|
|
Rubix.prototype.resizePath = function(d) {
|
|
var e = +(d == 'e'),
|
|
x = e ? 1 : -1,
|
|
y = this.master_detail_height/2;
|
|
return 'M' + (0.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);
|
|
};
|
|
|
|
Rubix.prototype._createBrushPath = function() {
|
|
var self = this;
|
|
if(this.brush_path) this.brush_path.remove();
|
|
this.brush_path = this.md_root.append('g');
|
|
this.brush_path.attr('height', this.master_detail_height+5);
|
|
this.brush_path.attr('width', this.md_root.attr('width'));
|
|
this.brush_path.attr('class', 'x brush');
|
|
this.brush_path.attr('transform', 'translate(0, -4)');
|
|
// this.brush_path.attr('clip-path', 'url(#'+this.master_id+'-clip-brush)');
|
|
this.brush_path.call(this.brush);
|
|
|
|
var color = 'rgb(16, 16, 16)';
|
|
if(this.opts.theme_style === 'light') {
|
|
color = 'white';
|
|
}
|
|
|
|
this.brush_path.selectAll('.extent').attr('stroke', 'none').attr('fill-opacity', 0).attr('shape-rendering', 'crispEdges');
|
|
this.brush_path.insert('rect', '.resize').attr('class', 'left-extent').attr('fill-opacity', 0.5).attr('fill', color).style('cursor', 'crosshair');
|
|
this.brush_path.insert('rect', '.resize').attr('class', 'right-extent').attr('fill-opacity', 0.5).attr('fill', color).style('cursor', 'crosshair');
|
|
this.brush_path.insert('rect', '.resize').attr('class', 'left-border').attr('x', 0).style('fill', this.opts.theme_style_color);
|
|
this.brush_path.insert('rect', '.resize').attr('class', 'left-top-border').attr('x', 0).style('fill', this.opts.theme_style_color);
|
|
this.brush_path.insert('rect', '.resize').attr('class', 'right-border').style('fill', this.opts.theme_style_color);
|
|
this.brush_path.insert('rect', '.resize').attr('class', 'right-top-border').style('fill', this.opts.theme_style_color);
|
|
this.brush_path.insert('rect', '.resize').attr('class', 'bottom-border').style('fill', 'rgb(16, 16, 16)').attr('fill-opacity', 0.3);
|
|
|
|
var brush_x_pos = this.brush_path.select('.extent').attr('x');
|
|
var brush_width = this.brush_path.select('.extent').attr('width');
|
|
var brush_height = this.brush_path.select('.extent').attr('height');
|
|
|
|
this.brush_path.select('.left-extent').attr('height', brush_height).attr('width', brush_x_pos).attr('x', 0);
|
|
var brush_r_x_pos = parseFloat(brush_x_pos)+parseFloat(brush_width);
|
|
this.brush_path.select('.right-extent').attr('height', brush_height).attr('x', brush_r_x_pos).attr('width', this.md_root.attr('width') - brush_r_x_pos);
|
|
|
|
this.brush_path.select('.left-border').attr('width', 1).attr('x', brush_x_pos);
|
|
this.brush_path.select('.left-top-border').attr('width', brush_x_pos);
|
|
this.brush_path.select('.right-border').attr('width', 1).attr('x', brush_r_x_pos);
|
|
this.brush_path.select('.right-top-border').attr('x', brush_r_x_pos).attr('width', this.md_root.attr('width') - brush_r_x_pos);
|
|
this.brush_path.select('.bottom-border').attr('x', brush_x_pos).attr('width', brush_width).attr('transform', 'translate(0, ' + (this.master_detail_height+5) + ')');
|
|
|
|
this.brush_path.selectAll('.resize').append('path').attr('d', function(d) {
|
|
return self.resizePath(d);
|
|
}).style('fill', '#EEE').style('stroke', '#666').attr('transform', 'translate(0, -'+this.master_detail_height/5+')');
|
|
|
|
var inner_rect = this.brush_path.selectAll('rect');
|
|
inner_rect.attr('height', this.master_detail_height+5);
|
|
|
|
this.brush_path.select('.left-top-border').attr('height', 1);
|
|
this.brush_path.select('.right-top-border').attr('height', 1);
|
|
this.brush_path.select('.bottom-border').attr('height', 1);
|
|
|
|
if(!this.extent.length) {
|
|
this.extent = this.x2.domain();
|
|
this.brush.extent(this.extent);
|
|
this._brush();
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.bisectLeft = d3.bisector(function(d) { return d.x; }).left;
|
|
Rubix.prototype.bisectRight = d3.bisector(function(d) { return d.x; }).right;
|
|
|
|
Rubix.prototype.move_tooltip_x = function(dx, ys, points) {
|
|
var dy, mid;
|
|
if(ys.length) {
|
|
dy = d3.mean(ys)/ys.length;
|
|
}
|
|
|
|
var copy = dx;
|
|
if(this.axis.x.range === 'column' || this.axis.x.range === 'bar') {
|
|
dx += this.x.rangeBand()/2;
|
|
}
|
|
|
|
var _elem = this.root_elem;
|
|
|
|
var tooltipPadding = (this.tooltip.outerWidth() - this.tooltip.width())/2;
|
|
|
|
var left = dx + this.margin.left + tooltipPadding;
|
|
var top = dy;
|
|
|
|
var elem_far_right = _elem.width();
|
|
|
|
var tooltip_far_right = left + this.tooltip.outerWidth();
|
|
|
|
if(this.axis.x.range === 'column' || this.axis.x.range === 'bar') {
|
|
dx = copy - this.x.rangeBand()/2;
|
|
}
|
|
|
|
if(tooltip_far_right > elem_far_right) {
|
|
left -= (tooltip_far_right - dx - this.margin.left + tooltipPadding);
|
|
}
|
|
|
|
this.tooltip.css('display', 'inline');
|
|
this.tooltip.css('display', useTable);
|
|
this.tooltip.css({
|
|
'left': left,
|
|
'top' : top
|
|
});
|
|
|
|
var html = "", formatterX, formatterY;
|
|
if(this.tooltipFormatter.format.x.length) {
|
|
if(this.axis.x.type === 'datetime') {
|
|
formatterX = d3.time.format(this.tooltipFormatter.format.x);
|
|
} else {
|
|
formatterX = d3.format(this.tooltipFormatter.format.x);
|
|
}
|
|
} else {
|
|
formatterX = function(d) { return d; };
|
|
}
|
|
if(this.tooltipFormatter.format.y.length) {
|
|
if(this.axis.y.type === 'datetime') {
|
|
formatterY = d3.time.format(this.tooltipFormatter.format.y);
|
|
} else {
|
|
formatterY = d3.format(this.tooltipFormatter.format.y);
|
|
}
|
|
} else {
|
|
formatterY = function(d) { return d; };
|
|
}
|
|
for(var name in points) {
|
|
var _x, _y;
|
|
if(this.axis.x.type === 'datetime') {
|
|
_x = formatterX(new Date(points[name].x));
|
|
} else {
|
|
if(points[name].invert) {
|
|
_x = formatterX(this.x.invert(points[name].x));
|
|
} else {
|
|
_x = formatterX(points[name].x);
|
|
}
|
|
}
|
|
if(this.axis.y.type === 'datetime') {
|
|
_y = (points[name].y !== null) ? formatterY(new Date(points[name].y)) : null;
|
|
} else {
|
|
if(points[name].invert) {
|
|
_y = (points[name].y !== null) ? formatterY(this.y.invert(points[name].y)) : null;
|
|
} else {
|
|
_y = formatterY(points[name].y);
|
|
}
|
|
}
|
|
|
|
_x = this.tooltipFormatter.abs.x ? Math.abs(_x) : _x;
|
|
_y = this.tooltipFormatter.abs.y ? Math.abs(_y) : _y;
|
|
var series = "<div style='color: "+points[name].opts.color+"; margin-bottom: 2px; line-height: 22px;'><b style='position:relative; top: -5px; left: -2px;'><span style='font-size: 22px;'>■ </span><span style='position:relative; top: -3px; left: -2px;'>"+name+"</span></b></div>";
|
|
var x = "<div style='font-size: 10px; margin-top: -10px;'>x : " + _x + " </div>";
|
|
var y = "<div style='font-size: 10px; margin-top: -5px;'>y : " + _y + " </div><br>";
|
|
html = (series+x+y) + html;
|
|
}
|
|
|
|
html = html.slice(0, html.length-4);
|
|
|
|
this.tooltip.html(html);
|
|
|
|
var tooltipHeight = this.tooltip.outerHeight();
|
|
var total_height = tooltipHeight + dy;
|
|
if(total_height >= _elem.height()) {
|
|
top = this.margin.top;
|
|
this.tooltip.css('top', top);
|
|
}
|
|
};
|
|
|
|
|
|
Rubix.prototype.move_tooltip_y = function(dy, yx, points) {
|
|
try {
|
|
var dx, mid;
|
|
if(yx.length) {
|
|
dx = d3.max(yx);
|
|
}
|
|
|
|
var copy = dx;
|
|
if(this.axis.x.range === 'column' || this.axis.x.range === 'bar') {
|
|
dx += this.y.rangeBand()/2;
|
|
}
|
|
|
|
var _elem = $(this.canvas.node());
|
|
|
|
var tooltipPadding = (this.tooltip.outerWidth() - this.tooltip.width())/2;
|
|
|
|
var left = dx + this.margin.left + tooltipPadding;
|
|
var top = dy;
|
|
|
|
var elem_far_right = _elem.width();
|
|
|
|
var tooltip_far_right = left + this.tooltip.outerWidth();
|
|
|
|
if(this.axis.x.range === 'column' || this.axis.x.range === 'bar') {
|
|
dx = copy - this.y.rangeBand()/2;
|
|
}
|
|
|
|
if(tooltip_far_right > elem_far_right) {
|
|
left -= tooltip_far_right - dx - this.margin.left + tooltipPadding;
|
|
}
|
|
|
|
var elem_far_bottom = _elem.height();
|
|
var tooltip_far_bottom = top + this.tooltip.outerHeight() + this.margin.bottom;
|
|
|
|
if(tooltip_far_bottom > elem_far_bottom) {
|
|
top -= (tooltip_far_bottom - elem_far_bottom);
|
|
}
|
|
|
|
this.tooltip.css('display', 'inline');
|
|
this.tooltip.css('display', useTable);
|
|
this.tooltip.css({
|
|
'left': left,
|
|
'top' : top
|
|
});
|
|
|
|
|
|
var html = "", formatterX, formatterY;
|
|
if(this.tooltipFormatter.format.x.length) {
|
|
if(this.axis.x.type === 'datetime') {
|
|
formatterX = d3.time.format(this.tooltipFormatter.format.x);
|
|
} else {
|
|
formatterX = d3.format(this.tooltipFormatter.format.x);
|
|
}
|
|
} else {
|
|
formatterX = function(d) { return d; };
|
|
}
|
|
if(this.tooltipFormatter.format.y.length) {
|
|
if(this.axis.y.type === 'datetime') {
|
|
formatterY = d3.time.format(this.tooltipFormatter.format.y);
|
|
} else {
|
|
formatterY = d3.format(this.tooltipFormatter.format.y);
|
|
}
|
|
} else {
|
|
formatterY = function(d) { return d; };
|
|
}
|
|
for(var name in points) {
|
|
var _x, _y;
|
|
if(this.axis.y.type === 'datetime') {
|
|
_x = formatterY(new Date(points[name].y));
|
|
} else {
|
|
if(points[name].invert) {
|
|
_x = formatterY(this.y.invert(points[name].y));
|
|
} else {
|
|
_x = formatterY(points[name].y);
|
|
}
|
|
}
|
|
if(this.axis.x.type === 'datetime') {
|
|
_y = formatterX(new Date(points[name].x));
|
|
} else {
|
|
if(points[name].invert) {
|
|
_y = formatterX(this.x.invert(points[name].x));
|
|
} else {
|
|
_y = formatterX(points[name].x);
|
|
}
|
|
}
|
|
|
|
_x = this.tooltipFormatter.abs.x ? Math.abs(_x) : _x;
|
|
_y = this.tooltipFormatter.abs.y ? Math.abs(_y) : _y;
|
|
var series = "<div style='color: "+points[name].opts.color+"; margin-bottom: 2px'><b style='position:relative; top: -5px; left: -2px;'><span style='font-size: 22px;'>■ </span><span style='position:relative; top: -3px; left: -2px;'>"+name+"</span></b></div>";
|
|
var x = "<div style='font-size: 10px; margin-top: -10px;'>x : " + _x + " </div>";
|
|
var y = "<div style='font-size: 10px; margin-top: -5px;'>y : " + _y + " </div><br>";
|
|
html = (series+x+y) + html;
|
|
}
|
|
|
|
html = html.slice(0, html.length-4);
|
|
|
|
this.tooltip.html(html);
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.overlayX = function(self, coordinates) {
|
|
var ycord = coordinates[1];
|
|
try {
|
|
if(self.axis.x.type === 'ordinal') {
|
|
var x0 = coordinates[0];
|
|
if(self.axis.x.range === 'column' || self.axis.x.range === 'bar') {
|
|
x0 = x0 - self.x.rangeBand()/2;
|
|
}
|
|
|
|
var i = self.bisectLeft(self.crosshair_data, x0, 1);
|
|
} else {
|
|
var x0 = self.x.invert(coordinates[0]+1);
|
|
var i = self.bisectLeft(self.crosshair_data, x0, 1);
|
|
}
|
|
|
|
var d0 = self.crosshair_data[i - 1],
|
|
d1 = self.crosshair_data[i],
|
|
d = x0 - d0.x > d1.x - x0 ? d1 : d0;
|
|
|
|
var y = d.y;
|
|
var others = d.others;
|
|
var xpos;
|
|
var ys = [];
|
|
var points = {};
|
|
var ok = [];
|
|
for(var name in self.charts) {
|
|
try {
|
|
if(y.hasOwnProperty(name)) {
|
|
if(self.axis.x.type === 'ordinal') {
|
|
if(y[name] !== null && d.others[name].y0 !== null) {
|
|
ok.push(true);
|
|
var dx = d.x;
|
|
var dy = self.y(y[name]);
|
|
if(self.axis.x.range === 'column' || self.axis.x.range === 'bar') {
|
|
if(self.grouped && self.charts[name].hasOwnProperty('count')) {
|
|
dx = d.x + ((self.x.rangeBand()/(self.charts[name].layers.length)) * (self.charts[name].count)) + self.x.rangeBand()/(2*self.charts[name].layers.length);
|
|
} else {
|
|
dx += self.x.rangeBand()/2;
|
|
}
|
|
}
|
|
|
|
self.charts[name].focus.attr('transform', 'translate(' + dx + ',' + dy +')').style('display', null);
|
|
|
|
if(self.axis.x.range === 'column' || self.axis.x.range === 'bar') {
|
|
if(self.grouped && self.charts[name].hasOwnProperty('count')) {
|
|
xpos = d.x + self.x.rangeBand()/2;
|
|
} else {
|
|
xpos = dx;
|
|
}
|
|
} else {
|
|
xpos = dx;
|
|
}
|
|
ys.push(dy);
|
|
points[name] = {
|
|
x: dx,
|
|
y: y[name] ? dy : null,
|
|
opts: self.charts[name].opts,
|
|
invert: true
|
|
};
|
|
}
|
|
} else {
|
|
ok.push(true);
|
|
var dx = self.x(d.x);
|
|
var dy = self.y(y[name]);
|
|
self.charts[name].focus.attr('transform', 'translate(' + dx + ',' + dy +')').style('display', null);
|
|
|
|
xpos = dx;
|
|
ys.push(dy);
|
|
points[name] = {
|
|
x: d.x,
|
|
y: y[name],
|
|
opts: self.charts[name].opts,
|
|
invert: false
|
|
};
|
|
}
|
|
if(ok.length) {
|
|
self.charts[name].on_focus(dx, dy);
|
|
}
|
|
} else {
|
|
self.charts[name].focus.style('display', 'none');
|
|
self.charts[name].off_focus();
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
}
|
|
if(ok.length) {
|
|
if(self.axis.x.type === 'ordinal') {
|
|
var dx = d.x;
|
|
if(self.axis.x.range === 'column' || self.axis.x.range === 'bar') {
|
|
dx += self.x.rangeBand()/2;
|
|
}
|
|
self.focusLine.style('display', null).attr('transform', 'translate(' + dx + ','+ '0)');
|
|
} else {
|
|
self.focusLine.style('display', null).attr('transform', 'translate(' + self.x(d.x) + ','+ '0)');
|
|
}
|
|
|
|
self.move_tooltip_x(xpos, ys, points);
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.overlayY = function(self, coordinates) {
|
|
// here .x is .y and .y should be .x due to axis inversion
|
|
var xcord = coordinates[0];
|
|
var y0 = coordinates[1];
|
|
|
|
try {
|
|
var len = 0;
|
|
if(self.axis.y.type === 'ordinal') {
|
|
if(self.axis.y.range === 'column' || self.axis.y.range === 'bar') {
|
|
y0 = y0 - self.y.rangeBand()/2;
|
|
}
|
|
} else {
|
|
y0 = self.y.invert(y0);
|
|
}
|
|
var i = self.bisectRight(self.crosshair_data, y0, 1);
|
|
|
|
var d0 = self.crosshair_data[i - 1],
|
|
d1 = self.crosshair_data[i],
|
|
d = y0 - d0.x > d1.x - y0 ? d1 : d0;
|
|
|
|
var y = d.y;
|
|
var others = d.others;
|
|
var xpos;
|
|
var ys = [];
|
|
var points = {};
|
|
var ok = [];
|
|
for(var name in self.charts) {
|
|
try {
|
|
if(y.hasOwnProperty(name)) {
|
|
if(self.axis.y.type === 'ordinal') {
|
|
if(y[name] !== null && d.others[name].y0 !== null) {
|
|
ok.push(true);
|
|
var dx = d.x;
|
|
var dy = self.x(y[name]);
|
|
if(self.axis.y.range === 'column' || self.axis.y.range === 'bar') {
|
|
if(self.grouped && self.charts[name].hasOwnProperty('count')) {
|
|
dx = d.x + ((self.y.rangeBand()/(self.charts[name].layers.length)) * (self.charts[name].count)) + self.y.rangeBand()/(2*self.charts[name].layers.length);
|
|
} else {
|
|
dx += self.y.rangeBand()/2;
|
|
}
|
|
}
|
|
|
|
self.charts[name].focus.attr('transform', 'translate(' + dy + ',' + dx +')').style('display', null);
|
|
xpos = dx;
|
|
ys.push(dy);
|
|
points[name] = {
|
|
x: dy,
|
|
y: dx,
|
|
opts: self.charts[name].opts,
|
|
invert: true
|
|
};
|
|
}
|
|
} else {
|
|
ok.push(true);
|
|
var dx = self.y(d.x);
|
|
var dy = self.x(y[name]);
|
|
self.charts[name].focus.attr('transform', 'translate(' + dy + ',' + dx +')').style('display', null);
|
|
xpos = dx;
|
|
ys.push(dy);
|
|
points[name] = {
|
|
x: y[name],
|
|
y: d.x,
|
|
opts: self.charts[name].opts,
|
|
invert: false
|
|
};
|
|
}
|
|
if(ok.length) {
|
|
self.charts[name].on_focus(dy, dx);
|
|
}
|
|
} else {
|
|
self.charts[name].focus.style('display', 'none');
|
|
self.charts[name].off_focus();
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
}
|
|
if(ok.length) {
|
|
if(self.axis.y.type === 'ordinal') {
|
|
var dx = d.x;
|
|
if(self.axis.y.range === 'column' || self.axis.y.range === 'bar') {
|
|
dx += self.y.rangeBand()/2;
|
|
}
|
|
self.focusLine.style('display', null).attr('transform', 'translate(0,' + dx + ')');
|
|
} else {
|
|
self.focusLine.style('display', null).attr('transform', 'translate(0,'+self.y(d.x)+')');
|
|
}
|
|
|
|
self.move_tooltip_y(xpos, ys, points);
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.resetFocus = function(forced) {
|
|
if(!this.width) return;
|
|
if(!this.height) return;
|
|
|
|
if(this.focusLine) this.focusLine.remove();
|
|
if(this.symbols) this.symbols.remove();
|
|
if(this.overlay) this.overlay.remove();
|
|
|
|
if(this.xlabel) this.xlabel.remove();
|
|
if(this.ylabel) this.ylabel.remove();
|
|
var self = this;
|
|
|
|
if(this.axis.x.label.length) {
|
|
this.xlabel = this.root.append('text');
|
|
this.xlabel.style('font-size', '12px');
|
|
this.xlabel.style('font-weight', 'bold');
|
|
this.xlabel.style('font-family', 'Lato, "Lucida Grande", Arial, Helvetica, sans-serif');
|
|
this.xlabel.attr('fill', this.xlabelcolor || 'steelblue');
|
|
this.xlabel.attr('stroke', 'none');
|
|
this.xlabel.style('text-anchor', 'middle');
|
|
if(this.opts.invertAxes) {
|
|
this.xlabel.attr('y', -this.margin.left+20);
|
|
this.xlabel.attr('x', -parseInt(this.root.attr('height'))/2);
|
|
this.xlabel.attr('transform', 'rotate(-90)');
|
|
this.xlabel.text(this.axis.y.label);
|
|
} else {
|
|
this.xlabel.attr('y', (parseInt(this.root.attr('height')) + this.margin.bottom - 15));
|
|
this.xlabel.attr('x', parseInt(this.root.attr('width'))/2);
|
|
this.xlabel.text(this.axis.x.label);
|
|
}
|
|
}
|
|
|
|
if(this.axis.y.label.length) {
|
|
this.ylabel = this.root.append('text');
|
|
this.ylabel.style('font-size', '12px');
|
|
this.ylabel.style('font-weight', 'bold');
|
|
this.ylabel.style('font-family', 'Lato, "Lucida Grande", Arial, Helvetica, sans-serif');
|
|
this.ylabel.attr('fill', this.ylabelcolor || 'steelblue');
|
|
this.ylabel.attr('stroke', 'none');
|
|
this.ylabel.style('text-anchor', 'middle');
|
|
if(this.opts.invertAxes) {
|
|
this.ylabel.attr('y', (parseInt(this.root.attr('height')) + this.margin.bottom - 15));
|
|
this.ylabel.attr('x', parseInt(this.root.attr('width'))/2);
|
|
this.ylabel.text(this.axis.x.label);
|
|
} else {
|
|
this.ylabel.attr('y', -this.margin.left+20);
|
|
this.ylabel.attr('x', -parseInt(this.root.attr('height'))/2);
|
|
this.ylabel.attr('transform', 'rotate(-90)');
|
|
this.ylabel.text(this.axis.y.label);
|
|
}
|
|
}
|
|
|
|
if(this.opts.invertAxes) {
|
|
this.focusLine = this.focus_line_group.append('line').attr('x1', 0).attr('y1', 0).attr('x2', this.width).attr('y2', 0).attr('stroke', this.opts.theme_focus_line_color).attr('stroke-width', 1).attr('shape-rendering', 'crispEdges').style('display', 'none').attr('class', 'focus-line');
|
|
} else {
|
|
this.focusLine = this.focus_line_group.append('line').attr('x1', 0).attr('y1', 0).attr('x2', 0).attr('y2', this.height).attr('stroke', this.opts.theme_focus_line_color).attr('stroke-width', 1).attr('shape-rendering', 'crispEdges').style('display', 'none').attr('class', 'focus-line');
|
|
}
|
|
|
|
this.symbols = this.symbols_group.append('g');
|
|
for(var name in self.charts) {
|
|
self.charts[name].on_complete_draw();
|
|
}
|
|
|
|
var order = [];
|
|
for(var name in self.charts) {
|
|
order.push(name);
|
|
}
|
|
|
|
if(this.axis.x.range == 'column' || this.axis.x.range == 'bar') {
|
|
order.reverse();
|
|
}
|
|
|
|
for(var i=0; i<order.length; i++) {
|
|
var name = order[i];
|
|
self.charts[name].setupFocus();
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
if(!forced) {
|
|
this._createBrushPath();
|
|
}
|
|
}
|
|
|
|
this.overlay = this.root.append('rect');
|
|
this.overlay.attr('class', 'overlay');
|
|
this.overlay.attr('width', this.width);
|
|
this.overlay.attr('height', this.height);
|
|
this.overlay.attr('fill', 'none');
|
|
this.overlay.attr('pointer-events', 'all');
|
|
|
|
this.overlay.attr('clip-path', 'url(#'+this.master_id+'-clip)');
|
|
|
|
var over = function() {
|
|
$(window).trigger('rubix.sidebar.off');
|
|
d3.event.preventDefault();
|
|
self.focusLine.style('display', null);
|
|
$(this).focus();
|
|
};
|
|
|
|
var move = function() {
|
|
$(window).trigger('rubix.sidebar.off');
|
|
d3.event.preventDefault();
|
|
$(this).focus();
|
|
if(!self.hasData) return;
|
|
if(self.is_touch_device) {
|
|
var coordinates = d3.touches(this)[0];
|
|
} else {
|
|
var coordinates = d3.mouse(this);
|
|
}
|
|
if(self.opts.invertAxes) {
|
|
if(coordinates[1] < 0 || coordinates[1] > self.height) return;
|
|
self.overlayY.call(this, self, coordinates);
|
|
} else {
|
|
if(coordinates[0] < 0 || coordinates[0] > self.width) return;
|
|
self.overlayX.call(this, self, coordinates);
|
|
}
|
|
};
|
|
|
|
var out = function() {
|
|
$(window).trigger('rubix.sidebar.on');
|
|
d3.event.preventDefault();
|
|
$(this).focus();
|
|
self.tooltip.hide();
|
|
for(var name in self.charts) {
|
|
try {
|
|
self.charts[name].focus.style('display', 'none');
|
|
self.charts[name].off_focus();
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
}
|
|
self.focusLine.style('display', 'none');
|
|
};
|
|
|
|
if(self.is_touch_device) {
|
|
if(window.navigator.msPointerEnabled) {
|
|
this.overlay.on('MSPointerDown', over);
|
|
this.overlay.on('MSPointerMove', move);
|
|
this.overlay.on('MSPointerUp' , out);
|
|
} else {
|
|
this.overlay.on('touchstart', over);
|
|
this.overlay.on('touchmove', move);
|
|
this.overlay.on('touchend' , out);
|
|
}
|
|
} else {
|
|
this.overlay.on('mouseover', over);
|
|
this.overlay.on('mousemove', move);
|
|
this.overlay.on('mouseout' , out);
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.runCommand = function(cmd) {
|
|
for(var i in this.charts) {
|
|
try {
|
|
this.charts[i][cmd](this);
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.prototype.resetLabelHandlers = function() {
|
|
var self = this;
|
|
$('.'+this.master_id+'-legend-labels').css({
|
|
'opacity': 0.9
|
|
});
|
|
|
|
$('.'+this.master_id+'-legend-labels').off().on({
|
|
'hover': function(e) {
|
|
var hasClass = $(this).hasClass('toggle');
|
|
|
|
if(hasClass) return;
|
|
|
|
switch(e.type) {
|
|
case 'mouseenter':
|
|
$(this).css({
|
|
'opacity': 1
|
|
});
|
|
break;
|
|
case 'mouseleave':
|
|
$(this).css({
|
|
'opacity': 0.9
|
|
});
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
'click': function(e) {
|
|
var hasClass = $(this).hasClass('toggle');
|
|
var name = $(this).attr('data-name');
|
|
var type = $(this).attr('data-type') || '';
|
|
|
|
if(!hasClass) {
|
|
$(this).css({
|
|
'opacity': 0.2
|
|
});
|
|
$(this).addClass('toggle');
|
|
|
|
var chart = self.charts[name];
|
|
chart.hidden();
|
|
delete self.charts[name];
|
|
self.chart_stack[name] = chart;
|
|
|
|
if(type.length) {
|
|
var fetchData = self.column_stack_data;
|
|
var pushData = self.column_stack_data_stack;
|
|
if(type === 'astack') {
|
|
fetchData = self.area_stack_data;
|
|
pushData = self.area_stack_data_stack;
|
|
}
|
|
var rdata, orderKey = 0;
|
|
for(var i=0; i<fetchData.length; i++) {
|
|
if(!fetchData[i]) continue;
|
|
var fname = fetchData[i].key;
|
|
if(fname === name) {
|
|
orderKey = fetchData[i].orderKey;
|
|
rdata = fetchData[i];
|
|
fetchData.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
pushData.splice(orderKey, 0, rdata);
|
|
}
|
|
|
|
var data = self.data[name];
|
|
self.data_stack[name] = data;
|
|
delete self.data[name];
|
|
} else {
|
|
$(this).css({
|
|
'opacity': 1
|
|
});
|
|
$(this).removeClass('toggle');
|
|
|
|
var chart = self.chart_stack[name];
|
|
self.charts[name] = chart;
|
|
delete self.chart_stack[name];
|
|
|
|
if(type.length) {
|
|
var pushedData = self.column_stack_data_stack;
|
|
var origData = self.column_stack_data;
|
|
if(type === 'astack') {
|
|
pushedData = self.area_stack_data_stack;
|
|
origData = self.area_stack_data;
|
|
}
|
|
var rdata, orderKey = 0;
|
|
for(var i=0; i<pushedData.length; i++) {
|
|
if(!pushedData[i]) continue;
|
|
var fname = pushedData[i].key;
|
|
if(fname === name) {
|
|
orderKey = pushedData[i].orderKey;
|
|
rdata = pushedData[i];
|
|
pushedData.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
origData.splice(orderKey, 0, rdata);
|
|
}
|
|
|
|
var data = self.data_stack[name];
|
|
self.data[name] = data;
|
|
delete self.data_stack[name];
|
|
self.charts[name].show();
|
|
}
|
|
|
|
/** TODO: Investigate why draw needs to be called twice */
|
|
self.draw();
|
|
self.draw();
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @param {?Object} opts
|
|
* @return {Rubix.LineSeries}
|
|
*/
|
|
Rubix.prototype.line_series = function(opts) {
|
|
opts = opts || {};
|
|
opts.name = opts.name || this._generate_name();
|
|
if(this.charts.hasOwnProperty(opts.name)) {
|
|
throw new Error("Series exists: " + name);
|
|
}
|
|
var line_series = new Rubix.LineSeries(this, opts);
|
|
this.charts[line_series.name] = line_series;
|
|
this.legend.append("<div class='"+this.master_id+"-legend-labels' data-name='"+line_series.name+"' style='cursor: pointer; display: inline-block; font-weight: bold; margin-right: 10px; color: "+line_series.opts.color+"'><span style='font-size: 22px;'>■ </span><span style='font-size: 12px; position:relative; top: -3px; left: -2px;'>"+line_series.name+"</span></div>");
|
|
this.resetLabelHandlers();
|
|
return line_series;
|
|
};
|
|
|
|
/**
|
|
* @param {?Object} opts
|
|
* @return {Rubix.AreaSeries}
|
|
*/
|
|
Rubix.prototype.area_series = function(opts) {
|
|
opts = opts || {};
|
|
opts.name = opts.name || this._generate_name();
|
|
if(this.charts.hasOwnProperty(opts.name)) {
|
|
throw new Error("Series exists: " + name);
|
|
}
|
|
if(this.stacked) {
|
|
var stacked_area_series = new Rubix.StackedAreaSeries(this, opts);
|
|
this.charts[stacked_area_series.name] = stacked_area_series;
|
|
this.legend.append("<div class='"+this.master_id+"-legend-labels' data-name='"+stacked_area_series.name+"' data-type='astack' style='cursor: pointer; display: inline-block; font-weight: bold; margin-right: 10px; color: "+stacked_area_series.opts.color+"'><span style='font-size: 22px;'>■ </span><span style='font-size: 12px; position:relative; top: -3px; left: -2px;'>"+stacked_area_series.name+"</span></div>");
|
|
this.resetLabelHandlers();
|
|
return stacked_area_series;
|
|
}
|
|
var area_series = new Rubix.AreaSeries(this, opts);
|
|
this.charts[area_series.name] = area_series;
|
|
this.legend.append("<div class='"+this.master_id+"-legend-labels' data-name='"+area_series.name+"' style='cursor: pointer; display: inline-block; font-weight: bold; margin-right: 10px; color: "+area_series.opts.color+"'><span style='font-size: 22px;'>■ </span><span style='font-size: 12px; position:relative; top: -3px; left: -2px;'>"+area_series.name+"</span></div>");
|
|
this.resetLabelHandlers();
|
|
return area_series;
|
|
};
|
|
|
|
Rubix.prototype._generate_name = function() {
|
|
return 'Series ' + (++this.chart_counter);
|
|
};
|
|
|
|
/**
|
|
* @param {?Object} opts
|
|
* @return {Rubix.ColumnSeries}
|
|
*/
|
|
Rubix.prototype.column_series = function(opts) {
|
|
opts = opts || {};
|
|
opts.name = opts.name || this._generate_name();
|
|
if(this.charts.hasOwnProperty(opts.name)) {
|
|
throw new Error("Series exists: " + name);
|
|
}
|
|
if(this.opts.invertAxes !== false || this.opts.axis.x.range !== 'column') {
|
|
this.opts.invertAxes = false;
|
|
this.opts.axis.x.type = 'ordinal';
|
|
this.opts.axis.x.range = 'column';
|
|
this.setup()
|
|
}
|
|
var column_series = new Rubix.ColumnSeries(this, opts);
|
|
this.charts[column_series.name] = column_series;
|
|
this.legend.append("<div class='"+this.master_id+"-legend-labels' data-name='"+column_series.name+"' data-type='cstack' style='cursor: pointer; display: inline-block; font-weight: bold; margin-right: 10px; color: "+column_series.opts.color+"'><span style='font-size: 22px;'>■ </span><span style='font-size: 12px; position:relative; top: -3px; left: -2px;'>"+column_series.name+"</span></div>");
|
|
this.resetLabelHandlers();
|
|
return column_series;
|
|
};
|
|
|
|
/**
|
|
* @param {?Object} opts
|
|
* @return {Rubix.ColumnSeries}
|
|
*/
|
|
Rubix.prototype.bar_series = function(opts) {
|
|
opts = opts || {};
|
|
opts.name = opts.name || this._generate_name();
|
|
if(this.charts.hasOwnProperty(opts.name)) {
|
|
throw new Error("Series exists: " + name);
|
|
}
|
|
if(this.opts.invertAxes !== true || this.opts.axis.x.range !== 'column') {
|
|
this.opts.invertAxes = true;
|
|
this.opts.axis.x.type = 'ordinal';
|
|
this.opts.axis.x.range = 'column';
|
|
this.setup()
|
|
}
|
|
var column_series = new Rubix.ColumnSeries(this, opts);
|
|
this.charts[column_series.name] = column_series;
|
|
this.legend.append("<div class='"+this.master_id+"-legend-labels' data-name='"+column_series.name+"' data-type='cstack' style='cursor: pointer; display: inline-block; font-weight: bold; margin-right: 10px; color: "+column_series.opts.color+"'><span style='font-size: 22px;'>■ </span><span style='font-size: 12px; position:relative; top: -3px; left: -2px;'>"+column_series.name+"</span></div>");
|
|
this.resetLabelHandlers();
|
|
return column_series;
|
|
};
|
|
|
|
window.Rubix = window.Rubix || {};
|
|
/**
|
|
* @param {Rubix} rubix
|
|
* @param {Object} opts
|
|
* @constructor
|
|
*/
|
|
Rubix.StackedAreaSeries = function(rubix, opts) {
|
|
this.opts = opts;
|
|
this.opts.color = this.opts.color || 'steelblue';
|
|
this.opts.marker = this.opts.marker || 'circle';
|
|
this.opts.fillopacity = this.opts.fillopacity || 0.5;
|
|
this.opts.strokewidth = this.opts.strokewidth || 1;
|
|
this.opts.noshadow = this.opts.noshadow || false;
|
|
this.show_markers = this.opts.show_markers;
|
|
|
|
if(!this.opts.hasOwnProperty('name')) throw new Error('StackedAreaSeries should have a \'name\' property');
|
|
|
|
this.name = this.opts.name;
|
|
|
|
this.chart_hidden = false;
|
|
this.temp_stack = [];
|
|
|
|
this.setup = false;
|
|
this.last_stack = [];
|
|
this.last_stack_name = '';
|
|
this.last_stack_added = false;
|
|
|
|
this._setup(rubix);
|
|
};
|
|
|
|
/**
|
|
* @param {Rubix} rubix
|
|
* @private
|
|
*/
|
|
Rubix.StackedAreaSeries.prototype._setup = function(rubix) {
|
|
this.rubix = rubix;
|
|
|
|
this.root = this.rubix.root;
|
|
this.data = this.rubix.data;
|
|
this.width = this.rubix.width;
|
|
this.height = this.rubix.height;
|
|
this.stack = this.rubix.area_stack;
|
|
this.offset = this.rubix.area_offset;
|
|
this.area_stack_data = this.rubix.area_stack_data;
|
|
this.stacked_area_series = this.rubix.root_stacked_area_series;
|
|
this.show_markers = (this.show_markers === undefined) ? this.rubix.show_markers : this.show_markers;
|
|
|
|
this.master_detail = this.rubix.master_detail;
|
|
|
|
if(this.master_detail) {
|
|
this.md_root = this.rubix.md_root;
|
|
}
|
|
|
|
if(!this.id) {
|
|
this.id = this.rubix.uid('stacked-area');
|
|
}
|
|
|
|
/** separator */
|
|
var self = this;
|
|
this.line = d3.svg.line();
|
|
this.line.defined(function(d) {
|
|
if(self.rubix.offset === 'expand') {
|
|
if(d.y_new === 0 && d.y0 === 1) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return d.y_new !== null && d.x !== null;
|
|
});
|
|
if(this.rubix.opts.invertAxes) {
|
|
this.line.x(function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.x(val);
|
|
});
|
|
this.line.y(function(d) {
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
return self.rubix.y(d.x) + self.rubix.y.rangeBand()/2;
|
|
}
|
|
return self.rubix.y(d.x);
|
|
});
|
|
this.line.interpolate(this.rubix.interpolate);
|
|
} else {
|
|
this.line.x(function(d) {
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
return self.rubix.x(d.x) + self.rubix.x.rangeBand()/2;
|
|
}
|
|
return self.rubix.x(d.x);
|
|
});
|
|
this.line.y(function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.y(val);
|
|
});
|
|
this.line.interpolate(this.rubix.interpolate);
|
|
}
|
|
|
|
this.area = d3.svg.area();
|
|
this.area.defined(this.line.defined());
|
|
|
|
if(this.rubix.opts.invertAxes) {
|
|
this.area.x0(function(d) {
|
|
var val = d.y0;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.x(val);
|
|
});
|
|
this.area.x1(function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.x(val);
|
|
});
|
|
this.area.y(function(d) {
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
return self.rubix.y(d.x) + self.rubix.y.rangeBand()/2;
|
|
}
|
|
return self.rubix.y(d.x);
|
|
});
|
|
this.area.interpolate(this.rubix.interpolate);
|
|
} else {
|
|
this.area.x(function(d) {
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
return self.rubix.x(d.x) + self.rubix.x.rangeBand()/2;
|
|
}
|
|
return self.rubix.x(d.x);
|
|
});
|
|
this.area.y0(function(d) {
|
|
var val = d.y0;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.y(val);
|
|
});
|
|
this.area.y1(function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.y(val);
|
|
});
|
|
this.area.interpolate(this.rubix.interpolate);
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.master_line = d3.svg.line();
|
|
this.master_line.defined(function(d) {
|
|
if(self.rubix.area_offset === 'expand') {
|
|
if(d.y_new === 0 && d.y0 === 1) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return d.y_new !== null && d.x !== null;
|
|
});
|
|
this.master_line.x(function(d) {
|
|
return self.rubix.x2(d.x);
|
|
});
|
|
this.master_line.y(function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.y2(val);
|
|
});
|
|
this.master_line.interpolate(this.rubix.interpolate);
|
|
|
|
this.master_area = d3.svg.area();
|
|
this.master_area.defined(this.master_line.defined());
|
|
this.master_area.x(function(d) {
|
|
return self.rubix.x2(d.x);
|
|
});
|
|
this.master_area.y0(function(d) {
|
|
var val = d.y0;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.y2(val);
|
|
});
|
|
this.master_area.y1(function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
return 0;
|
|
}
|
|
return self.rubix.y2(val);
|
|
});
|
|
this.master_area.interpolate(this.rubix.interpolate);
|
|
}
|
|
};
|
|
|
|
// Alias for draw
|
|
Rubix.StackedAreaSeries.prototype.redraw = function(rubix) {
|
|
this._setup(rubix);
|
|
this.draw();
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.noRedraw = function(rubix) {
|
|
this._setup(rubix);
|
|
this.draw(true);
|
|
};
|
|
|
|
/**
|
|
* @param {Array|Object} data
|
|
*/
|
|
Rubix.StackedAreaSeries.prototype.addData = function(data) {
|
|
this.rubix.data_changed = true;
|
|
if(!(data instanceof Array)) {
|
|
if(!(data instanceof Object)) {
|
|
throw new Error("Data must be an array or object");
|
|
} else {
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
data = [data];
|
|
}
|
|
}
|
|
|
|
if(!data.length) return;
|
|
|
|
if(!this.rubix.opts.noSort) {
|
|
data.sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.data[this.name] = data;
|
|
|
|
this.area_stack_data.push({
|
|
key: this.name,
|
|
color: this.opts.color,
|
|
values: this.data[this.name],
|
|
fillopacity: this.opts.fillopacity,
|
|
strokewidth: this.opts.strokewidth,
|
|
orderKey: this.area_stack_data.length
|
|
});
|
|
|
|
this.rubix.resetAxis(true);
|
|
this.rubix.forceRedraw();
|
|
this._animate_draw();
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.draw = function(forced) {
|
|
if(!this.name) return;
|
|
if(!this.data) return;
|
|
if(!this.data.hasOwnProperty(this.name)) return;
|
|
if(!this.data[this.name].length) return;
|
|
|
|
var oldLayers = this.layers;
|
|
try {
|
|
this.layers = this.stack(this.area_stack_data);
|
|
} catch(e) {
|
|
// data un-available. retaining old layer.
|
|
this.layers = oldLayers;
|
|
}
|
|
|
|
var self = this;
|
|
|
|
var isConstructed = this.stacked_area_series.selectAll('.'+this.id)[0].length;
|
|
|
|
if(!isConstructed) {
|
|
try {
|
|
this.stacked_area_series.selectAll('.layer').remove();
|
|
this.stacked_area_series.selectAll('.' + this.id + '-line').remove();
|
|
|
|
var p = this.stacked_area_series.selectAll('.layer').data(this.layers, function(d) {
|
|
return d.key;
|
|
});
|
|
|
|
var createdPath = p.enter().append('path');
|
|
createdPath.attr('class', 'layer')
|
|
.attr('d', function(d) { return self.area(d.values); })
|
|
.attr('fill', function(d) { return d.color; })
|
|
.attr('fill-opacity', function(d) {
|
|
return d.fillopacity
|
|
})
|
|
.attr('stroke', 'none');
|
|
|
|
p.exit().remove();
|
|
|
|
if(this.master_detail) {
|
|
createdPath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
createdPath.classed('clipped', true);
|
|
|
|
if(!forced) {
|
|
var md_p = this.md_root.select('.md-layers > .md_stacked_area_series').selectAll('.layer').data(this.layers, function(d) {
|
|
return d.key;
|
|
});
|
|
|
|
var createdMdPath = md_p.enter().append('path');
|
|
createdMdPath.attr('class', 'layer')
|
|
.attr('d', function(d) { return self.master_area(d.values); })
|
|
.attr('fill', function(d) { return d.color; })
|
|
.attr('fill-opacity', function(d) {
|
|
return d.fillopacity;
|
|
})
|
|
.attr('stroke', 'none');
|
|
|
|
createdMdPath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
|
|
createdMdPath.classed('clipped', true);
|
|
|
|
md_p.exit().remove();
|
|
}
|
|
}
|
|
|
|
for(var i=0; i<this.layers.length; i++) {
|
|
var name = this.layers[i].key;
|
|
if(this.name === name) {
|
|
var datum = this.layers[i].values;
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1 = this.stacked_area_series.append('path').attr('class', this.id+'-line').datum(datum).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 5 * this.opts.strokewidth).attr('stroke-opacity', 0.05000000000000001).attr('transform', 'translate(1,1)');
|
|
this.strokePath2 = this.stacked_area_series.append('path').attr('class', this.id+'-line').datum(datum).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 3 * this.opts.strokewidth).attr('stroke-opacity', 0.1).attr('transform', 'translate(1,1)');
|
|
this.strokePath3 = this.stacked_area_series.append('path').attr('class', this.id+'-line').datum(datum).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 1 * this.opts.strokewidth).attr('stroke-opacity', 0.15000000000000002).attr('transform', 'translate(1,1)');
|
|
}
|
|
this.linePath = this.stacked_area_series.append('path').datum(datum).attr('class', this.id+'-line').attr('stroke', this.opts.color).attr('fill', 'none').attr('stroke-linecap', 'round').attr('d', this.line).attr('stroke-width', 2 * this.opts.strokewidth);
|
|
|
|
if(this.master_detail) {
|
|
if(!forced) {
|
|
this.md_root.select('.md-layers > .md_stacked_area_series').selectAll('.' + this.id + '-line').remove();
|
|
this.masterLinePath = this.md_root.select('.md-layers > .md_stacked_area_series').append('path').datum(datum).attr('class', this.id+'-line').attr('stroke', this.opts.color).attr('fill', 'none').attr('stroke-linecap', 'round').attr('d', this.master_line).attr('stroke-width', 2 * this.opts.strokewidth);
|
|
this.masterLinePath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.masterLinePath.classed('clipped', true);
|
|
|
|
if(this.dataChanged) {
|
|
this.rubix.resetExtent();
|
|
this.dataChanged = false;
|
|
}
|
|
}
|
|
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.strokePath2.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.strokePath3.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
}
|
|
this.linePath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1.classed('clipped', true);
|
|
this.strokePath2.classed('clipped', true);
|
|
this.strokePath3.classed('clipped', true);
|
|
}
|
|
this.linePath.classed('clipped', true);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
this.setup = true;
|
|
} else {
|
|
this.rubix.runCommand('globalRedraw');
|
|
}
|
|
|
|
this.rubix.resetFocus(forced);
|
|
};
|
|
|
|
/**
|
|
* @param {Object} data
|
|
* @param {?Boolean} shift
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.StackedAreaSeries.prototype.updatePoint = function(data, shift, noRedraw) {
|
|
this.addPoint(data, shift, noRedraw);
|
|
};
|
|
|
|
/**
|
|
* @param {*} ref
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.StackedAreaSeries.prototype.removePoint = function(ref, noRedraw) {
|
|
if(this.chart_hidden) {
|
|
this.temp_stack.push({
|
|
type: 'removePoint',
|
|
ref: ref
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(!this.data[this.name]) {
|
|
this.data[this.name] = [];
|
|
}
|
|
|
|
var removed = false, pos = 0;
|
|
for(var i=0; i<this.data[this.name].length; i++) {
|
|
if(this.data[this.name][i].x === ref) {
|
|
this.data[this.name][i].y = null;
|
|
pos = i;
|
|
removed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!removed) return;
|
|
|
|
var found = false;
|
|
for(var i=0; i<this.area_stack_data.length; i++) {
|
|
var st = this.area_stack_data[i];
|
|
try {
|
|
if(st.values[pos].x === ref) {
|
|
if(st.values[pos].y !== null) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
}
|
|
|
|
if(!found) {
|
|
for(var i=0; i<this.area_stack_data.length; i++) {
|
|
this.area_stack_data[i].values.splice(pos, 1);
|
|
}
|
|
}
|
|
|
|
var oldLayers = this.layers;
|
|
try {
|
|
this.layers = this.stack(this.area_stack_data);
|
|
} catch(e) {
|
|
// data un-available. retaining old layer.
|
|
this.layers = oldLayers;
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.dataChanged = true;
|
|
}
|
|
|
|
if(noRedraw) return;
|
|
|
|
if(this.setup) {
|
|
this._animate_draw();
|
|
} else {
|
|
this.rubix.resetAxis(true);
|
|
this.rubix.forceRedraw();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {Object} data
|
|
* @param {?Boolean} shift
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.StackedAreaSeries.prototype.addPoint = function(data, shift, noRedraw) {
|
|
this.rubix.data_changed = true;
|
|
if(!(data instanceof Object) || (data instanceof Array)) {
|
|
throw new Error("Object required for addPoint");
|
|
}
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
if(this.chart_hidden) {
|
|
this.temp_stack.push({
|
|
type: 'addPoint',
|
|
data: data,
|
|
shift: shift
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(!this.data[this.name]) {
|
|
this.data[this.name] = [];
|
|
}
|
|
|
|
var added = false;
|
|
for(var i=0; i<this.data[this.name].length; i++) {
|
|
if(this.data[this.name][i].x === data.x) {
|
|
this.data[this.name][i].y = data.y;
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!added) {
|
|
this.data[this.name].push(data);
|
|
}
|
|
|
|
this.data[this.name].sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
|
|
if(this.rubix.opts.interval) {
|
|
if(this.rubix.opts.interval < this.data[this.name].length) {
|
|
this.data[this.name].shift();
|
|
}
|
|
} else {
|
|
if(shift) {
|
|
this.data[this.name].shift();
|
|
}
|
|
}
|
|
|
|
var max_elems = d3.max(this.area_stack_data, function(d) {
|
|
return d.values.length;
|
|
});
|
|
|
|
for(var i=0; i<this.area_stack_data.length; i++) {
|
|
var st = this.area_stack_data[i];
|
|
if(st.values.length < max_elems) {
|
|
st.values.push({
|
|
x: data.x,
|
|
y: null
|
|
});
|
|
}
|
|
}
|
|
|
|
var oldLayers = this.layers;
|
|
try {
|
|
this.layers = this.stack(this.area_stack_data);
|
|
} catch(e) {
|
|
// data un-available. retaining old layer.
|
|
this.layers = oldLayers;
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.dataChanged = true;
|
|
}
|
|
|
|
if(noRedraw) return;
|
|
|
|
if(this.setup) {
|
|
this._animate_draw();
|
|
} else {
|
|
this.rubix.resetAxis(true);
|
|
this.rubix.forceRedraw();
|
|
}
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype._animate_draw = function() {
|
|
var text = this.root.selectAll('.y.axis').selectAll('text')[0];
|
|
var width = [];
|
|
for(var i=0; i<text.length; i++) {
|
|
width.push(text[i].getBBox().width);
|
|
}
|
|
var origMaxWidth = d3.max(width);
|
|
|
|
this.rubix.resetAxis(true);
|
|
|
|
this.rubix.runCommand('globalRedraw');
|
|
|
|
text = this.root.selectAll('.y.axis').selectAll('text')[0];
|
|
width = [];
|
|
for(var i=0; i<text.length; i++) {
|
|
width.push(text[i].getBBox().width);
|
|
}
|
|
|
|
var maxWidth = d3.max(width);
|
|
|
|
this.rubix.marginLeft(maxWidth);
|
|
this.rubix.resetFocus();
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.hidden = function() {
|
|
this.chart_hidden = true;
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.show = function() {
|
|
this.chart_hidden = false;
|
|
while(this.temp_stack.length>1) {
|
|
var rawData = this.temp_stack.shift();
|
|
if(rawData.type === 'addPoint') {
|
|
this.addPoint(rawData.data, rawData.shift, true);
|
|
} else {
|
|
this.removePoint(rawData.ref);
|
|
}
|
|
}
|
|
if(this.temp_stack.length) {
|
|
var rawData = this.temp_stack.shift();
|
|
if(rawData.type === 'addPoint') {
|
|
this.addPoint(rawData.data, rawData.shift, true);
|
|
} else {
|
|
this.removePoint(rawData.ref);
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.globalRedraw = function(rubix) {
|
|
this.stacked_area_series.select('.'+this.id+'-line').attr('d', this.line);
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.forceRedraw = function(rubix) {
|
|
this.redraw(rubix);
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.on_complete_draw = function() {
|
|
if(!this.setup) return;
|
|
var self = this;
|
|
if(this.opts.marker !== undefined && this.show_markers) {
|
|
if(this.master_detail) {
|
|
this.rubix.symbols.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip-symbols)');
|
|
}
|
|
this.markers = this.rubix.symbols.selectAll('.'+this.id+'-marker');
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(this.opts.marker);
|
|
|
|
var symbolType = this.markers.data(this.data[this.name]);
|
|
var symbolPath = symbolType.enter().append('path');
|
|
symbolPath.attr('d', symbol)
|
|
.attr('class', this.id + '-marker')
|
|
.attr('fill', this.opts.color)
|
|
.style('display', function(d) {
|
|
if(self.rubix.area_offset === 'expand') {
|
|
if(d.y_new === 0 && d.y0 === 1) {
|
|
return 'none';
|
|
}
|
|
return null;
|
|
}
|
|
if(d.y_new === null) {
|
|
return 'none';
|
|
}
|
|
return null;
|
|
})
|
|
.attr('stroke', 'white')
|
|
.attr('transform', function(d) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
val = 0;
|
|
}
|
|
if(self.rubix.opts.invertAxes) {
|
|
var _y = self.rubix.y(d.x);
|
|
var _x = self.rubix.x(val);
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
_y += self.rubix.y.rangeBand()/2;
|
|
}
|
|
} else {
|
|
var _y = self.rubix.y(val);
|
|
var _x = self.rubix.x(d.x);
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
_x += self.rubix.x.rangeBand()/2;
|
|
}
|
|
}
|
|
return 'translate('+_x+','+_y+')';
|
|
});
|
|
symbolType.exit().remove();
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.setupFocus = function() {
|
|
if(!this.setup) return;
|
|
if(this.focus) this.focus.remove();
|
|
this.focus = this.rubix.focus_group.append('g');
|
|
this.focus.attr('class', 'focus');
|
|
this.focus.style('display', 'none');
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(this.opts.marker);
|
|
var path = this.focus.append('path').attr('d', symbol).attr('fill', this.opts.color).attr('stroke', 'white').attr('stroke-width', 2);
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.on_focus = function() {
|
|
if(!this.setup) return;
|
|
this.linePath.attr('stroke-width', 2 * this.opts.strokewidth);
|
|
};
|
|
|
|
Rubix.StackedAreaSeries.prototype.off_focus = function() {
|
|
if(!this.setup) return;
|
|
this.linePath.attr('stroke-width', 2 * this.opts.strokewidth);
|
|
};
|
|
window.Rubix = window.Rubix || {};
|
|
|
|
var slugify = function(text) {
|
|
text = text.replace(/[^-a-zA-Z0-9,&\s]+/ig, '');
|
|
text = text.replace(/-/gi, "_");
|
|
text = text.replace(/\s/gi, "-");
|
|
return text;
|
|
};
|
|
|
|
/**
|
|
* @param {string} id
|
|
* @param {Object} opts
|
|
* @return {Rubix.PieDonut}
|
|
*/
|
|
Rubix.Pie = function(id, opts) {
|
|
return new Rubix.PieDonut(id, 'pie', opts);
|
|
};
|
|
|
|
/**
|
|
* @param {string} id
|
|
* @param {Object} opts
|
|
* @return {Rubix.PieDonut}
|
|
*/
|
|
Rubix.Donut = function(id, opts) {
|
|
return new Rubix.PieDonut(id, 'donut', opts);
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
* @param {string} id
|
|
* @param {Rubix.Pie || Rubix.Donut} type
|
|
* @param {Object} opts
|
|
* @constructor
|
|
*/
|
|
Rubix.PieDonut = function(id, type, opts) {
|
|
opts = opts || {};
|
|
this.type = type;
|
|
this.is_touch_device = 'ontouchstart' in document.documentElement;
|
|
|
|
this.legend_hidden = {};
|
|
|
|
this.chart_counter = 0;
|
|
this.master_id = this.uid('master_id');
|
|
|
|
this.area_stack_data_stack = [];
|
|
this.column_stack_data_stack = [];
|
|
|
|
this.root_elem = $(id);
|
|
this.root_elem.css('position', 'relative');
|
|
this.root_elem.addClass('rubixccnium-main-chart');
|
|
this.root_elem.append('<div class="rubixccnium-tooltip"></div>');
|
|
this.root_elem.append('<div class="rubixccnium-title"></div>');
|
|
this.root_elem.append('<div class="rubixccnium-subtitle"></div>');
|
|
this.root_elem.append('<div class="rubixccnium-chart"><div style="margin-top:5px">Loading...</div></div>');
|
|
this.root_elem.append('<div class="rubixccnium-legend"></div>');
|
|
|
|
var width = opts.width || '100%';
|
|
var height = opts.height || 150;
|
|
this.elem_width = width, this.elem_height = height;
|
|
this.root_elem.width(width).height(height);
|
|
|
|
this.elem = this.root_elem.find('.rubixccnium-chart');
|
|
|
|
this.tooltip = this.root_elem.find('.rubixccnium-tooltip');
|
|
this.tooltip.hide();
|
|
this.tooltip.html("");
|
|
|
|
opts.tooltip = opts.tooltip || {};
|
|
|
|
this.tooltip.css({
|
|
'font-family': 'Lato, "Lucida Grande", Arial, Helvetica, sans-serif',
|
|
'font-size': '12px',
|
|
'position': 'absolute',
|
|
'background': 'white',
|
|
'color': '#89949B',
|
|
'display': 'none',
|
|
'padding': '5px 15px',
|
|
'pointer-events': 'none',
|
|
'border-radius': '5px',
|
|
'z-index': 100,
|
|
'min-height': 50,
|
|
'user-select': 'none',
|
|
'cursor': 'default',
|
|
'border': '3px solid ' + (opts.tooltip.color ? opts.tooltip.color : '#89949B'),
|
|
'box-shadow': 'rgba(0, 0, 0, 0.2) 2px 4px 8px',
|
|
'background': 'white'
|
|
});
|
|
|
|
opts.legend = opts.legend || {};
|
|
|
|
this.legend = this.root_elem.find('.rubixccnium-legend');
|
|
this.legend.css({
|
|
'font-family': "Lato, 'Lucida Grande', Arial, Helvetica, sans-serif",
|
|
'text-align': 'center',
|
|
'margin-top': opts.legend.top || '-10px',
|
|
'margin-bottom': opts.legend.bottom || '5px',
|
|
'user-select': 'none',
|
|
'display': opts.hideLegend ? 'none' : 'block'
|
|
});
|
|
|
|
this.title = this.root_elem.find('.rubixccnium-title');
|
|
this.subtitle = this.root_elem.find('.rubixccnium-subtitle');
|
|
|
|
this.title.css({
|
|
'font-family': "Lato, 'Lucida Grande', Arial, Helvetica, sans-serif",
|
|
'text-align': 'center',
|
|
'user-select': 'none',
|
|
'font-weight': 'bold',
|
|
'font-size': '16px',
|
|
'color': 'steelblue',
|
|
'margin-top': '10px',
|
|
'cursor': 'default'
|
|
});
|
|
|
|
this.subtitle.css({
|
|
'font-family': "Lato, 'Lucida Grande', Arial, Helvetica, sans-serif",
|
|
'text-align': 'center',
|
|
'user-select': 'none',
|
|
'font-size': '10px',
|
|
'color': 'steelblue',
|
|
'margin-top': '10px',
|
|
'cursor': 'default',
|
|
'opacity': 0.8
|
|
});
|
|
|
|
var self = this;
|
|
|
|
this.elem.css({
|
|
'width': '100%',
|
|
'height': parseInt(this.root_elem.get(0).style.height),
|
|
'user-select': 'none',
|
|
'cursor': 'default'
|
|
});
|
|
this.root_elem.css('height', '100%');
|
|
this.opts = opts || {};
|
|
|
|
this.data = [];
|
|
this.is_touch_device = 'ontouchstart' in document.documentElement;
|
|
this.d3_eventSource = function () {
|
|
var e = d3.event, s;
|
|
while (s = e.sourceEvent) e = s;
|
|
return e;
|
|
}
|
|
|
|
this.last_render = null;
|
|
this.data_changed = false;
|
|
this.setup();
|
|
};
|
|
|
|
Rubix.PieDonut.prototype.setup = function() {
|
|
this._setupOpts();
|
|
this._setupOnce();
|
|
this._setupRedraw();
|
|
|
|
this.draw();
|
|
};
|
|
|
|
|
|
/** @private */
|
|
Rubix.PieDonut.prototype._setupOpts = function() {
|
|
this.opts.theme_style = this.opts.theme_style || 'light';
|
|
this.opts.theme_style_color = (this.opts.theme_style === 'light') ? '#C0D0E0' : '#555';
|
|
this.opts.theme_focus_line_color = (this.opts.theme_style === 'light') ? '#C0D0E0' : '#888';
|
|
|
|
this.opts.legend_color_brightness = this.opts.legend_color_brightness || 0.5;
|
|
this.opts.global_legend_color = this.opts.global_legend_color || false;
|
|
|
|
this.opts.titleColor = this.opts.titleColor || 'steelblue';
|
|
this.opts.subtitleColor = this.opts.subtitleColor || 'steelblue';
|
|
|
|
this.title.css('color', this.opts.titleColor);
|
|
this.subtitle.css('color', this.opts.subtitleColor);
|
|
|
|
if(this.opts.theme_style === 'dark') {
|
|
this.tooltip.css({
|
|
"color": "#aaa",
|
|
"font-weight": "bold",
|
|
"border": "1px solid #222",
|
|
"background-color": "#303030"
|
|
});
|
|
}
|
|
|
|
this.opts.margin = this.opts.margin || {};
|
|
this.margin = {
|
|
top : this.opts.margin.top || 25,
|
|
left : this.opts.margin.left || 25,
|
|
right : this.opts.margin.right || 25,
|
|
bottom : this.opts.margin.bottom || 25
|
|
};
|
|
|
|
this.opts.tooltip = this.opts.tooltip || {};
|
|
this.opts.tooltip.format = this.opts.tooltip.format || {};
|
|
this.tooltipFormatter = {
|
|
format: {
|
|
x: this.opts.tooltip.format.x || '',
|
|
y: this.opts.tooltip.format.y || ''
|
|
}
|
|
}
|
|
|
|
this.resize = this.opts.resize || 'throttled';
|
|
|
|
this.opts.title = this.opts.title || '';
|
|
this.opts.subtitle = this.opts.subtitle || '';
|
|
|
|
this.title.html(this.opts.title);
|
|
this.subtitle.html(this.opts.subtitle);
|
|
if(this.opts.title.length || this.opts.subtitle.length) {
|
|
this.elem.css('margin-top', '-20px');
|
|
}
|
|
(this.opts.title.length) ? this.title.show() : this.title.hide();
|
|
(this.opts.subtitle.length) ? this.subtitle.show() : this.subtitle.hide();
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.PieDonut.prototype._setupOnce = function() {
|
|
// Add Pie Stack data here.
|
|
};
|
|
|
|
Rubix.PieDonut.prototype._setupRedraw = function() {
|
|
var self = this;
|
|
|
|
$(window).on('orientationchange', function() {
|
|
self.draw();
|
|
});
|
|
|
|
$(window).on('rubix.redraw.'+this.master_id, function() {
|
|
self.draw();
|
|
});
|
|
|
|
switch(this.resize) {
|
|
case 'always':
|
|
$(window).on('resize', function() {
|
|
self.draw();
|
|
});
|
|
break;
|
|
case 'debounced':
|
|
$(window).on('debouncedresize', function() {
|
|
self.draw();
|
|
});
|
|
break;
|
|
case 'throttled':
|
|
$(window).on('throttledresize', function() {
|
|
self.draw();
|
|
});
|
|
break;
|
|
default:
|
|
throw new Error('Unknown resize type!');
|
|
break;
|
|
}
|
|
};
|
|
|
|
Rubix.PieDonut.prototype.draw = function() {
|
|
this._draw();
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
Rubix.PieDonut.prototype._draw = function() {
|
|
this._cleanElement();
|
|
this._setSize();
|
|
this._setupCanvas();
|
|
this._setupRoot();
|
|
this._setupSeries();
|
|
this.final_draw();
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.PieDonut.prototype._cleanElement = function() {
|
|
this.elem.children().remove();
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.PieDonut.prototype._setSize = function() {
|
|
this.outerWidth = this.elem.width();
|
|
this.outerHeight = this.elem.height();
|
|
|
|
this.width = this.outerWidth - this.margin.left - this.margin.right;
|
|
this.height = this.outerHeight - this.margin.top - this.margin.bottom;
|
|
|
|
this.innerRadius = 0;
|
|
this.outerRadius = (Math.min(this.width, this.height) * .5) - 10;
|
|
|
|
if(this.type === 'donut') {
|
|
this.innerRadius = this.outerRadius * .6;
|
|
}
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.PieDonut.prototype._setupCanvas = function() {
|
|
this.elem.html('');
|
|
this.canvas = d3.select(this.elem.get(0)).append('svg');
|
|
this.canvas.attr('width', this.outerWidth);
|
|
this.canvas.attr('height', this.outerHeight);
|
|
|
|
this.canvas.append('desc').text("Powered by RubixÆžium");
|
|
};
|
|
|
|
/** @private */
|
|
Rubix.PieDonut.prototype._setupRoot = function() {
|
|
this.root = this.canvas.append('g');
|
|
this.root.attr('width', this.width);
|
|
this.root.attr('height', this.height);
|
|
this.root.attr('transform', 'translate('+this.margin.left+','+this.margin.top+')');
|
|
|
|
if(!this.hasData) {
|
|
this.canvas.append('text').attr('class', 'noData').attr('font-size', '30px').attr('text-anchor', 'middle').attr('transform', 'translate('+this.outerWidth/2+','+this.outerHeight/2+')').attr('font-family', 'Lato, "Lucida Grande", Arial, Helvetica, sans-serif').text("No Data");
|
|
} else {
|
|
this.canvas.select('.noData').style('display', 'none');
|
|
}
|
|
};
|
|
|
|
Rubix.PieDonut.prototype._setupSeries = function() {
|
|
this.root_piedonut_series = this.root.append('g').attr('class', 'piedonut_series').attr('transform', "translate(" + this.width / 2 + "," + this.height / 2 + ")");
|
|
};
|
|
|
|
Rubix.PieDonut.prototype.uid = function(type) {
|
|
return 'rubixcc-pie-donut-' + type +'-' + Math.floor(2147483648*Math.random()).toString(36);
|
|
};
|
|
|
|
Rubix.PieDonut.prototype.addData = function(data) {
|
|
this.data_changed = true;
|
|
data.forEach(function(d) {
|
|
d.value = +d.value;
|
|
});
|
|
|
|
this.data = data;
|
|
|
|
this._setupLegend();
|
|
this.final_draw('launch');
|
|
};
|
|
|
|
Rubix.PieDonut.prototype._setupLegend = function() {
|
|
var self = this;
|
|
this.legend.children().remove();
|
|
for(var i=0; i<this.data.length; i++) {
|
|
var color = this.opts.global_legend_color || this.data[i].color;
|
|
this.legend.append("<div class='"+this.master_id+"-legend-labels' data-name='"+this.data[i].name+"' style='cursor: pointer; display: inline-block; font-weight: bold; margin-right: 10px; color: "+color+"'><span style='font-size: 22px;'>■ </span><span style='font-size: 12px; position:relative; top: -3px; left: -2px;'>"+this.data[i].name+"</span></div>");
|
|
}
|
|
|
|
$('.'+this.master_id+'-legend-labels').css({
|
|
'opacity': 0.9
|
|
});
|
|
|
|
for(var id in this.legend_hidden) {
|
|
var name = this.legend_hidden[id];
|
|
$('[data-name='+name+']').css({
|
|
'opacity': 0.2
|
|
}).addClass('toggle');
|
|
}
|
|
|
|
$('.'+this.master_id+'-legend-labels').off().on({
|
|
'hover': function(e) {
|
|
var hasClass = $(this).hasClass('toggle');
|
|
var name = $(this).attr('data-name');
|
|
var id = '#'+self.master_id+'-'+slugify(name);
|
|
|
|
if(hasClass) return;
|
|
|
|
switch(e.type) {
|
|
case 'mouseenter':
|
|
$(this).css({
|
|
'opacity': 1
|
|
});
|
|
d3.select(id).node().__onmouseover(e);
|
|
break;
|
|
case 'mouseleave':
|
|
$(this).css({
|
|
'opacity': 0.9
|
|
});
|
|
d3.select(id).node().__onmouseout(e)
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
'click': function(e) {
|
|
var hasClass = $(this).hasClass('toggle');
|
|
var name = $(this).attr('data-name');
|
|
var id = '#'+self.master_id+'-'+slugify(name);
|
|
|
|
if(!hasClass) {
|
|
$(this).css({
|
|
'opacity': 0.2
|
|
});
|
|
$(this).addClass('toggle');
|
|
$(id).fadeOut();
|
|
self.legend_hidden[id] = name;
|
|
} else {
|
|
$(this).css({
|
|
'opacity': 1
|
|
});
|
|
$(this).removeClass('toggle');
|
|
$(id).fadeIn();
|
|
delete self.legend_hidden[id];
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
Rubix.PieDonut.prototype.final_draw = function(animate) {
|
|
var self = this;
|
|
if(this.data.length) {
|
|
this.canvas.select('.noData').style('display', 'none');
|
|
}
|
|
var arc = d3.svg.arc();
|
|
arc.innerRadius(this.innerRadius);
|
|
arc.outerRadius(this.outerRadius);
|
|
|
|
var pie = d3.layout.pie();
|
|
pie.sort(null);
|
|
pie.value(function(d) {
|
|
return d.value;
|
|
});
|
|
|
|
var main = this.root_piedonut_series.selectAll('.arc')
|
|
.data(pie(this.data), function(d) {
|
|
return d.data.name;
|
|
});
|
|
|
|
var g = main.enter().append('g')
|
|
.attr('class', 'arc').style('position', 'relative');
|
|
|
|
var color = d3.scale.category20();
|
|
var path = g.attr('id', function(d) {
|
|
return self.master_id+'-'+slugify(d.data.name);
|
|
}).style("fill", function(d, i) { return d.data.color; }).style('stroke', 'white').append("path").attr("d", function(d) {
|
|
return arc({
|
|
startAngle: 0,
|
|
endAngle: 0
|
|
});
|
|
})
|
|
|
|
if(animate === 'launch') {
|
|
this.old_pie_data = pie(this.data);
|
|
setTimeout(function() {
|
|
path.transition().attrTween('d', function(d) {
|
|
var i = d3.interpolate({
|
|
startAngle: 0,
|
|
endAngle: 0
|
|
}, {
|
|
startAngle: d.startAngle,
|
|
endAngle: d.endAngle
|
|
});
|
|
return function(t) {
|
|
return arc(i(t));
|
|
}
|
|
}).duration(500);
|
|
}, 15);
|
|
} else if(animate === 'add') {
|
|
var t = main.transition().duration(500).each('end', function() {
|
|
self.old_pie_data = pie(self.data);
|
|
});
|
|
t.select('path').attrTween('d', function(d, k) {
|
|
var startAngle = 0;
|
|
var endAngle = 0;
|
|
try {
|
|
startAngle = self.old_pie_data[k].startAngle;
|
|
endAngle = self.old_pie_data[k].endAngle;
|
|
} catch(e) {
|
|
startAngle = self.old_pie_data[self.old_pie_data.length-1].startAngle;
|
|
endAngle = self.old_pie_data[self.old_pie_data.length-1].endAngle;
|
|
// do nothing
|
|
}
|
|
var i = d3.interpolate({
|
|
startAngle: startAngle,
|
|
endAngle: endAngle
|
|
}, {
|
|
startAngle: d.startAngle,
|
|
endAngle: d.endAngle
|
|
});
|
|
|
|
if(d.data.hasOwnProperty('_remove')) {
|
|
self.data.splice(k, 1);
|
|
}
|
|
self._setupLegend();
|
|
return function(t) {
|
|
return arc(i(t));
|
|
}
|
|
});
|
|
} else {
|
|
this.old_pie_data = pie(this.data);
|
|
path.attr('d', function(d) {
|
|
return arc(d);
|
|
});
|
|
}
|
|
|
|
var mouseover = 'mouseover',
|
|
mousemove = 'mousemove',
|
|
mouseout = 'mouseout';
|
|
if(this.is_touch_device) {
|
|
mouseover = 'touchstart';
|
|
mousemove = 'touchstart';
|
|
mouseout = 'touchend';
|
|
}
|
|
|
|
var timer;
|
|
|
|
g.on(mouseover, function(d) {
|
|
clearTimeout(timer);
|
|
var centroid = arc.centroid(d);
|
|
var h = Math.sqrt(Math.pow(centroid[0], 2) + Math.pow(centroid[1], 2));
|
|
|
|
var x = centroid[0]/h * 8;
|
|
var y = centroid[1]/h * 8;
|
|
if(!self.is_touch_device) {
|
|
d3.select(this).transition().duration(150).attr('transform', "translate(" + [x, y] + ")");
|
|
}
|
|
});
|
|
|
|
g.on(mousemove, function(d, i) {
|
|
clearTimeout(timer);
|
|
if(self.is_touch_device) {
|
|
var coordinates = d3.touches(this, self.d3_eventSource().changedTouches)[0];
|
|
} else {
|
|
var coordinates = d3.mouse(this);
|
|
}
|
|
self.tooltip.css('display', useTable);
|
|
self.tooltip.css({
|
|
'left': (self.width/2 - self.margin.left/2) + coordinates[0],
|
|
'top' : (self.height/2 - self.margin.top/2) + coordinates[1]
|
|
});
|
|
|
|
if(self.opts.tooltip.customPlaceholder) {
|
|
var tip = self.opts.tooltip.customPlaceholder.replace("%n", d.data.name);
|
|
tip = tip.replace("%v", d.data.value);
|
|
tip = tip.replace("%c", d.data.color);
|
|
self.tooltip.html(tip);
|
|
} else {
|
|
self.tooltip.html("<div style='color: "+d.data.color+";'><b><span style='font-size: 22px;'>■ </span><span style='position:relative; top: -3px; left: -2px;'>"+d.data.name+" :</span></b> <span style='position:relative; top: -3px; left: -2px;'>"+d.data.value+"</span></div>");
|
|
}
|
|
|
|
d3.select(this).style('fill', d.data.color);
|
|
});
|
|
|
|
g.on(mouseout, function(d, i) {
|
|
var $this = this;
|
|
if(self.is_touch_device) {
|
|
timer = setTimeout(function() {
|
|
self.tooltip.hide();
|
|
}, 3000);
|
|
|
|
setTimeout(function() {
|
|
d3.select($this).style('fill', d.data.color);
|
|
}, 3000);
|
|
} else {
|
|
self.tooltip.hide();
|
|
d3.select($this).style('fill', d.data.color);
|
|
d3.select($this).transition().duration(150).attr('transform', 'translate(0, 0)');
|
|
}
|
|
});
|
|
|
|
main.exit().remove();
|
|
|
|
for(var i in this.legend_hidden) {
|
|
$(i).hide();
|
|
}
|
|
};
|
|
|
|
Rubix.PieDonut.prototype.removePoint = function(ref) {
|
|
var removed = false;
|
|
for(var i=0; i<this.data.length; i++) {
|
|
if(this.data[i].name === ref) {
|
|
removed = true;
|
|
this.data[i].value = 0;
|
|
this.data[i]._remove = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!removed) return;
|
|
|
|
this.final_draw('add');
|
|
};
|
|
|
|
Rubix.PieDonut.prototype.updatePoint = function(data) {
|
|
this.addPoint(data);
|
|
};
|
|
|
|
Rubix.PieDonut.prototype.addPoint = function(data) {
|
|
this.data_changed = true;
|
|
var updated = false;
|
|
for(var i=0; i<this.data.length; i++) {
|
|
if(this.data[i].name === data.name) {
|
|
updated = true;
|
|
this.data[i].value = +data.value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!updated) {
|
|
this.data.push(data);
|
|
}
|
|
|
|
this.final_draw('add');
|
|
};
|
|
|
|
window.Rubix = window.Rubix || {};
|
|
|
|
/**
|
|
* @param {Rubix} rubix
|
|
* @param {Object} opts
|
|
* @constructor
|
|
*/
|
|
Rubix.LineSeries = function(rubix, opts) {
|
|
this.opts = opts;
|
|
this.opts.color = this.opts.color || 'steelblue';
|
|
this.opts.marker = this.opts.marker || 'circle';
|
|
this.opts.scatter = this.opts.scatter || false;
|
|
this.opts.noshadow = this.opts.noshadow || false;
|
|
this.show_markers = this.opts.show_markers;
|
|
this.opts.strokewidth = this.opts.strokewidth || 1;
|
|
|
|
if(!this.opts.hasOwnProperty('name')) throw new Error('LineSeries should have a \'name\' property');
|
|
|
|
this.name = this.opts.name;
|
|
|
|
this.chart_hidden = false;
|
|
this.temp_stack = [];
|
|
|
|
this.setup = false;
|
|
this._setup(rubix);
|
|
};
|
|
|
|
/**
|
|
* @param {Rubix} rubix
|
|
* @private
|
|
*/
|
|
Rubix.LineSeries.prototype._setup = function(rubix) {
|
|
this.rubix = rubix;
|
|
|
|
this.root = this.rubix.root;
|
|
this.data = this.rubix.data;
|
|
this.width = this.rubix.width;
|
|
this.height = this.rubix.height;
|
|
this.line_series = this.rubix.root_line_series;
|
|
this.show_markers = (this.show_markers === undefined) ? this.rubix.show_markers : this.show_markers;
|
|
|
|
this.master_detail = this.rubix.master_detail;
|
|
|
|
if(this.master_detail) {
|
|
this.md_root = this.rubix.md_root;
|
|
}
|
|
|
|
if(!this.id) {
|
|
this.id = this.rubix.uid('line');
|
|
}
|
|
|
|
/** separator */
|
|
var self = this;
|
|
this.line = d3.svg.line();
|
|
this.line.defined(function(d) {
|
|
return d.x !== null && d.y !== null;
|
|
});
|
|
this.line.x(function(d) {
|
|
if(self.rubix.opts.invertAxes) {
|
|
return self.rubix.x(d.y);
|
|
}
|
|
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
return self.rubix.x(d.x) + self.rubix.x.rangeBand()/2;
|
|
}
|
|
return self.rubix.x(d.x);
|
|
});
|
|
this.line.y(function(d) {
|
|
if(self.rubix.opts.invertAxes) {
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
return self.rubix.y(d.x) + self.rubix.y.rangeBand()/2;
|
|
}
|
|
return self.rubix.y(d.x);
|
|
}
|
|
return self.rubix.y(d.y);
|
|
});
|
|
this.line.interpolate(this.rubix.interpolate);
|
|
|
|
if(this.master_detail) {
|
|
this.master_line = d3.svg.line();
|
|
this.master_line.defined(function(d) {
|
|
return d.x !== null && d.y !== null;
|
|
});
|
|
this.master_line.x(function(d) {
|
|
if(self.rubix.opts.invertAxes) {
|
|
return self.rubix.x2(d.y);
|
|
}
|
|
return self.rubix.x2(d.x);
|
|
});
|
|
this.master_line.y(function(d) {
|
|
if(self.rubix.opts.invertAxes) {
|
|
return self.rubix.y2(d.x);
|
|
}
|
|
return self.rubix.y2(d.y);
|
|
});
|
|
this.master_line.interpolate(this.rubix.interpolate);
|
|
}
|
|
};
|
|
|
|
// Alias for draw
|
|
Rubix.LineSeries.prototype.redraw = function(rubix) {
|
|
this._setup(rubix);
|
|
this.draw();
|
|
};
|
|
|
|
// Alias for draw
|
|
Rubix.LineSeries.prototype.noRedraw = function(rubix) {
|
|
this._setup(rubix);
|
|
this.draw(true);
|
|
};
|
|
|
|
/**
|
|
* @param {Array|Object} data
|
|
*/
|
|
Rubix.LineSeries.prototype.addData = function(data) {
|
|
this.rubix.data_changed = true;
|
|
if(!(data instanceof Array)) {
|
|
if(!(data instanceof Object)) {
|
|
throw new Error("Data must be an array or object");
|
|
} else {
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
data = [data];
|
|
}
|
|
}
|
|
|
|
if(!data.length) return;
|
|
|
|
if(!this.rubix.opts.noSort) {
|
|
data.sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.data[this.name] = data;
|
|
|
|
this.rubix.resetAxis(false);
|
|
this.rubix.forceRedraw();
|
|
this._animate_draw();
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.draw = function(forced) {
|
|
if(!this.name) return;
|
|
if(!this.data) return;
|
|
if(!this.data.hasOwnProperty(this.name)) return;
|
|
if(!this.data[this.name].length) return;
|
|
|
|
var self = this;
|
|
|
|
var isConstructed = this.line_series.selectAll('.'+this.id)[0].length;
|
|
|
|
if(!isConstructed) {
|
|
this.line_series.selectAll('.'+this.id+'.clipped').remove();
|
|
if(!this.opts.scatter) {
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1 = this.line_series.append('path').attr('class', this.id+' line').datum(this.data[this.name]).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 5 * this.opts.strokewidth).attr('stroke-opacity', 0.05000000000000001).attr('transform', 'translate(1,1)');
|
|
this.strokePath2 = this.line_series.append('path').attr('class', this.id+' line').datum(this.data[this.name]).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 3 * this.opts.strokewidth).attr('stroke-opacity', 0.1).attr('transform', 'translate(1,1)');
|
|
this.strokePath3 = this.line_series.append('path').attr('class', this.id+' line').datum(this.data[this.name]).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 1 * this.opts.strokewidth).attr('stroke-opacity', 0.15000000000000002).attr('transform', 'translate(1,1)');
|
|
}
|
|
this.linePath = this.line_series.append('path').datum(this.data[this.name]).attr('class', this.id+' line').attr('stroke', this.opts.color).attr('fill', 'none').attr('stroke-linecap', 'round').attr('d', this.line).attr('stroke-width', 2 * this.opts.strokewidth);
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
if(!this.opts.scatter) {
|
|
this.md_root.select('.md-layers > .md_line_series').selectAll('.'+this.id+'.clipped').remove();
|
|
this.masterLinePath = this.md_root.select('.md-layers > .md_line_series').append('path').datum(this.data[this.name]).attr('class', this.id+' line').attr('stroke', this.opts.color).attr('fill', 'none').attr('stroke-linecap', 'round').attr('d', this.master_line).attr('stroke-width', 2 * this.opts.strokewidth);
|
|
this.masterLinePath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.masterLinePath.classed('clipped', true);
|
|
} else {
|
|
this.md_root.select('.md-layers > .md_line_series').attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
var self = this;
|
|
var datum = this.data[this.name];
|
|
this.markers_master = this.md_root.select('.md-layers > .md_line_series').selectAll('.'+this.id+'-marker');
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(this.opts.marker);
|
|
|
|
var symbolType = this.markers_master.data(datum);
|
|
symbolType.enter().append('path')
|
|
.attr('d', symbol)
|
|
.attr('class', this.id + '-marker')
|
|
.attr('fill', this.opts.color)
|
|
.style('display', function(d) {
|
|
if(d.y === null) {
|
|
return 'none';
|
|
}
|
|
return null;
|
|
})
|
|
.attr('stroke', 'none')
|
|
.attr('transform', function(d) {
|
|
var val = d.y;
|
|
if(isNaN(val)) {
|
|
val = 0;
|
|
}
|
|
if(self.rubix.opts.invertAxes) {
|
|
var _y = self.rubix.y(d.x);
|
|
var _x = self.rubix.x(val);
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
_y += self.rubix.y.rangeBand()/2;
|
|
}
|
|
} else {
|
|
var _y = self.rubix.y2(val);
|
|
var _x = self.rubix.x2(d.x);
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
_x += self.rubix.x.rangeBand()/2;
|
|
}
|
|
}
|
|
return 'translate('+_x+','+_y+')';
|
|
});
|
|
symbolType.exit().remove();
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(this.dataChanged) {
|
|
this.rubix.resetExtent();
|
|
this.dataChanged = false;
|
|
}
|
|
|
|
if(!this.opts.scatter) {
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.strokePath2.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.strokePath3.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
}
|
|
this.linePath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1.classed('clipped', true);
|
|
this.strokePath2.classed('clipped', true);
|
|
this.strokePath3.classed('clipped', true);
|
|
}
|
|
this.linePath.classed('clipped', true);
|
|
}
|
|
}
|
|
|
|
this.setup = true;
|
|
} else {
|
|
this.rubix.runCommand('globalRedraw');
|
|
}
|
|
|
|
this.rubix.resetFocus(forced);
|
|
};
|
|
|
|
/**
|
|
* @param {Array|Object} data
|
|
*/
|
|
Rubix.LineSeries.prototype.update = function(data) {
|
|
if(!(data instanceof Array)) {
|
|
if(!(data instanceof Object)) {
|
|
throw new Error("Data must be an array or object");
|
|
} else {
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
data = [data];
|
|
}
|
|
}
|
|
|
|
if(!this.rubix.opts.noSort) {
|
|
data.sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.data[this.name] = data;
|
|
|
|
if(this.setup) {
|
|
this._animate_draw();
|
|
} else {
|
|
this.rubix.resetAxis(false);
|
|
this.rubix.forceRedraw();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Alias for addPoint
|
|
* @param {Object} data
|
|
* @param {?Boolean} shift
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.LineSeries.prototype.updatePoint = function(data, shift, noRedraw) {
|
|
this.addPoint(data, shift, noRedraw);
|
|
};
|
|
|
|
/**
|
|
* @param {*} ref
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.LineSeries.prototype.removePoint = function(ref, noRedraw) {
|
|
if(this.chart_hidden) {
|
|
this.temp_stack.push({
|
|
type: 'removePoint',
|
|
ref: ref
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(!this.data[this.name]) {
|
|
this.data[this.name] = [];
|
|
}
|
|
|
|
var removed = false;
|
|
for(var i=0; i<this.data[this.name].length; i++) {
|
|
if(this.data[this.name][i].x === ref) {
|
|
this.data[this.name].splice(i, 1);
|
|
removed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.dataChanged = true;
|
|
}
|
|
|
|
if(noRedraw) return;
|
|
|
|
if(this.setup) {
|
|
this._animate_draw();
|
|
} else {
|
|
this.rubix.resetAxis(true);
|
|
this.rubix.forceRedraw();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {Object} data
|
|
* @param {?Boolean} shift
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.LineSeries.prototype.addPoint = function(data, shift, noRedraw) {
|
|
this.rubix.data_changed = true;
|
|
if(!(data instanceof Object) || (data instanceof Array)) {
|
|
throw new Error("Object required for addPoint");
|
|
}
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
if(this.chart_hidden) {
|
|
this.temp_stack.push({
|
|
type: 'addPoint',
|
|
data: data,
|
|
shift: shift
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(!this.data[this.name]) {
|
|
this.data[this.name] = [];
|
|
}
|
|
|
|
var added = false;
|
|
for(var i=0; i<this.data[this.name].length; i++) {
|
|
if(this.data[this.name][i].x === data.x) {
|
|
this.data[this.name][i].y = data.y;
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!added) {
|
|
this.data[this.name].push(data);
|
|
}
|
|
|
|
this.data[this.name].sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
|
|
if(this.rubix.opts.interval) {
|
|
if(this.rubix.opts.interval < this.data[this.name].length) {
|
|
this.data[this.name].shift();
|
|
}
|
|
} else {
|
|
if(shift) {
|
|
this.data[this.name].shift();
|
|
}
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.dataChanged = true;
|
|
}
|
|
|
|
if(noRedraw) return;
|
|
if(this.setup) {
|
|
this._animate_draw();
|
|
} else {
|
|
this.rubix.resetAxis(true);
|
|
this.rubix.forceRedraw();
|
|
}
|
|
};
|
|
|
|
Rubix.LineSeries.prototype._animate_draw = function() {
|
|
var text = this.root.selectAll('.y.axis').selectAll('text')[0];
|
|
var width = [];
|
|
for(var i=0; i<text.length; i++) {
|
|
width.push(text[i].getBBox().width);
|
|
}
|
|
var origMaxWidth = d3.max(width);
|
|
|
|
this.rubix.resetAxis(true);
|
|
|
|
this.rubix.runCommand('globalRedraw');
|
|
|
|
text = this.root.selectAll('.y.axis').selectAll('text')[0];
|
|
width = [];
|
|
for(var i=0; i<text.length; i++) {
|
|
width.push(text[i].getBBox().width);
|
|
}
|
|
|
|
var maxWidth = d3.max(width);
|
|
|
|
this.rubix.marginLeft(maxWidth);
|
|
this.rubix.resetFocus();
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.hidden = function() {
|
|
this.chart_hidden = true;
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.show = function() {
|
|
this.chart_hidden = false;
|
|
while(this.temp_stack.length>1) {
|
|
var rawData = this.temp_stack.shift();
|
|
if(rawData.type === 'addPoint') {
|
|
this.addPoint(rawData.data, rawData.shift, true);
|
|
} else {
|
|
this.removePoint(rawData.ref);
|
|
}
|
|
}
|
|
if(this.temp_stack.length) {
|
|
var rawData = this.temp_stack.shift();
|
|
if(rawData.type === 'addPoint') {
|
|
this.addPoint(rawData.data, rawData.shift, true);
|
|
} else {
|
|
this.removePoint(rawData.ref);
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.globalRedraw = function(rubix) {
|
|
this.line_series.selectAll('.'+this.id+'.line').attr('d', this.line);
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.forceRedraw = function(rubix) {
|
|
this.redraw(rubix);
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.on_complete_draw = function() {
|
|
if(!this.setup) return;
|
|
var self = this;
|
|
var datum = this.data[this.name];
|
|
if(this.opts.marker !== undefined && this.show_markers) {
|
|
if(this.master_detail) {
|
|
this.rubix.symbols.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip-symbols)');
|
|
}
|
|
this.markers = this.rubix.symbols.selectAll('.'+this.id+'-marker');
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(this.opts.marker);
|
|
|
|
var symbolType = this.markers.data(datum);
|
|
symbolType.enter().append('path')
|
|
.attr('d', symbol)
|
|
.attr('class', this.id + '-marker')
|
|
.attr('fill', this.opts.color)
|
|
.style('display', function(d) {
|
|
if(d.y === null) {
|
|
return 'none';
|
|
}
|
|
return null;
|
|
})
|
|
.attr('stroke', 'white')
|
|
.attr('transform', function(d) {
|
|
var val = d.y;
|
|
if(isNaN(val)) {
|
|
val = 0;
|
|
}
|
|
if(self.rubix.opts.invertAxes) {
|
|
var _y = self.rubix.y(d.x);
|
|
var _x = self.rubix.x(val);
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
_y += self.rubix.y.rangeBand()/2;
|
|
}
|
|
} else {
|
|
var _y = self.rubix.y(val);
|
|
var _x = self.rubix.x(d.x);
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
_x += self.rubix.x.rangeBand()/2;
|
|
}
|
|
}
|
|
return 'translate('+_x+','+_y+')';
|
|
});
|
|
symbolType.exit().remove();
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.setupFocus = function() {
|
|
if(!this.setup) return;
|
|
if(this.focus) this.focus.remove();
|
|
this.focus = this.rubix.focus_group.append('g');
|
|
this.focus.attr('class', 'focus');
|
|
this.focus.style('display', 'none');
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(this.opts.marker);
|
|
var path = this.focus.append('path').attr('d', symbol).attr('fill', this.opts.color).attr('stroke', 'white').attr('stroke-width', 2);
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.on_focus = function() {
|
|
if(!this.setup) return;
|
|
this.linePath.attr('stroke-width', 2 * this.opts.strokewidth);
|
|
};
|
|
|
|
Rubix.LineSeries.prototype.off_focus = function() {
|
|
if(!this.setup) return;
|
|
this.linePath.attr('stroke-width', 2 * this.opts.strokewidth);
|
|
};
|
|
window.Rubix = window.Rubix || {};
|
|
|
|
/**
|
|
* @param {Rubix} rubix
|
|
* @param {Object} opts
|
|
* @constructor
|
|
*/
|
|
Rubix.ColumnSeries = function(rubix, opts) {
|
|
this.type = 'column_series';
|
|
this.opts = opts;
|
|
this.opts.color = this.opts.color || 'steelblue';
|
|
this.opts.marker = this.opts.marker || 'circle';
|
|
this.opts.nostroke = this.opts.nostroke || '';
|
|
this.opts.fillopacity = this.opts.fillopacity || 0.85;
|
|
this.show_markers = this.opts.show_markers;
|
|
|
|
if(!this.opts.hasOwnProperty('name')) throw new Error('ColumnSeries should have a \'name\' property');
|
|
|
|
this.name = this.opts.name;
|
|
|
|
this.chart_hidden = false;
|
|
this.temp_stack = [];
|
|
|
|
this.setup = false;
|
|
this.last_stack = [];
|
|
this.last_stack_name = '';
|
|
this.last_stack_added = false;
|
|
|
|
this.count = 0;
|
|
|
|
this._setup(rubix);
|
|
};
|
|
|
|
/**
|
|
* @param {Rubix} rubix
|
|
* @private
|
|
*/
|
|
Rubix.ColumnSeries.prototype._setup = function(rubix) {
|
|
this.rubix = rubix;
|
|
this.rubix.stacked = true;
|
|
|
|
this.root = this.rubix.root;
|
|
this.data = this.rubix.data;
|
|
this.width = this.rubix.width;
|
|
this.height = this.rubix.height;
|
|
this.stack = this.rubix.column_stack;
|
|
this.offset = this.rubix.column_offset;
|
|
this.cb_series = this.rubix.root_cb_series;
|
|
this.column_stack = this.rubix.column_stack_data;
|
|
this.show_markers = (this.show_markers === undefined) ? this.rubix.show_markers : this.show_markers;
|
|
|
|
this.grouped = this.rubix.grouped;
|
|
|
|
if(!this.id) {
|
|
this.id = this.rubix.uid('stacked-column');
|
|
}
|
|
};
|
|
|
|
// Alias for draw
|
|
Rubix.ColumnSeries.prototype.redraw = function(rubix) {
|
|
this._setup(rubix);
|
|
this.draw();
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.noRedraw = function(rubix) {
|
|
this._setup(rubix);
|
|
this.draw(true);
|
|
};
|
|
|
|
/**
|
|
* @param {Array|Object} data
|
|
*/
|
|
Rubix.ColumnSeries.prototype.addData = function(data) {
|
|
this.rubix.data_changed = true;
|
|
if(!(data instanceof Array)) {
|
|
if(!(data instanceof Object)) {
|
|
throw new Error("Data must be an array or object");
|
|
} else {
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
data = [data];
|
|
}
|
|
}
|
|
|
|
if(!data.length) return;
|
|
|
|
if(!this.rubix.opts.noSort) {
|
|
data.sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
var maxLen = 0, columns = [];
|
|
for(var i in this.data) {
|
|
var len = this.data[i].length;
|
|
if(len > maxLen) {
|
|
maxLen = len;
|
|
columns = [];
|
|
for(var k=0; k<maxLen; k++) {
|
|
columns.push(this.data[i][k].x);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.rubix.maxLen = maxLen;
|
|
|
|
this.data[this.name] = data;
|
|
|
|
for(var i in this.data) {
|
|
var len = this.data[i].length;
|
|
if(len < maxLen) {
|
|
var dup_columns = columns.concat();
|
|
for(var j=0; j<this.data[i].length; j++) {
|
|
var column_index = dup_columns.indexOf(this.data[i][j].x);
|
|
dup_columns.splice(column_index, 1);
|
|
}
|
|
for(var j=0; j<dup_columns.length; j++) {
|
|
this.rubix.charts[i].addPoint({
|
|
x: dup_columns[j],
|
|
y: null
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
this.column_stack.push({
|
|
key: this.name,
|
|
color: this.opts.color,
|
|
marker: this.opts.marker,
|
|
values: this.data[this.name],
|
|
orderKey: this.column_stack.length
|
|
});
|
|
|
|
this.rubix.resetAxis();
|
|
this.rubix.forceRedraw();
|
|
this._animate_draw();
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype._createRect = function() {
|
|
var self = this;
|
|
var strokecolor = 'white';
|
|
if(this.opts.nostroke) strokecolor = 'none';
|
|
var rect = this.columnGroup.selectAll('rect').data(function(d) { return d.values; }).enter().append('rect').attr('stroke', strokecolor);
|
|
if(this.grouped) {
|
|
if(this.rubix.opts.invertAxes) {
|
|
rect.attr('x', function(d, i, j) {
|
|
var val = self.rubix.x(0);
|
|
if(isNaN(val)) {
|
|
return null;
|
|
}
|
|
return val;
|
|
});
|
|
rect.attr('y', function(d, i, j) {
|
|
return self.rubix.y(d.x) + self.rubix.y.rangeBand() / self.layers.length * j;
|
|
});
|
|
rect.attr('class', function(d, i, j) {
|
|
return 'column-' + ((self.rubix.y(d.x) + (self.rubix.y.rangeBand()/self.layers.length) * (self.layers.length-1)) + self.rubix.y.rangeBand()/(2*self.layers.length));
|
|
});
|
|
rect.attr("width", function(d) {
|
|
var val = Math.abs(self.rubix.x(d.y) - self.rubix.x(0));
|
|
if(isNaN(val)) {
|
|
return null;
|
|
}
|
|
return val;
|
|
});
|
|
rect.attr('height', self.rubix.y.rangeBand() / self.layers.length);
|
|
rect.attr('transform', function(d) {
|
|
var val = self.rubix.x(d.y_new) - self.rubix.x(0);
|
|
if(d.y < 0) {
|
|
return 'translate('+(-Math.abs(val))+',0)';
|
|
} else {
|
|
return 'translate(0,0)';
|
|
}
|
|
});
|
|
} else {
|
|
rect.attr('x', function(d, i, j) {
|
|
return self.rubix.x(d.x) + self.rubix.x.rangeBand() / self.layers.length * j;
|
|
});
|
|
rect.attr('y', function(d) {
|
|
var val = self.rubix.y(0);
|
|
if(isNaN(val)) {
|
|
return null;
|
|
}
|
|
return val;
|
|
});
|
|
rect.attr('class', function(d, i, j) {
|
|
return 'column-' + ((self.rubix.x(d.x) + (self.rubix.x.rangeBand()/self.layers.length) * (self.layers.length-1)) + self.rubix.x.rangeBand()/(2*self.layers.length));
|
|
});
|
|
rect.attr("width", self.rubix.x.rangeBand() / self.layers.length)
|
|
rect.attr('height', function(d) {
|
|
var val = Math.abs(self.rubix.y(d.y) - self.rubix.y(0));
|
|
if(isNaN(val)) {
|
|
return null;
|
|
}
|
|
return val;
|
|
});
|
|
rect.attr('transform', function(d) {
|
|
var val = self.rubix.y(d.y_new) - self.rubix.y(0);
|
|
if(d.y > 0) {
|
|
return 'translate(0,'+(-Math.abs(val))+')';
|
|
} else {
|
|
return 'translate(0,0)';
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
if(this.rubix.opts.invertAxes) {
|
|
rect.attr('x', function(d) {
|
|
var val = self.rubix.x(d.y0);
|
|
if(isNaN(val)) {
|
|
return null;
|
|
}
|
|
return val;
|
|
});
|
|
rect.attr('y', function(d) {
|
|
return self.rubix.y(d.x) || 0;
|
|
});
|
|
rect.attr('class', function(d) {
|
|
return 'column-' + (self.rubix.y(d.x) + self.rubix.y.rangeBand()/2);
|
|
});
|
|
rect.attr("width", function(d) {
|
|
var val = Math.abs(self.rubix.x(d.y0)-self.rubix.x(d.y+d.y0));
|
|
if(isNaN(val)) {
|
|
return null;
|
|
}
|
|
return val;
|
|
});
|
|
rect.attr('height', self.rubix.y.rangeBand());
|
|
rect.attr('transform', function(d) {
|
|
if(d.y < 0) {
|
|
var val = Math.abs(self.rubix.x(d.y0)-self.rubix.x(d.y+d.y0));
|
|
return 'translate('+(-val)+',0)';
|
|
}
|
|
return null;
|
|
});
|
|
} else {
|
|
rect.attr('x', function(d) {
|
|
return self.rubix.x(d.x);
|
|
});
|
|
rect.attr('y', function(d) {
|
|
var val = self.rubix.y(d.y0);
|
|
if(isNaN(val)) {
|
|
return null;
|
|
}
|
|
return val;
|
|
});
|
|
rect.attr('class', function(d) {
|
|
return 'column-' + ((self.rubix.x(d.x) + self.rubix.x.rangeBand()/2));
|
|
});
|
|
rect.attr("width", self.rubix.x.rangeBand())
|
|
rect.attr('height', function(d) {
|
|
var val = Math.abs(self.rubix.y(d.y0)-self.rubix.y(d.y+d.y0));
|
|
if(isNaN(val)) {
|
|
return null;
|
|
}
|
|
return val;
|
|
});
|
|
rect.attr('transform', function(d) {
|
|
if(d.y > 0) {
|
|
var val = Math.abs(self.rubix.y(d.y0)-self.rubix.y(d.y+d.y0));
|
|
return 'translate(0,'+(-val)+')';
|
|
}
|
|
return null;
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.draw = function(forced) {
|
|
try {
|
|
if(!this.name) return;
|
|
if(!this.data) return;
|
|
if(!this.data.hasOwnProperty(this.name)) return;
|
|
if(!this.data[this.name].length) return;
|
|
|
|
var oldLayers = this.layers;
|
|
|
|
try {
|
|
this.layers = this.stack(this.column_stack);
|
|
} catch(e) {
|
|
// data un-available. retaining old layer.
|
|
this.layers = oldLayers;
|
|
}
|
|
|
|
if(!this.grouped) {
|
|
var max = this.rubix.maxLen;
|
|
for(var i=0; i<max; i++) {
|
|
var x = this.layers[0].values[i].x;
|
|
var ceiling = null;
|
|
for(var j=0; j<this.layers.length; j++) {
|
|
for(var k=0; k<max; k++) {
|
|
try {
|
|
if(this.layers[j].values[k].x === x) {
|
|
if(this.layers[j].values[k].y >= 0) {
|
|
if(this.layers[j].values[k].y === null) {
|
|
this.layers[j].values[k].y0 = null;
|
|
} else {
|
|
if(ceiling === null) ceiling = 0;
|
|
this.layers[j].values[k].y0 = ceiling;
|
|
ceiling += this.layers[j].values[k].y;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} catch(e) {
|
|
this.layers[j].values.push({
|
|
x: x,
|
|
y: null,
|
|
y0: null,
|
|
y_new: null
|
|
});
|
|
}
|
|
}
|
|
}
|
|
ceiling = null;
|
|
for(var j=this.layers.length-1; j>=0; j--) {
|
|
for(var k=0; k<max; k++) {
|
|
try {
|
|
if(this.layers[j].values[k].x === x) {
|
|
if(this.layers[j].values[k].y < 0) {
|
|
if(this.layers[j].values[k].y === null) {
|
|
this.layers[j].values[k].y0 = null;
|
|
} else {
|
|
if(ceiling === null) ceiling = 0;
|
|
this.layers[j].values[k].y0 = -ceiling;
|
|
ceiling += Math.abs(this.layers[j].values[k].y);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} catch(e) {
|
|
this.layers[j].values.push({
|
|
x: x,
|
|
y: null,
|
|
y0: null,
|
|
y_new: null
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var self = this;
|
|
var isConstructed = this.cb_series.selectAll('.'+this.id)[0].length;
|
|
if(!isConstructed) {
|
|
try {
|
|
this.cb_series.selectAll('.column-layer').remove();
|
|
var p = this.cb_series.selectAll('.column-layer').data(this.layers, function(d, i) {
|
|
if(d.key === self.name) {
|
|
self.count = i;
|
|
}
|
|
return d.key;
|
|
});
|
|
|
|
this.columnGroup = p.enter().append('g');
|
|
this.columnGroup.attr('class', 'column-layer')
|
|
.attr('fill', function(d) { return d.color; }).attr('fill-opacity', this.opts.fillopacity);
|
|
|
|
this._createRect();
|
|
|
|
p.exit().remove();
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
this.setup = true;
|
|
} else {
|
|
this.rubix.runCommand('globalRedraw');
|
|
}
|
|
|
|
this.rubix.resetFocus(forced);
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {Array|Object} data
|
|
*/
|
|
Rubix.ColumnSeries.prototype.update = function(data) {
|
|
if(!(data instanceof Array)) {
|
|
if(!(data instanceof Object)) {
|
|
throw new Error("Data must be an array or object");
|
|
} else {
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
data = [data];
|
|
}
|
|
}
|
|
|
|
if(!this.rubix.opts.noSort) {
|
|
data.sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.data[this.name] = data;
|
|
|
|
if(this.setup) {
|
|
this._animate_draw();
|
|
} else {
|
|
this.rubix.resetAxis(true);
|
|
this.rubix.forceRedraw();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {Object} data
|
|
* @param {?Boolean} shift
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.ColumnSeries.prototype.updatePoint = function(data, shift, noRedraw) {
|
|
this.addPoint(data, shift, noRedraw);
|
|
};
|
|
|
|
/**
|
|
* @param {*} ref
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.ColumnSeries.prototype.removePoint = function(ref, noRedraw) {
|
|
if(this.chart_hidden) {
|
|
this.temp_stack.push({
|
|
type: 'removePoint',
|
|
ref: ref
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(!this.data[this.name]) {
|
|
this.data[this.name] = [];
|
|
}
|
|
|
|
var removed = false, pos = 0;
|
|
for(var i=0; i<this.data[this.name].length; i++) {
|
|
if(this.data[this.name][i].x === ref) {
|
|
this.data[this.name][i].y = null;
|
|
this.data[this.name][i].y0 = null;
|
|
this.data[this.name][i].y_new = null;
|
|
pos = i;
|
|
removed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!removed) return;
|
|
|
|
var found = false;
|
|
for(var i=0; i<this.column_stack.length; i++) {
|
|
var st = this.column_stack[i];
|
|
try {
|
|
if(st.values[pos].x === ref) {
|
|
if(st.values[pos].y !== null) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
}
|
|
|
|
if(!found) {
|
|
for(var i=0; i<this.column_stack.length; i++) {
|
|
this.column_stack[i].values.splice(pos, 1);
|
|
}
|
|
}
|
|
|
|
var maxLen = 0;
|
|
for(var i in this.data) {
|
|
var len = this.data[i].length;
|
|
if(len > maxLen) {
|
|
maxLen = len;
|
|
}
|
|
}
|
|
|
|
this.rubix.maxLen = maxLen;
|
|
|
|
if(noRedraw) return;
|
|
|
|
this.rubix.resetAxis();
|
|
this.rubix.forceRedraw();
|
|
this._animate_draw();
|
|
};
|
|
|
|
/**
|
|
* @param {Object} data
|
|
* @param {?Boolean} shift
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.ColumnSeries.prototype.addPoint = function(data, shift, noRedraw) {
|
|
this.rubix.data_changed = true;
|
|
if(!(data instanceof Object) || (data instanceof Array)) {
|
|
throw new Error("Object required for addPoint");
|
|
}
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
if(this.chart_hidden) {
|
|
this.temp_stack.push({
|
|
type: 'addPoint',
|
|
data: data,
|
|
shift: shift
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(!this.data[this.name]) {
|
|
this.data[this.name] = [];
|
|
}
|
|
|
|
var added = false;
|
|
for(var i=0; i<this.data[this.name].length; i++) {
|
|
if(this.data[this.name][i].x === data.x) {
|
|
this.data[this.name][i].y = data.y;
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!added) {
|
|
this.data[this.name].push(data);
|
|
}
|
|
|
|
this.data[this.name].sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
|
|
added = false;
|
|
for(var i=0; i<this.column_stack.length; i++) {
|
|
if(this.column_stack[i].key === this.name) {
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(this.rubix.opts.interval) {
|
|
if(this.rubix.opts.interval < this.data[this.name].length) {
|
|
this.data[this.name].shift();
|
|
}
|
|
} else {
|
|
if(shift) {
|
|
this.data[this.name].shift();
|
|
}
|
|
}
|
|
|
|
var maxLen = 0, columns = [];
|
|
for(var i in this.data) {
|
|
var len = this.data[i].length;
|
|
if(len > maxLen) {
|
|
maxLen = len;
|
|
columns = [];
|
|
for(var k=0; k<maxLen; k++) {
|
|
columns.push(this.data[i][k].x);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.rubix.maxLen = maxLen;
|
|
|
|
for(var i in this.data) {
|
|
var len = this.data[i].length;
|
|
if(len < maxLen) {
|
|
var dup_columns = columns.concat();
|
|
for(var j=0; j<this.data[i].length; j++) {
|
|
var column_index = dup_columns.indexOf(this.data[i][j].x);
|
|
dup_columns.splice(column_index, 1);
|
|
}
|
|
for(var j=0; j<dup_columns.length; j++) {
|
|
this.rubix.charts[i].addPoint({
|
|
x: dup_columns[j],
|
|
y: null
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if(noRedraw) return;
|
|
|
|
this.rubix.resetAxis();
|
|
this.rubix.forceRedraw();
|
|
this._animate_draw();
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype._animate_draw = function() {
|
|
try {
|
|
var text = this.root.selectAll('.y.axis').selectAll('text')[0];
|
|
var width = [];
|
|
for(var i=0; i<text.length; i++) {
|
|
width.push(text[i].getBBox().width);
|
|
}
|
|
var origMaxWidth = d3.max(width);
|
|
|
|
this.rubix.resetAxis(true);
|
|
|
|
this.rubix.runCommand('globalRedraw');
|
|
|
|
text = this.root.selectAll('.y.axis').selectAll('text')[0];
|
|
width = [];
|
|
for(var i=0; i<text.length; i++) {
|
|
width.push(text[i].getBBox().width);
|
|
}
|
|
|
|
var maxWidth = d3.max(width);
|
|
|
|
this.rubix.marginLeft(maxWidth);
|
|
this.rubix.resetFocus();
|
|
} catch(e) {
|
|
// do nothing
|
|
}
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.hidden = function() {
|
|
this.chart_hidden = true;
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.show = function() {
|
|
this.chart_hidden = false;
|
|
while(this.temp_stack.length>1) {
|
|
var rawData = this.temp_stack.shift();
|
|
if(rawData.type === 'addPoint') {
|
|
this.addPoint(rawData.data, rawData.shift, true);
|
|
} else {
|
|
this.removePoint(rawData.ref);
|
|
}
|
|
}
|
|
if(this.temp_stack.length) {
|
|
var rawData = this.temp_stack.shift();
|
|
if(rawData.type === 'addPoint') {
|
|
this.addPoint(rawData.data, rawData.shift, true);
|
|
} else {
|
|
this.removePoint(rawData.ref);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
Rubix.ColumnSeries.prototype.globalRedraw = function(rubix) {
|
|
// do nothing
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.forceRedraw = function(rubix) {
|
|
this.redraw(rubix);
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.on_complete_draw = function() {
|
|
if(!this.setup) return;
|
|
var strokecolor = 'white';
|
|
var self = this;
|
|
if(this.opts.marker !== undefined && this.show_markers) {
|
|
this.markers = this.rubix.symbols;
|
|
|
|
this.markers.selectAll('.column-symbols').remove();
|
|
var p = this.markers.selectAll('.column-symbols').data(this.layers, function(d) {
|
|
return d.key;
|
|
});
|
|
|
|
var symbolGroup = p.enter().append('g');
|
|
symbolGroup.attr('class', 'column-symbols');
|
|
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbolPath = symbolGroup.selectAll('path').data(function(d) {
|
|
for(var i=0; i<d.values.length; i++) {
|
|
d.values[i].marker = d.marker;
|
|
d.values[i].color = d.color;
|
|
}
|
|
return d.values;
|
|
}).enter().append('path');
|
|
symbolPath
|
|
.attr('d', function(d) {
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(d.marker);
|
|
return symbol();
|
|
})
|
|
.attr('fill', function(d) {
|
|
return d.color;
|
|
})
|
|
.style('display', function(d) {
|
|
if(self.rubix.column_offset === 'expand') {
|
|
if(d.y_new === 0 && d.y0 === 1) {
|
|
return 'none';
|
|
}
|
|
return null;
|
|
}
|
|
if(d.y_new === null) {
|
|
return 'none';
|
|
}
|
|
return null;
|
|
})
|
|
.attr('stroke', strokecolor)
|
|
if(this.rubix.opts.invertAxes) {
|
|
symbolPath.attr('transform', function(d, i, j) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
val = 0;
|
|
}
|
|
var _y = self.rubix.x(val);
|
|
if(self.grouped) {
|
|
_y = self.rubix.x(d.y_new);
|
|
}
|
|
var _x = self.rubix.y(d.x) + self.rubix.y.rangeBand()/2;
|
|
if(self.grouped) {
|
|
_x = (self.rubix.y(d.x) + (self.rubix.y.rangeBand()/self.layers.length) * j) + self.rubix.y.rangeBand()/(2*self.layers.length);
|
|
}
|
|
return 'translate('+_y+','+_x+')';
|
|
});
|
|
} else {
|
|
symbolPath.attr('transform', function(d, i, j) {
|
|
var val = d.y0 + d.y_new;
|
|
if(isNaN(val)) {
|
|
val = 0;
|
|
}
|
|
var _y = self.rubix.y(val);
|
|
if(self.grouped) {
|
|
_y = self.rubix.y(d.y_new);
|
|
}
|
|
var _x = self.rubix.x(d.x) + self.rubix.x.rangeBand()/2;
|
|
if(self.grouped) {
|
|
_x = (self.rubix.x(d.x) + (self.rubix.x.rangeBand()/self.layers.length) * j) + self.rubix.x.rangeBand()/(2*self.layers.length);
|
|
}
|
|
return 'translate('+_x+','+_y+')';
|
|
});
|
|
}
|
|
p.exit().remove();
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.setupFocus = function() {
|
|
if(!this.setup) return;
|
|
if(this.focus) this.focus.remove();
|
|
this.focus = this.rubix.focus_group.append('g');
|
|
this.focus.attr('class', 'focus');
|
|
this.focus.style('display', 'none');
|
|
var strokecolor = 'white';
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(this.opts.marker);
|
|
this.focus.append('path').attr('d', symbol).attr('fill', this.opts.color).attr('stroke', strokecolor).attr('stroke-width', 2);
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.on_focus = function(dx, dy) {
|
|
if(!this.setup) return;
|
|
this.off_focus();
|
|
if(this.rubix.opts.invertAxes) {
|
|
dy = dy.toString().replace('.', '\\\.');
|
|
var rects = this.cb_series.selectAll('.column-'+dy);
|
|
} else {
|
|
dx = dx.toString().replace('.', '\\\.');
|
|
var rects = this.cb_series.selectAll('.column-'+dx);
|
|
}
|
|
rects.classed('filled', true);
|
|
rects.attr('fill-opacity', 1);
|
|
rects.attr('stroke-width', 2);
|
|
};
|
|
|
|
Rubix.ColumnSeries.prototype.off_focus = function() {
|
|
if(!this.setup) return;
|
|
var rects = this.cb_series.selectAll('.filled');
|
|
rects.classed('filled', false);
|
|
rects.attr('fill-opacity', 0.85);
|
|
rects.attr('stroke-width', 1);
|
|
};
|
|
|
|
window.Rubix = window.Rubix || {};
|
|
|
|
/**
|
|
* @param {Rubix} rubix
|
|
* @param {Object} opts
|
|
* @constructor
|
|
*/
|
|
Rubix.AreaSeries = function(rubix, opts) {
|
|
this.opts = opts;
|
|
this.opts.color = this.opts.color || 'steelblue';
|
|
this.opts.marker = this.opts.marker || 'circle';
|
|
this.opts.fillopacity = this.opts.fillopacity || 0.5;
|
|
this.opts.strokewidth = this.opts.strokewidth || 1;
|
|
this.opts.noshadow = this.opts.noshadow || false;
|
|
this.show_markers = this.opts.show_markers;
|
|
|
|
if(!this.opts.hasOwnProperty('name')) throw new Error('AreaSeries should have a \'name\' property');
|
|
|
|
this.name = this.opts.name;
|
|
|
|
this.chart_hidden = false;
|
|
this.temp_stack = [];
|
|
|
|
this.setup = false;
|
|
this.animating = false;
|
|
|
|
this._setup(rubix);
|
|
};
|
|
|
|
/**
|
|
* @param {Rubix} rubix
|
|
* @private
|
|
*/
|
|
Rubix.AreaSeries.prototype._setup = function(rubix) {
|
|
this.rubix = rubix;
|
|
|
|
this.root = this.rubix.root;
|
|
this.data = this.rubix.data;
|
|
this.width = this.rubix.width;
|
|
this.height = this.rubix.height;
|
|
this.area_series = this.rubix.root_area_series;
|
|
this.show_markers = (this.show_markers === undefined) ? this.rubix.show_markers : this.show_markers;
|
|
|
|
this.master_detail = this.rubix.master_detail;
|
|
|
|
if(this.master_detail) {
|
|
this.md_root = this.rubix.md_root;
|
|
}
|
|
|
|
if(!this.id) {
|
|
this.id = this.rubix.uid('area');
|
|
}
|
|
|
|
/** separator */
|
|
var self = this;
|
|
this.line = d3.svg.line();
|
|
this.line.defined(function(d) {
|
|
return d.x !== null && d.y !== null;
|
|
});
|
|
this.line.x(function(d) {
|
|
if(self.rubix.opts.invertAxes) {
|
|
return self.rubix.x(d.y);
|
|
}
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
return self.rubix.x(d.x) + self.rubix.x.rangeBand()/2;
|
|
}
|
|
return self.rubix.x(d.x);
|
|
});
|
|
this.line.y(function(d) {
|
|
if(self.rubix.opts.invertAxes) {
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
return self.rubix.y(d.x) + self.rubix.y.rangeBand()/2;
|
|
}
|
|
return self.rubix.y(d.x);
|
|
}
|
|
return self.rubix.y(d.y);
|
|
});
|
|
this.line.interpolate(this.rubix.interpolate);
|
|
|
|
this.area = d3.svg.area();
|
|
this.area.defined(this.line.defined());
|
|
if(this.rubix.opts.invertAxes) {
|
|
this.area.x0(function(d) {
|
|
return self.rubix.x(0);
|
|
});
|
|
this.area.x1(function(d) {
|
|
return self.rubix.x(d.y);
|
|
});
|
|
this.area.y(function(d) {
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
return self.rubix.y(d.x) + self.rubix.y.rangeBand()/2;
|
|
}
|
|
return self.rubix.y(d.x);
|
|
});
|
|
this.area.interpolate(this.rubix.interpolate);
|
|
} else {
|
|
this.area.x(function(d) {
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
return self.rubix.x(d.x) + self.rubix.x.rangeBand()/2;
|
|
}
|
|
return self.rubix.x(d.x);
|
|
});
|
|
this.area.y0(function(d) {
|
|
return self.rubix.y(0);
|
|
});
|
|
this.area.y1(function(d) {
|
|
return self.rubix.y(d.y);
|
|
});
|
|
this.area.interpolate(this.rubix.interpolate);
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.master_line = d3.svg.line();
|
|
this.master_line.defined(function(d) {
|
|
return d.x !== null && d.y !== null;
|
|
});
|
|
this.master_line.x(function(d) {
|
|
if(self.rubix.opts.invertAxes) {
|
|
return self.rubix.x2(d.y);
|
|
}
|
|
return self.rubix.x2(d.x);
|
|
});
|
|
this.master_line.y(function(d) {
|
|
if(self.rubix.opts.invertAxes) {
|
|
return self.rubix.y2(d.x);
|
|
}
|
|
return self.rubix.y2(d.y);
|
|
});
|
|
this.master_line.interpolate(this.rubix.interpolate);
|
|
|
|
this.master_area = d3.svg.area();
|
|
this.master_area.defined(this.master_line.defined());
|
|
this.area.interpolate(this.rubix.interpolate);
|
|
if(this.rubix.opts.invertAxes) {
|
|
this.master_area.x0(function(d) {
|
|
return self.rubix.x2(0);
|
|
});
|
|
this.master_area.x1(function(d) {
|
|
return self.rubix.x2(d.y);
|
|
});
|
|
this.master_area.y(function(d) {
|
|
return self.rubix.y2(d.x);
|
|
});
|
|
this.master_area.interpolate(this.rubix.interpolate);
|
|
} else {
|
|
this.master_area.x(function(d) {
|
|
return self.rubix.x2(d.x);
|
|
});
|
|
this.master_area.y0(function(d) {
|
|
return self.rubix.y2(0);
|
|
});
|
|
this.master_area.y1(function(d) {
|
|
return self.rubix.y2(d.y);
|
|
});
|
|
this.master_area.interpolate(this.rubix.interpolate);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Alias for draw
|
|
Rubix.AreaSeries.prototype.redraw = function(rubix) {
|
|
this._setup(rubix);
|
|
this.draw();
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.noRedraw = function(rubix) {
|
|
this._setup(rubix);
|
|
this.draw(true);
|
|
};
|
|
|
|
/**
|
|
* @param {Array|Object} data
|
|
*/
|
|
Rubix.AreaSeries.prototype.addData = function(data) {
|
|
this.rubix.data_changed = true;
|
|
if(!(data instanceof Array)) {
|
|
if(!(data instanceof Object)) {
|
|
throw new Error("Data must be an array or object");
|
|
} else {
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
data = [data];
|
|
}
|
|
}
|
|
|
|
if(!data.length) return;
|
|
|
|
if(!this.rubix.opts.noSort) {
|
|
data.sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.data[this.name] = data;
|
|
|
|
this.rubix.resetAxis(false);
|
|
this.rubix.forceRedraw();
|
|
this._animate_draw();
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.draw = function(forced) {
|
|
if(!this.name) return;
|
|
if(!this.data) return;
|
|
if(!this.data.hasOwnProperty(this.name)) return;
|
|
if(!this.data[this.name].length) return;
|
|
var self = this;
|
|
|
|
var isConstructed = this.area_series.selectAll('.'+this.id)[0].length;
|
|
|
|
if(!isConstructed) {
|
|
this.area_series.selectAll('.'+this.id+'.clipped').remove();
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1 = this.area_series.append('path').attr('class', this.id+' line').datum(this.data[this.name]).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 5 * this.opts.strokewidth).attr('stroke-opacity', 0.05000000000000001).attr('transform', 'translate(1,1)');
|
|
this.strokePath2 = this.area_series.append('path').attr('class', this.id+' line').datum(this.data[this.name]).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 3 * this.opts.strokewidth).attr('stroke-opacity', 0.1).attr('transform', 'translate(1,1)');
|
|
this.strokePath3 = this.area_series.append('path').attr('class', this.id+' line').datum(this.data[this.name]).attr('d', this.line).attr('stroke', 'black').attr('fill', 'none').attr('stroke-linecap', 'round').attr('stroke-width', 1 * this.opts.strokewidth).attr('stroke-opacity', 0.15000000000000002).attr('transform', 'translate(1,1)');
|
|
}
|
|
this.areaPath = this.area_series.append('path').attr('class', this.id+' area').datum(this.data[this.name]).attr('d', this.area).attr('fill', this.opts.color).attr('fill-opacity', this.opts.fillopacity).attr('stroke', 'none');
|
|
this.linePath = this.area_series.append('path').datum(this.data[this.name]).attr('class', this.id+' line').attr('stroke', this.opts.color).attr('fill', 'none').attr('stroke-linecap', 'round').attr('d', this.line).attr('stroke-width', 2 * this.opts.strokewidth);
|
|
|
|
if(this.master_detail) {
|
|
this.md_root.select('.md-layers > .md_area_series').selectAll('.'+this.id+'.clipped').remove();
|
|
this.masterLinePath = this.md_root.select('.md-layers > .md_area_series').append('path').datum(this.data[this.name]).attr('class', this.id+' line').attr('stroke', this.opts.color).attr('fill', 'none').attr('stroke-linecap', 'round').attr('d', this.master_line).attr('stroke-width', 2 * this.opts.strokewidth);
|
|
this.masterLinePath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.masterLinePath.classed('clipped', true);
|
|
|
|
this.masterAreaPath = this.md_root.select('.md-layers > .md_area_series').append('path').attr('class', this.id+' area').datum(this.data[this.name]).attr('d', this.master_area).attr('fill', this.opts.color).attr('fill-opacity', this.opts.fillopacity).attr('stroke', 'none');
|
|
this.masterAreaPath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.masterAreaPath.classed('clipped', true);
|
|
|
|
if(this.dataChanged) {
|
|
this.rubix.resetExtent();
|
|
this.dataChanged = false;
|
|
}
|
|
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.strokePath2.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
this.strokePath3.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
}
|
|
this.linePath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
|
|
if(!this.opts.noshadow) {
|
|
this.strokePath1.classed('clipped', true);
|
|
this.strokePath2.classed('clipped', true);
|
|
this.strokePath3.classed('clipped', true);
|
|
}
|
|
this.linePath.classed('clipped', true);
|
|
this.areaPath.classed('clipped', true);
|
|
}
|
|
this.areaPath.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip)');
|
|
|
|
this.setup = true;
|
|
} else {
|
|
this.rubix.runCommand('globalRedraw');
|
|
}
|
|
|
|
this.rubix.resetFocus(forced);
|
|
};
|
|
|
|
/**
|
|
* Alias for addPoint
|
|
* @param {Object} data
|
|
* @param {?Boolean} shift
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.AreaSeries.prototype.updatePoint = function(data, shift, noRedraw) {
|
|
this.addPoint(data, shift, noRedraw);
|
|
};
|
|
|
|
/**
|
|
* @param {*} ref
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.AreaSeries.prototype.removePoint = function(ref, noRedraw) {
|
|
if(this.chart_hidden) {
|
|
this.temp_stack.push({
|
|
type: 'removePoint',
|
|
ref: ref
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(!this.data[this.name]) {
|
|
this.data[this.name] = [];
|
|
}
|
|
|
|
var removed = false;
|
|
for(var i=0; i<this.data[this.name].length; i++) {
|
|
if(this.data[this.name][i].x === ref) {
|
|
this.data[this.name].splice(i, 1);
|
|
removed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.dataChanged = true;
|
|
}
|
|
|
|
if(noRedraw) return;
|
|
|
|
if(this.setup) {
|
|
this._animate_draw();
|
|
} else {
|
|
this.rubix.resetAxis(true);
|
|
this.rubix.forceRedraw();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {Object} data
|
|
* @param {?Boolean} shift
|
|
* @param {?Boolean} noRedraw
|
|
*/
|
|
Rubix.AreaSeries.prototype.addPoint = function(data, shift, noRedraw) {
|
|
this.rubix.data_changed = true;
|
|
if(!(data instanceof Object) || (data instanceof Array)) {
|
|
throw new Error("Object required for addPoint");
|
|
}
|
|
if(!(data.hasOwnProperty('x') || data.hasOwnProperty('y'))) {
|
|
throw new Error("Object must be in the form: {x: ..., y: ...}");
|
|
}
|
|
|
|
if(this.chart_hidden) {
|
|
this.temp_stack.push({
|
|
type: 'addPoint',
|
|
data: data,
|
|
shift: shift
|
|
});
|
|
return;
|
|
}
|
|
|
|
if(!this.data[this.name]) {
|
|
this.data[this.name] = [];
|
|
}
|
|
|
|
var added = false;
|
|
for(var i=0; i<this.data[this.name].length; i++) {
|
|
if(this.data[this.name][i].x === data.x) {
|
|
this.data[this.name][i].y = data.y;
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!added) {
|
|
this.data[this.name].push(data);
|
|
}
|
|
|
|
this.data[this.name].sort(function(a, b) {
|
|
if(a.x > b.x) {
|
|
return 1;
|
|
} else if(a.x === b.x) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
});
|
|
|
|
if(this.rubix.opts.interval) {
|
|
if(this.rubix.opts.interval < this.data[this.name].length) {
|
|
this.data[this.name].shift();
|
|
}
|
|
} else {
|
|
if(shift) {
|
|
this.data[this.name].shift();
|
|
}
|
|
}
|
|
|
|
if(this.master_detail) {
|
|
this.dataChanged = true;
|
|
}
|
|
|
|
if(noRedraw) return;
|
|
if(this.setup) {
|
|
this._animate_draw();
|
|
} else {
|
|
this.rubix.resetAxis(true);
|
|
this.rubix.forceRedraw();
|
|
}
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype._animate_draw = function(local) {
|
|
var self = this;
|
|
var text = this.root.selectAll('.y.axis').selectAll('text')[0];
|
|
var width = [];
|
|
for(var i=0; i<text.length; i++) {
|
|
width.push(text[i].getBBox().width);
|
|
}
|
|
var origMaxWidth = d3.max(width);
|
|
|
|
this.rubix.resetAxis(false);
|
|
|
|
text = this.root.selectAll('.y.axis').selectAll('text')[0];
|
|
width = [];
|
|
for(var i=0; i<text.length; i++) {
|
|
width.push(text[i].getBBox().width);
|
|
}
|
|
|
|
var maxWidth = d3.max(width);
|
|
this.rubix.marginLeft(maxWidth);
|
|
this.rubix.resetFocus();
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.hidden = function() {
|
|
this.chart_hidden = true;
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.show = function() {
|
|
this.chart_hidden = false;
|
|
while(this.temp_stack.length>1) {
|
|
var rawData = this.temp_stack.shift();
|
|
if(rawData.type === 'addPoint') {
|
|
this.addPoint(rawData.data, rawData.shift, true);
|
|
} else {
|
|
this.removePoint(rawData.ref);
|
|
}
|
|
}
|
|
if(this.temp_stack.length) {
|
|
var rawData = this.temp_stack.shift();
|
|
if(rawData.type === 'addPoint') {
|
|
this.addPoint(rawData.data, rawData.shift, true);
|
|
} else {
|
|
this.removePoint(rawData.ref);
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.globalRedraw = function(rubix) {
|
|
this.area_series.selectAll('.'+this.id+'.line').attr('d', this.line);
|
|
this.area_series.selectAll('.'+this.id+'.area').attr('d', this.area);
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.forceRedraw = function(rubix) {
|
|
this.redraw(rubix);
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.on_complete_draw = function() {
|
|
if(!this.setup) return;
|
|
var self = this;
|
|
var datum = this.data[this.name];
|
|
if(this.opts.marker !== undefined && this.show_markers) {
|
|
if(this.master_detail) {
|
|
this.rubix.symbols.attr('clip-path', 'url(#'+self.rubix.master_id+'-clip-symbols)');
|
|
}
|
|
this.markers = this.rubix.symbols.selectAll('.'+this.id+'-marker');
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(this.opts.marker);
|
|
|
|
var symbolType = this.markers.data(datum);
|
|
symbolType.enter().append('path')
|
|
.attr('d', symbol)
|
|
.attr('class', this.id + '-marker')
|
|
.attr('fill', this.opts.color)
|
|
.style('display', function(d) {
|
|
if(d.y === null) {
|
|
return 'none';
|
|
}
|
|
return null;
|
|
})
|
|
.attr('stroke', 'white')
|
|
.attr('transform', function(d) {
|
|
var val = d.y;
|
|
if(isNaN(val)) {
|
|
val = 0;
|
|
}
|
|
if(self.rubix.opts.invertAxes) {
|
|
var _y = self.rubix.y(d.x);
|
|
var _x = self.rubix.x(val);
|
|
if(self.rubix.axis.y.range === 'column' || self.rubix.axis.y.range === 'bar') {
|
|
_y += self.rubix.y.rangeBand()/2;
|
|
}
|
|
} else {
|
|
var _y = self.rubix.y(val);
|
|
var _x = self.rubix.x(d.x);
|
|
if(self.rubix.axis.x.range === 'column' || self.rubix.axis.x.range === 'bar') {
|
|
_x += self.rubix.x.rangeBand()/2;
|
|
}
|
|
}
|
|
return 'translate('+_x+','+_y+')';
|
|
});
|
|
symbolType.exit().remove();
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.setupFocus = function() {
|
|
if(!this.setup) return;
|
|
if(this.focus) this.focus.remove();
|
|
this.focus = this.rubix.focus_group.append('g');
|
|
this.focus.attr('class', 'focus');
|
|
this.focus.style('display', 'none');
|
|
switch(this.opts.marker) {
|
|
case 'circle':
|
|
case 'cross':
|
|
case 'square':
|
|
case 'diamond':
|
|
case 'triangle-up':
|
|
case 'triangle-down':
|
|
var symbol = d3.svg.symbol();
|
|
symbol.type(this.opts.marker);
|
|
var path = this.focus.append('path').attr('d', symbol).attr('fill', this.opts.color).attr('stroke', 'white').attr('stroke-width', 2);
|
|
break;
|
|
default:
|
|
throw new Error('Unknown marker type : ' + this.opts.marker);
|
|
break;
|
|
}
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.on_focus = function() {
|
|
if(!this.setup) return;
|
|
this.linePath.attr('stroke-width', 2 * this.opts.strokewidth);
|
|
};
|
|
|
|
Rubix.AreaSeries.prototype.off_focus = function() {
|
|
if(!this.setup) return;
|
|
this.linePath.attr('stroke-width', 2 * this.opts.strokewidth);
|
|
};
|
|
|
|
Rubix.Cleanup = function() {
|
|
var master_id = null;
|
|
for(var i=0; i<RubixListeners.length; i++) {
|
|
master_id = RubixListeners[i];
|
|
$(window).off('orientationchange.'+master_id);
|
|
$(window).off('rubix.redraw.'+master_id);
|
|
$(window).off('resize.rubix.'+master_id);
|
|
$(window).off('debouncedresize.rubix.'+master_id);
|
|
$(window).off('throttledresize.rubix.'+master_id);
|
|
}
|
|
RubixListeners = [];
|
|
};
|
|
|
|
Rubix.redraw = function() {
|
|
$(window).trigger('resize');
|
|
};
|