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('
'); this.root_elem.append('
'); this.root_elem.append('
'); this.root_elem.append('
Loading...
'); this.root_elem.append('
'); 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 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 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 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 = "
â–  "+name+"
"; var x = "
x : " + _x + "
"; var y = "
y : " + _y + "

"; 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 = "
â–  "+name+"
"; var x = "
x : " + _x + "
"; var y = "
y : " + _y + "

"; 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 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â–  "+line_series.name+""); 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("
â–  "+stacked_area_series.name+"
"); this.resetLabelHandlers(); return stacked_area_series; } var area_series = new Rubix.AreaSeries(this, opts); this.charts[area_series.name] = area_series; this.legend.append("
â–  "+area_series.name+"
"); 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("
â–  "+column_series.name+"
"); 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("
â–  "+column_series.name+"
"); 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 .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 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; i1) { 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('
'); this.root_elem.append('
'); this.root_elem.append('
'); this.root_elem.append('
Loading...
'); this.root_elem.append('
'); 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[i].name+""); } $('.'+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("
â–  "+d.data.name+" : "+d.data.value+"
"); } 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 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 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; i1) { 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 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= 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 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 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 b.x) { return 1; } else if(a.x === b.x) { return 0; } else { return -1; } }); added = false; for(var i=0; i maxLen) { maxLen = len; columns = []; for(var k=0; k1) { 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 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 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; i1) { 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