X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=skyquake%2Fframework%2Fjs%2Fn3-line-chart.js;fp=skyquake%2Fframework%2Fjs%2Fn3-line-chart.js;h=b3105cd221893b52dd81b61c69d73e428e2ee5bd;hb=e29efc315df33d546237e270470916e26df391d6;hp=0000000000000000000000000000000000000000;hpb=9c5e457509ba5a1822c316635c6308874e61b4b9;p=osm%2FUI.git diff --git a/skyquake/framework/js/n3-line-chart.js b/skyquake/framework/js/n3-line-chart.js new file mode 100644 index 000000000..b3105cd22 --- /dev/null +++ b/skyquake/framework/js/n3-line-chart.js @@ -0,0 +1,1821 @@ + +/* + * + * Copyright 2016 RIFT.IO Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* +line-chart - v1.1.9 - 21 June 2015 +https://github.com/n3-charts/line-chart +Copyright (c) 2015 n3-charts + */ +var directive, m, mod, old_m, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + +old_m = angular.module('n3-charts.linechart', ['n3charts.utils']); + +m = angular.module('n3-line-chart', ['n3charts.utils']); + +directive = function(name, conf) { + old_m.directive(name, conf); + return m.directive(name, conf); +}; + +directive('linechart', [ + 'n3utils', '$window', '$timeout', function(n3utils, $window, $timeout) { + var link; + link = function(scope, element, attrs, ctrl) { + var dispatch, id, initialHandlers, isUpdatingOptions, promise, updateEvents, window_resize, _u; + _u = n3utils; + dispatch = _u.getEventDispatcher(); + id = _u.uuid(); + element[0].style['font-size'] = 0; + scope.redraw = function() { + scope.update(); + }; + isUpdatingOptions = false; + initialHandlers = { + onSeriesVisibilityChange: function(_arg) { + var index, newVisibility, series; + series = _arg.series, index = _arg.index, newVisibility = _arg.newVisibility; + scope.options.series[index].visible = newVisibility; + return scope.$apply(); + } + }; + scope.update = function() { + var axes, columnWidth, dataPerSeries, dimensions, fn, handlers, isThumbnail, options, svg; + options = _u.sanitizeOptions(scope.options, attrs.mode); + handlers = angular.extend(initialHandlers, _u.getTooltipHandlers(options)); + dataPerSeries = _u.getDataPerSeries(scope.data, options); + dimensions = _u.getDimensions(options, element, attrs); + isThumbnail = attrs.mode === 'thumbnail'; + _u.clean(element[0]); + svg = _u.bootstrap(element[0], id, dimensions); + fn = function(key) { + return (options.series.filter(function(s) { + return s.axis === key && s.visible !== false; + })).length > 0; + }; + axes = _u.createAxes(svg, dimensions, options.axes).andAddThemIf({ + all: !isThumbnail, + x: true, + y: fn('y'), + y2: fn('y2') + }); + if (dataPerSeries.length) { + _u.setScalesDomain(axes, scope.data, options.series, svg, options); + } + _u.createContent(svg, id, options, handlers); + if (dataPerSeries.length) { + columnWidth = _u.getBestColumnWidth(axes, dimensions, dataPerSeries, options); + _u.drawArea(svg, axes, dataPerSeries, options, handlers).drawColumns(svg, axes, dataPerSeries, columnWidth, options, handlers, dispatch).drawLines(svg, axes, dataPerSeries, options, handlers); + if (options.drawDots) { + _u.drawDots(svg, axes, dataPerSeries, options, handlers, dispatch); + } + } + if (options.drawLegend) { + _u.drawLegend(svg, options.series, dimensions, handlers, dispatch); + } + if (options.tooltip.mode === 'scrubber') { + return _u.createGlass(svg, dimensions, handlers, axes, dataPerSeries, options, dispatch, columnWidth); + } else if (options.tooltip.mode !== 'none') { + return _u.addTooltips(svg, dimensions, options.axes); + } + }; + updateEvents = function() { + if (scope.oldclick) { + dispatch.on('click', scope.oldclick); + } else if (scope.click) { + dispatch.on('click', scope.click); + } else { + dispatch.on('click', null); + } + if (scope.oldhover) { + dispatch.on('hover', scope.oldhover); + } else if (scope.hover) { + dispatch.on('hover', scope.hover); + } else { + dispatch.on('hover', null); + } + if (scope.oldfocus) { + dispatch.on('focus', scope.oldfocus); + } else if (scope.focus) { + dispatch.on('focus', scope.focus); + } else { + dispatch.on('focus', null); + } + if (scope.toggle) { + return dispatch.on('toggle', scope.toggle); + } else { + return dispatch.on('toggle', null); + } + }; + promise = void 0; + window_resize = function() { + if (promise != null) { + $timeout.cancel(promise); + } + return promise = $timeout(scope.redraw, 1); + }; + $window.addEventListener('resize', window_resize); + scope.$watch('data', scope.redraw, true); + scope.$watch('options', scope.redraw, true); + scope.$watchCollection('[click, hover, focus, toggle]', updateEvents); + scope.$watchCollection('[oldclick, oldhover, oldfocus]', updateEvents); + console.log('data', scope.data) + window_resize(); + }; + return { + replace: true, + restrict: 'E', + scope: { + data: '=', + options: '=', + oldclick: '=click', + oldhover: '=hover', + oldfocus: '=focus', + click: '=onClick', + hover: '=onHover', + focus: '=onFocus', + toggle: '=onToggle' + }, + template: '
', + link: link + }; + } +]); + +mod = angular.module('n3charts.utils', []); + +mod.factory('n3utils', [ + '$window', '$log', '$rootScope', function($window, $log, $rootScope) { + return { + addPatterns: function(svg, series) { + var pattern; + pattern = svg.select('defs').selectAll('pattern').data(series.filter(function(s) { + return s.striped; + })).enter().append('pattern').attr({ + id: function(s) { + return s.type + 'Pattern_' + s.index; + }, + patternUnits: "userSpaceOnUse", + x: 0, + y: 0, + width: 60, + height: 60 + }).append('g').style({ + 'fill': function(s) { + return s.color; + }, + 'fill-opacity': 0.3 + }); + pattern.append('rect').style('fill-opacity', 0.3).attr('width', 60).attr('height', 60); + pattern.append('path').attr('d', "M 10 0 l10 0 l -20 20 l 0 -10 z"); + pattern.append('path').attr('d', "M40 0 l10 0 l-50 50 l0 -10 z"); + pattern.append('path').attr('d', "M60 10 l0 10 l-40 40 l-10 0 z"); + return pattern.append('path').attr('d', "M60 40 l0 10 l-10 10 l -10 0 z"); + }, + drawArea: function(svg, scales, data, options) { + var areaSeries, drawers; + areaSeries = data.filter(function(series) { + return series.type === 'area'; + }); + this.addPatterns(svg, areaSeries); + drawers = { + y: this.createLeftAreaDrawer(scales, options.lineMode, options.tension), + y2: this.createRightAreaDrawer(scales, options.lineMode, options.tension) + }; + svg.select('.content').selectAll('.areaGroup').data(areaSeries).enter().append('g').attr('class', function(s) { + return 'areaGroup ' + 'series_' + s.index; + }).append('path').attr('class', 'area').style('fill', function(s) { + if (s.striped !== true) { + return s.color; + } + return "url(#areaPattern_" + s.index + ")"; + }).style('opacity', function(s) { + if (s.striped) { + return '1'; + } else { + return '0.3'; + } + }).attr('d', function(d) { + return drawers[d.axis](d.values); + }); + return this; + }, + createLeftAreaDrawer: function(scales, mode, tension) { + return d3.svg.area().x(function(d) { + return scales.xScale(d.x); + }).y0(function(d) { + return scales.yScale(d.y0); + }).y1(function(d) { + return scales.yScale(d.y0 + d.y); + }).interpolate(mode).tension(tension); + }, + createRightAreaDrawer: function(scales, mode, tension) { + return d3.svg.area().x(function(d) { + return scales.xScale(d.x); + }).y0(function(d) { + return scales.y2Scale(d.y0); + }).y1(function(d) { + return scales.y2Scale(d.y0 + d.y); + }).interpolate(mode).tension(tension); + }, + getPseudoColumns: function(data, options) { + var keys, pseudoColumns; + data = data.filter(function(s) { + return s.type === 'column'; + }); + pseudoColumns = {}; + keys = []; + data.forEach(function(series) { + var i, inAStack, index; + inAStack = false; + options.stacks.forEach(function(stack, index) { + var _ref; + if ((series.id != null) && (_ref = series.id, __indexOf.call(stack.series, _ref) >= 0)) { + pseudoColumns[series.id] = index; + if (__indexOf.call(keys, index) < 0) { + keys.push(index); + } + return inAStack = true; + } + }); + if (inAStack === false) { + i = pseudoColumns[series.id] = index = keys.length; + return keys.push(i); + } + }); + return { + pseudoColumns: pseudoColumns, + keys: keys + }; + }, + getMinDelta: function(seriesData, key, scale, range) { + return d3.min(seriesData.map(function(series) { + return series.values.map(function(d) { + return scale(d[key]); + }).filter(function(e) { + if (range) { + return e >= range[0] && e <= range[1]; + } else { + return true; + } + }).reduce(function(prev, cur, i, arr) { + var diff; + diff = i > 0 ? cur - arr[i - 1] : Number.MAX_VALUE; + if (diff < prev) { + return diff; + } else { + return prev; + } + }, Number.MAX_VALUE); + })); + }, + getBestColumnWidth: function(axes, dimensions, seriesData, options) { + var colData, delta, innerWidth, keys, nSeries, pseudoColumns, _ref; + if (!(seriesData && seriesData.length !== 0)) { + return 10; + } + if ((seriesData.filter(function(s) { + return s.type === 'column'; + })).length === 0) { + return 10; + } + _ref = this.getPseudoColumns(seriesData, options), pseudoColumns = _ref.pseudoColumns, keys = _ref.keys; + innerWidth = dimensions.width - dimensions.left - dimensions.right; + colData = seriesData.filter(function(d) { + return pseudoColumns.hasOwnProperty(d.id); + }); + delta = this.getMinDelta(colData, 'x', axes.xScale, [0, innerWidth]); + if (delta > innerWidth) { + delta = 0.25 * innerWidth; + } + nSeries = keys.length; + return parseInt((delta - options.columnsHGap) / nSeries); + }, + getColumnAxis: function(data, columnWidth, options) { + var keys, pseudoColumns, x1, _ref; + _ref = this.getPseudoColumns(data, options), pseudoColumns = _ref.pseudoColumns, keys = _ref.keys; + x1 = d3.scale.ordinal().domain(keys).rangeBands([0, keys.length * columnWidth], 0); + return function(s) { + var index; + if (pseudoColumns[s.id] == null) { + return 0; + } + index = pseudoColumns[s.id]; + return x1(index) - keys.length * columnWidth / 2; + }; + }, + drawColumns: function(svg, axes, data, columnWidth, options, handlers, dispatch) { + var colGroup, x1; + data = data.filter(function(s) { + return s.type === 'column'; + }); + x1 = this.getColumnAxis(data, columnWidth, options); + data.forEach(function(s) { + return s.xOffset = x1(s) + columnWidth * .5; + }); + colGroup = svg.select('.content').selectAll('.columnGroup').data(data).enter().append("g").attr('class', function(s) { + return 'columnGroup series_' + s.index; + }).attr('transform', function(s) { + return "translate(" + x1(s) + ",0)"; + }); + colGroup.each(function(series) { + return d3.select(this).selectAll("rect").data(series.values).enter().append("rect").style({ + 'stroke': series.color, + 'fill': series.color, + 'stroke-opacity': function(d) { + if (d.y === 0) { + return '0'; + } else { + return '1'; + } + }, + 'stroke-width': '1px', + 'fill-opacity': function(d) { + if (d.y === 0) { + return 0; + } else { + return 0.7; + } + } + }).attr({ + width: columnWidth, + x: function(d) { + return axes.xScale(d.x); + }, + height: function(d) { + if (d.y === 0) { + return axes[d.axis + 'Scale'].range()[0]; + } + return Math.abs(axes[d.axis + 'Scale'](d.y0 + d.y) - axes[d.axis + 'Scale'](d.y0)); + }, + y: function(d) { + if (d.y === 0) { + return 0; + } else { + return axes[d.axis + 'Scale'](Math.max(0, d.y0 + d.y)); + } + } + }).on({ + 'click': function(d, i) { + return dispatch.click(d, i); + } + }).on('mouseover', function(d, i) { + dispatch.hover(d, i); + return typeof handlers.onMouseOver === "function" ? handlers.onMouseOver(svg, { + series: series, + x: axes.xScale(d.x), + y: axes[d.axis + 'Scale'](d.y0 + d.y), + datum: d + }, options.axes) : void 0; + }).on('mouseout', function(d) { + return typeof handlers.onMouseOut === "function" ? handlers.onMouseOut(svg) : void 0; + }); + }); + return this; + }, + drawDots: function(svg, axes, data, options, handlers, dispatch) { + var dotGroup; + dotGroup = svg.select('.content').selectAll('.dotGroup').data(data.filter(function(s) { + var _ref; + return ((_ref = s.type) === 'line' || _ref === 'area') && s.drawDots; + })).enter().append('g'); + dotGroup.attr({ + "class": function(s) { + return "dotGroup series_" + s.index; + }, + fill: function(s) { + return s.color; + } + }).selectAll('.dot').data(function(d) { + return d.values; + }).enter().append('circle').attr({ + 'class': 'dot', + 'r': function(d) { + return d.dotSize; + }, + 'cx': function(d) { + return axes.xScale(d.x); + }, + 'cy': function(d) { + return axes[d.axis + 'Scale'](d.y + d.y0); + } + }).style({ + 'stroke': 'white', + 'stroke-width': '2px' + }).on({ + 'click': function(d, i) { + return dispatch.click(d, i); + } + }).on({ + 'mouseover': function(d, i) { + return dispatch.hover(d, i); + } + }); + if (options.tooltip.mode !== 'none') { + dotGroup.on('mouseover', function(series) { + var d, target; + target = d3.select(d3.event.target); + d = target.datum(); + target.attr('r', function(s) { + return s.dotSize + 2; + }); + return typeof handlers.onMouseOver === "function" ? handlers.onMouseOver(svg, { + series: series, + x: target.attr('cx'), + y: target.attr('cy'), + datum: d + }, options.axes) : void 0; + }).on('mouseout', function(d) { + d3.select(d3.event.target).attr('r', function(s) { + return s.dotSize; + }); + return typeof handlers.onMouseOut === "function" ? handlers.onMouseOut(svg) : void 0; + }); + } + return this; + }, + getEventDispatcher: function() { + var events; + events = ['focus', 'hover', 'click', 'toggle']; + return d3.dispatch.apply(this, events); + }, + computeLegendLayout: function(svg, series, dimensions) { + var cumul, i, j, leftLayout, leftWidths, padding, rightLayout, rightWidths, that, w; + padding = 10; + that = this; + leftWidths = this.getLegendItemsWidths(svg, 'y'); + leftLayout = [0]; + i = 1; + while (i < leftWidths.length) { + leftLayout.push(leftWidths[i - 1] + leftLayout[i - 1] + padding); + i++; + } + rightWidths = this.getLegendItemsWidths(svg, 'y2'); + if (!(rightWidths.length > 0)) { + return [leftLayout]; + } + w = dimensions.width - dimensions.right - dimensions.left; + cumul = 0; + rightLayout = []; + j = rightWidths.length - 1; + while (j >= 0) { + rightLayout.push(w - cumul - rightWidths[j]); + cumul += rightWidths[j] + padding; + j--; + } + rightLayout.reverse(); + return [leftLayout, rightLayout]; + }, + getLegendItemsWidths: function(svg, axis) { + var bbox, i, items, that, widths; + that = this; + bbox = function(t) { + return that.getTextBBox(t).width; + }; + items = svg.selectAll(".legendItem." + axis); + if (!(items.length > 0)) { + return []; + } + widths = []; + i = 0; + while (i < items[0].length) { + widths.push(bbox(items[0][i])); + i++; + } + return widths; + }, + drawLegend: function(svg, series, dimensions, handlers, dispatch) { + var d, groups, legend, that, translateLegends; + that = this; + legend = svg.append('g').attr('class', 'legend'); + d = 16; + svg.select('defs').append('svg:clipPath').attr('id', 'legend-clip').append('circle').attr('r', d / 2); + groups = legend.selectAll('.legendItem').data(series); + groups.enter().append('g').on('click', function(s, i) { + var visibility; + visibility = !(s.visible !== false); + dispatch.toggle(s, i, visibility); + return typeof handlers.onSeriesVisibilityChange === "function" ? handlers.onSeriesVisibilityChange({ + series: s, + index: i, + newVisibility: visibility + }) : void 0; + }); + groups.attr({ + 'class': function(s, i) { + return "legendItem series_" + i + " " + s.axis; + }, + 'opacity': function(s, i) { + if (s.visible === false) { + that.toggleSeries(svg, i); + return '0.2'; + } + return '1'; + } + }).each(function(s) { + var item, _ref; + item = d3.select(this); + item.append('circle').attr({ + 'fill': s.color, + 'stroke': s.color, + 'stroke-width': '2px', + 'r': d / 2 + }); + item.append('path').attr({ + 'clip-path': 'url(#legend-clip)', + 'fill-opacity': (_ref = s.type) === 'area' || _ref === 'column' ? '1' : '0', + 'fill': 'white', + 'stroke': 'white', + 'stroke-width': '2px', + 'd': that.getLegendItemPath(s, d, d) + }); + item.append('circle').attr({ + 'fill-opacity': 0, + 'stroke': s.color, + 'stroke-width': '2px', + 'r': d / 2 + }); + return item.append('text').attr({ + 'class': function(d, i) { + return "legendText series_" + i; + }, + 'font-family': 'Courier', + 'font-size': 10, + 'transform': 'translate(13, 4)', + 'text-rendering': 'geometric-precision' + }).text(s.label || s.y); + }); + translateLegends = function() { + var left, right, _ref; + _ref = that.computeLegendLayout(svg, series, dimensions), left = _ref[0], right = _ref[1]; + return groups.attr({ + 'transform': function(s, i) { + if (s.axis === 'y') { + return "translate(" + (left.shift()) + "," + (dimensions.height - 40) + ")"; + } else { + return "translate(" + (right.shift()) + "," + (dimensions.height - 40) + ")"; + } + } + }); + }; + translateLegends(); + setTimeout(translateLegends, 0); + return this; + }, + getLegendItemPath: function(series, w, h) { + var base_path, path; + if (series.type === 'column') { + path = 'M' + (-w / 3) + ' ' + (-h / 8) + ' l0 ' + h + ' '; + path += 'M0' + ' ' + (-h / 3) + ' l0 ' + h + ' '; + path += 'M' + w / 3 + ' ' + (-h / 10) + ' l0 ' + h + ' '; + return path; + } + base_path = 'M-' + w / 2 + ' 0' + h / 3 + ' l' + w / 3 + ' -' + h / 3 + ' l' + w / 3 + ' ' + h / 3 + ' l' + w / 3 + ' -' + 2 * h / 3; + if (series.type === 'area') { + base_path + ' l0 ' + h + ' l-' + w + ' 0z'; + } + return base_path; + }, + toggleSeries: function(svg, index) { + var isVisible; + isVisible = false; + svg.select('.content').selectAll('.series_' + index).style('display', function(s) { + if (d3.select(this).style('display') === 'none') { + isVisible = true; + return 'initial'; + } else { + isVisible = false; + return 'none'; + } + }); + return isVisible; + }, + drawLines: function(svg, scales, data, options, handlers) { + var drawers, interpolateData, lineGroup; + drawers = { + y: this.createLeftLineDrawer(scales, options.lineMode, options.tension), + y2: this.createRightLineDrawer(scales, options.lineMode, options.tension) + }; + lineGroup = svg.select('.content').selectAll('.lineGroup').data(data.filter(function(s) { + var _ref; + return (_ref = s.type) === 'line' || _ref === 'area'; + })).enter().append('g'); + lineGroup.style('stroke', function(s) { + return s.color; + }).attr('class', function(s) { + return "lineGroup series_" + s.index; + }).append('path').attr({ + "class": 'line', + d: function(d) { + return drawers[d.axis](d.values); + } + }).style({ + 'fill': 'none', + 'stroke-width': function(s) { + return s.thickness; + }, + 'stroke-dasharray': function(s) { + if (s.lineMode === 'dashed') { + return '10,3'; + } + return void 0; + } + }); + if (options.tooltip.interpolate) { + interpolateData = function(series) { + var datum, error, i, interpDatum, maxXPos, maxXValue, maxYPos, maxYValue, minXPos, minXValue, minYPos, minYValue, mousePos, target, valuesData, x, xPercentage, xVal, y, yPercentage, yVal, _i, _len; + target = d3.select(d3.event.target); + try { + mousePos = d3.mouse(this); + } catch (_error) { + error = _error; + mousePos = [0, 0]; + } + valuesData = target.datum().values; + for (i = _i = 0, _len = valuesData.length; _i < _len; i = ++_i) { + datum = valuesData[i]; + x = scales.xScale(datum.x); + y = scales.yScale(datum.y); + if ((typeof minXPos === "undefined" || minXPos === null) || x < minXPos) { + minXPos = x; + minXValue = datum.x; + } + if ((typeof maxXPos === "undefined" || maxXPos === null) || x > maxXPos) { + maxXPos = x; + maxXValue = datum.x; + } + if ((typeof minYPos === "undefined" || minYPos === null) || y < minYPos) { + minYPos = y; + } + if ((typeof maxYPos === "undefined" || maxYPos === null) || y > maxYPos) { + maxYPos = y; + } + if ((typeof minYValue === "undefined" || minYValue === null) || datum.y < minYValue) { + minYValue = datum.y; + } + if ((typeof maxYValue === "undefined" || maxYValue === null) || datum.y > maxYValue) { + maxYValue = datum.y; + } + } + xPercentage = (mousePos[0] - minXPos) / (maxXPos - minXPos); + yPercentage = (mousePos[1] - minYPos) / (maxYPos - minYPos); + xVal = Math.round(xPercentage * (maxXValue - minXValue) + minXValue); + yVal = Math.round((1 - yPercentage) * (maxYValue - minYValue) + minYValue); + interpDatum = { + x: xVal, + y: yVal + }; + return typeof handlers.onMouseOver === "function" ? handlers.onMouseOver(svg, { + series: series, + x: mousePos[0], + y: mousePos[1], + datum: interpDatum + }, options.axes) : void 0; + }; + lineGroup.on('mousemove', interpolateData).on('mouseout', function(d) { + return typeof handlers.onMouseOut === "function" ? handlers.onMouseOut(svg) : void 0; + }); + } + return this; + }, + createLeftLineDrawer: function(scales, mode, tension) { + return d3.svg.line().x(function(d) { + return scales.xScale(d.x); + }).y(function(d) { + return scales.yScale(d.y + d.y0); + }).interpolate(mode).tension(tension); + }, + createRightLineDrawer: function(scales, mode, tension) { + return d3.svg.line().x(function(d) { + return scales.xScale(d.x); + }).y(function(d) { + return scales.y2Scale(d.y + d.y0); + }).interpolate(mode).tension(tension); + }, + getPixelCssProp: function(element, propertyName) { + var string; + string = $window.getComputedStyle(element, null).getPropertyValue(propertyName); + return +string.replace(/px$/, ''); + }, + getDefaultMargins: function() { + return { + top: 20, + right: 50, + bottom: 60, + left: 50 + }; + }, + getDefaultThumbnailMargins: function() { + return { + top: 1, + right: 1, + bottom: 2, + left: 0 + }; + }, + getElementDimensions: function(element, width, height) { + var bottom, dim, left, parent, right, top; + dim = {}; + parent = element; + top = this.getPixelCssProp(parent, 'padding-top'); + bottom = this.getPixelCssProp(parent, 'padding-bottom'); + left = this.getPixelCssProp(parent, 'padding-left'); + right = this.getPixelCssProp(parent, 'padding-right'); + dim.width = +(width || parent.offsetWidth || 900) - left - right; + dim.height = +(height || parent.offsetHeight || 500) - top - bottom; + return dim; + }, + getDimensions: function(options, element, attrs) { + var dim; + dim = this.getElementDimensions(element[0].parentElement, attrs.width, attrs.height); + dim = angular.extend(options.margin, dim); + return dim; + }, + clean: function(element) { + return d3.select(element).on('keydown', null).on('keyup', null).select('svg').remove(); + }, + uuid: function() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r, v; + r = Math.random() * 16 | 0; + v = c === 'x' ? r : r & 0x3 | 0x8; + return v.toString(16); + }); + }, + bootstrap: function(element, id, dimensions) { + var defs, height, svg, width; + d3.select(element).classed('chart', true); + width = dimensions.width; + height = dimensions.height; + svg = d3.select(element).append('svg').attr({ + width: width, + height: height + }).append('g').attr('transform', 'translate(' + dimensions.left + ',' + dimensions.top + ')'); + defs = svg.append('defs').attr('class', 'patterns'); + defs.append('clipPath').attr('class', 'content-clip').attr('id', "content-clip-" + id).append('rect').attr({ + 'x': 0, + 'y': 0, + 'width': width - dimensions.left - dimensions.right, + 'height': height - dimensions.top - dimensions.bottom + }); + return svg; + }, + createContent: function(svg, id, options) { + var content; + content = svg.append('g').attr('class', 'content'); + if (options.hideOverflow) { + return content.attr('clip-path', "url(#content-clip-" + id + ")"); + } + }, + createGlass: function(svg, dimensions, handlers, axes, data, options, dispatch, columnWidth) { + var glass, scrubberGroup, that; + that = this; + glass = svg.append('g').attr({ + 'class': 'glass-container', + 'opacity': 0 + }); + scrubberGroup = glass.selectAll('.scrubberItem').data(data).enter().append('g').attr('class', function(s, i) { + return "scrubberItem series_" + i; + }); + scrubberGroup.each(function(s, i) { + var g, g2, item; + item = d3.select(this); + g = item.append('g').attr({ + 'class': "rightTT" + }); + g.append('path').attr({ + 'class': "scrubberPath series_" + i, + 'y': '-7px', + 'fill': s.color + }); + that.styleTooltip(g.append('text').style('text-anchor', 'start').attr({ + 'class': function(d, i) { + return "scrubberText series_" + i; + }, + 'height': '14px', + 'transform': 'translate(7, 3)', + 'text-rendering': 'geometric-precision' + })).text(s.label || s.y); + g2 = item.append('g').attr({ + 'class': "leftTT" + }); + g2.append('path').attr({ + 'class': "scrubberPath series_" + i, + 'y': '-7px', + 'fill': s.color + }); + that.styleTooltip(g2.append('text').style('text-anchor', 'end').attr({ + 'class': "scrubberText series_" + i, + 'height': '14px', + 'transform': 'translate(-13, 3)', + 'text-rendering': 'geometric-precision' + })).text(s.label || s.y); + return item.append('circle').attr({ + 'class': "scrubberDot series_" + i, + 'fill': 'white', + 'stroke': s.color, + 'stroke-width': '2px', + 'r': 4 + }); + }); + return glass.append('rect').attr({ + "class": 'glass', + width: dimensions.width - dimensions.left - dimensions.right, + height: dimensions.height - dimensions.top - dimensions.bottom + }).style('fill', 'white').style('fill-opacity', 0.000001).on('mouseover', function() { + return handlers.onChartHover(svg, d3.select(this), axes, data, options, dispatch, columnWidth); + }); + }, + getDataPerSeries: function(data, options) { + var axes, layout, series, straightened; + series = options.series; + axes = options.axes; + if (!(series && series.length && data && data.length)) { + return []; + } + straightened = series.map(function(s, i) { + var seriesData; + seriesData = { + index: i, + name: s.y, + values: [], + color: s.color, + axis: s.axis || 'y', + xOffset: 0, + type: s.type, + thickness: s.thickness, + drawDots: s.drawDots !== false + }; + if (s.dotSize != null) { + seriesData.dotSize = s.dotSize; + } + if (s.striped === true) { + seriesData.striped = true; + } + if (s.lineMode != null) { + seriesData.lineMode = s.lineMode; + } + if (s.id) { + seriesData.id = s.id; + } + data.filter(function(row) { + return row[s.y] != null; + }).forEach(function(row) { + var d; + d = { + x: row[options.axes.x.key], + y: row[s.y], + y0: 0, + axis: s.axis || 'y' + }; + if (s.dotSize != null) { + d.dotSize = s.dotSize; + } + return seriesData.values.push(d); + }); + return seriesData; + }); + if ((options.stacks == null) || options.stacks.length === 0) { + return straightened; + } + layout = d3.layout.stack().values(function(s) { + return s.values; + }); + options.stacks.forEach(function(stack) { + var layers; + if (!(stack.series.length > 0)) { + return; + } + layers = straightened.filter(function(s, i) { + var _ref; + return (s.id != null) && (_ref = s.id, __indexOf.call(stack.series, _ref) >= 0); + }); + return layout(layers); + }); + return straightened; + }, + estimateSideTooltipWidth: function(svg, text) { + var bbox, t; + t = svg.append('text'); + t.text('' + text); + this.styleTooltip(t); + bbox = this.getTextBBox(t[0][0]); + t.remove(); + return bbox; + }, + getTextBBox: function(svgTextElement) { + var error; + if (svgTextElement !== null) { + try { + return svgTextElement.getBBox(); + } catch (_error) { + error = _error; + return { + height: 0, + width: 0, + y: 0, + x: 0 + }; + } + } + return {}; + }, + getWidestTickWidth: function(svg, axisKey) { + var bbox, max, ticks, _ref; + max = 0; + bbox = this.getTextBBox; + ticks = svg.select("." + axisKey + ".axis").selectAll('.tick'); + if ((_ref = ticks[0]) != null) { + _ref.forEach(function(t) { + return max = Math.max(max, bbox(t).width); + }); + } + return max; + }, + getWidestOrdinate: function(data, series, options) { + var widest; + widest = ''; + data.forEach(function(row) { + return series.forEach(function(series) { + var v, _ref; + v = row[series.y]; + if ((series.axis != null) && ((_ref = options.axes[series.axis]) != null ? _ref.ticksFormatter : void 0)) { + v = options.axes[series.axis].ticksFormatter(v); + } + if (v == null) { + return; + } + if (('' + v).length > ('' + widest).length) { + return widest = v; + } + }); + }); + return widest; + }, + getDefaultOptions: function() { + return { + tooltip: { + mode: 'scrubber' + }, + lineMode: 'linear', + tension: 0.7, + margin: this.getDefaultMargins(), + axes: { + x: { + type: 'linear', + key: 'x' + }, + y: { + type: 'linear' + } + }, + series: [], + drawLegend: true, + drawDots: true, + stacks: [], + columnsHGap: 5, + hideOverflow: false + }; + }, + sanitizeOptions: function(options, mode) { + var defaultMargin; + if (options == null) { + options = {}; + } + if (mode === 'thumbnail') { + options.drawLegend = false; + options.drawDots = false; + options.tooltip = { + mode: 'none', + interpolate: false + }; + } + options.series = this.sanitizeSeriesOptions(options.series); + options.stacks = this.sanitizeSeriesStacks(options.stacks, options.series); + options.axes = this.sanitizeAxes(options.axes, this.haveSecondYAxis(options.series)); + options.tooltip = this.sanitizeTooltip(options.tooltip); + options.margin = this.sanitizeMargins(options.margin); + options.lineMode || (options.lineMode = this.getDefaultOptions().lineMode); + options.tension = /^\d+(\.\d+)?$/.test(options.tension) ? options.tension : this.getDefaultOptions().tension; + options.drawLegend = options.drawLegend !== false; + options.drawDots = options.drawDots !== false; + if (!angular.isNumber(options.columnsHGap)) { + options.columnsHGap = 5; + } + options.hideOverflow = options.hideOverflow || false; + defaultMargin = mode === 'thumbnail' ? this.getDefaultThumbnailMargins() : this.getDefaultMargins(); + options.series = angular.extend(this.getDefaultOptions().series, options.series); + options.axes = angular.extend(this.getDefaultOptions().axes, options.axes); + options.tooltip = angular.extend(this.getDefaultOptions().tooltip, options.tooltip); + options.margin = angular.extend(defaultMargin, options.margin); + return options; + }, + sanitizeMargins: function(options) { + var attrs, margin, opt, value; + attrs = ['top', 'right', 'bottom', 'left']; + margin = {}; + for (opt in options) { + value = options[opt]; + if (__indexOf.call(attrs, opt) >= 0) { + margin[opt] = parseFloat(value); + } + } + return margin; + }, + sanitizeSeriesStacks: function(stacks, series) { + var seriesKeys; + if (stacks == null) { + return []; + } + seriesKeys = {}; + series.forEach(function(s) { + return seriesKeys[s.id] = s; + }); + stacks.forEach(function(stack) { + return stack.series.forEach(function(id) { + var s; + s = seriesKeys[id]; + if (s != null) { + if (s.axis !== stack.axis) { + return $log.warn("Series " + id + " is not on the same axis as its stack"); + } + } else { + if (!s) { + return $log.warn("Unknown series found in stack : " + id); + } + } + }); + }); + return stacks; + }, + sanitizeTooltip: function(options) { + var _ref; + if (!options) { + return { + mode: 'scrubber' + }; + } + if ((_ref = options.mode) !== 'none' && _ref !== 'axes' && _ref !== 'scrubber') { + options.mode = 'scrubber'; + } + if (options.mode === 'scrubber') { + delete options.interpolate; + } else { + options.interpolate = !!options.interpolate; + } + if (options.mode === 'scrubber' && options.interpolate) { + throw new Error('Interpolation is not supported for scrubber tooltip mode.'); + } + return options; + }, + sanitizeSeriesOptions: function(options) { + var colors, knownIds; + if (options == null) { + return []; + } + colors = d3.scale.category10(); + knownIds = {}; + options.forEach(function(s, i) { + if (knownIds[s.id] != null) { + throw new Error("Twice the same ID (" + s.id + ") ? Really ?"); + } + if (s.id != null) { + return knownIds[s.id] = s; + } + }); + options.forEach(function(s, i) { + var cnt, _ref, _ref1, _ref2, _ref3; + s.axis = ((_ref = s.axis) != null ? _ref.toLowerCase() : void 0) !== 'y2' ? 'y' : 'y2'; + s.color || (s.color = colors(i)); + s.type = (_ref1 = s.type) === 'line' || _ref1 === 'area' || _ref1 === 'column' ? s.type : "line"; + if (s.type === 'column') { + delete s.thickness; + delete s.lineMode; + delete s.drawDots; + delete s.dotSize; + } else if (!/^\d+px$/.test(s.thickness)) { + s.thickness = '1px'; + } + if ((_ref2 = s.type) === 'line' || _ref2 === 'area') { + if ((_ref3 = s.lineMode) !== 'dashed') { + delete s.lineMode; + } + if (s.drawDots !== false && (s.dotSize == null)) { + s.dotSize = 2; + } + } + if (s.id == null) { + cnt = 0; + while (knownIds["series_" + cnt] != null) { + cnt++; + } + s.id = "series_" + cnt; + knownIds[s.id] = s; + } + if (s.drawDots === false) { + return delete s.dotSize; + } + }); + return options; + }, + sanitizeAxes: function(axesOptions, secondAxis) { + var _base; + if (axesOptions == null) { + axesOptions = {}; + } + axesOptions.x = this.sanitizeAxisOptions(axesOptions.x); + (_base = axesOptions.x).key || (_base.key = "x"); + axesOptions.y = this.sanitizeAxisOptions(axesOptions.y); + if (secondAxis) { + axesOptions.y2 = this.sanitizeAxisOptions(axesOptions.y2); + } + return axesOptions; + }, + sanitizeExtrema: function(options) { + var max, min; + min = this.getSanitizedNumber(options.min); + if (min != null) { + options.min = min; + } else { + delete options.min; + } + max = this.getSanitizedNumber(options.max); + if (max != null) { + return options.max = max; + } else { + return delete options.max; + } + }, + getSanitizedNumber: function(value) { + var number; + if (value == null) { + return void 0; + } + number = parseFloat(value); + if (isNaN(number)) { + $log.warn("Invalid extremum value : " + value + ", deleting it."); + return void 0; + } + return number; + }, + sanitizeAxisOptions: function(options) { + if (options == null) { + return { + type: 'linear' + }; + } + options.type || (options.type = 'linear'); + if (options.ticksRotate != null) { + options.ticksRotate = this.getSanitizedNumber(options.ticksRotate); + } + if (options.labelFunction != null) { + options.ticksFormatter = options.labelFunction; + } + if (options.ticksFormat != null) { + if (options.type === 'date') { + options.ticksFormatter = d3.time.format(options.ticksFormat); + } else { + options.ticksFormatter = d3.format(options.ticksFormat); + } + if (options.tooltipFormatter == null) { + options.tooltipFormatter = options.ticksFormatter; + } + } + if (options.tooltipFormat != null) { + if (options.type === 'date') { + options.tooltipFormatter = d3.time.format(options.tooltipFormat); + } else { + options.tooltipFormatter = d3.format(options.tooltipFormat); + } + } + if (options.ticksInterval != null) { + options.ticksInterval = this.getSanitizedNumber(options.ticksInterval); + } + this.sanitizeExtrema(options); + return options; + }, + createAxes: function(svg, dimensions, axesOptions) { + var createY2Axis, height, style, width, x, xAxis, y, y2, y2Axis, yAxis; + createY2Axis = axesOptions.y2 != null; + width = dimensions.width; + height = dimensions.height; + width = width - dimensions.left - dimensions.right; + height = height - dimensions.top - dimensions.bottom; + x = void 0; + if (axesOptions.x.type === 'date') { + x = d3.time.scale().rangeRound([0, width]); + } else { + x = d3.scale.linear().rangeRound([0, width]); + } + xAxis = this.createAxis(x, 'x', axesOptions); + y = void 0; + if (axesOptions.y.type === 'log') { + y = d3.scale.log().clamp(true).rangeRound([height, 0]); + } else { + y = d3.scale.linear().rangeRound([height, 0]); + } + y.clamp(true); + yAxis = this.createAxis(y, 'y', axesOptions); + y2 = void 0; + if (createY2Axis && axesOptions.y2.type === 'log') { + y2 = d3.scale.log().clamp(true).rangeRound([height, 0]); + } else { + y2 = d3.scale.linear().rangeRound([height, 0]); + } + y2.clamp(true); + y2Axis = this.createAxis(y2, 'y2', axesOptions); + style = function(group) { + group.style({ + 'font': '10px Courier', + 'shape-rendering': 'crispEdges' + }); + return group.selectAll('path').style({ + 'fill': 'none', + 'stroke': '#000' + }); + }; + return { + xScale: x, + yScale: y, + y2Scale: y2, + xAxis: xAxis, + yAxis: yAxis, + y2Axis: y2Axis, + andAddThemIf: function(conditions) { + if (!!conditions.all) { + if (!!conditions.x) { + svg.append('g').attr('class', 'x axis').attr('transform', 'translate(0,' + height + ')').call(xAxis).call(style); + } + if (!!conditions.y) { + svg.append('g').attr('class', 'y axis').call(yAxis).call(style); + } + if (createY2Axis && !!conditions.y2) { + svg.append('g').attr('class', 'y2 axis').attr('transform', 'translate(' + width + ', 0)').call(y2Axis).call(style); + } + } + return { + xScale: x, + yScale: y, + y2Scale: y2, + xAxis: xAxis, + yAxis: yAxis, + y2Axis: y2Axis + }; + } + }; + }, + createAxis: function(scale, key, options) { + var axis, o, sides; + sides = { + x: 'bottom', + y: 'left', + y2: 'right' + }; + o = options[key]; + axis = d3.svg.axis().scale(scale).orient(sides[key]).tickFormat(o != null ? o.ticksFormatter : void 0); + if (o == null) { + return axis; + } + if (angular.isArray(o.ticks)) { + axis.tickValues(o.ticks); + } else if (angular.isNumber(o.ticks)) { + axis.ticks(o.ticks); + } else if (angular.isFunction(o.ticks)) { + axis.ticks(o.ticks, o.ticksInterval); + } + return axis; + }, + setScalesDomain: function(scales, data, series, svg, options) { + var axis, y2Domain, yDomain; + this.setXScale(scales.xScale, data, series, options.axes); + axis = svg.selectAll('.x.axis').call(scales.xAxis); + if (options.axes.x.ticksRotate != null) { + axis.selectAll('.tick>text').attr('dy', null).attr('transform', 'translate(0,5) rotate(' + options.axes.x.ticksRotate + ' 0,6)').style('text-anchor', options.axes.x.ticksRotate >= 0 ? 'start' : 'end'); + } + if ((series.filter(function(s) { + return s.axis === 'y' && s.visible !== false; + })).length > 0) { + yDomain = this.getVerticalDomain(options, data, series, 'y'); + scales.yScale.domain(yDomain).nice(); + axis = svg.selectAll('.y.axis').call(scales.yAxis); + if (options.axes.y.ticksRotate != null) { + axis.selectAll('.tick>text').attr('transform', 'rotate(' + options.axes.y.ticksRotate + ' -6,0)').style('text-anchor', 'end'); + } + } + if ((series.filter(function(s) { + return s.axis === 'y2' && s.visible !== false; + })).length > 0) { + y2Domain = this.getVerticalDomain(options, data, series, 'y2'); + scales.y2Scale.domain(y2Domain).nice(); + axis = svg.selectAll('.y2.axis').call(scales.y2Axis); + if (options.axes.y2.ticksRotate != null) { + return axis.selectAll('.tick>text').attr('transform', 'rotate(' + options.axes.y2.ticksRotate + ' 6,0)').style('text-anchor', 'start'); + } + } + }, + getVerticalDomain: function(options, data, series, key) { + var domain, mySeries, o; + if (!(o = options.axes[key])) { + return []; + } + if ((o.ticks != null) && angular.isArray(o.ticks)) { + return [o.ticks[0], o.ticks[o.ticks.length - 1]]; + } + mySeries = series.filter(function(s) { + return s.axis === key && s.visible !== false; + }); + domain = this.yExtent(series.filter(function(s) { + return s.axis === key && s.visible !== false; + }), data, options.stacks.filter(function(stack) { + return stack.axis === key; + })); + if (o.type === 'log') { + domain[0] = domain[0] === 0 ? 0.001 : domain[0]; + } + if (o.min != null) { + domain[0] = o.min; + } + if (o.max != null) { + domain[1] = o.max; + } + return domain; + }, + yExtent: function(series, data, stacks) { + var groups, maxY, minY; + minY = Number.POSITIVE_INFINITY; + maxY = Number.NEGATIVE_INFINITY; + groups = []; + stacks.forEach(function(stack) { + return groups.push(stack.series.map(function(id) { + return (series.filter(function(s) { + return s.id === id; + }))[0]; + })); + }); + series.forEach(function(series, i) { + var isInStack; + isInStack = false; + stacks.forEach(function(stack) { + var _ref; + if (_ref = series.id, __indexOf.call(stack.series, _ref) >= 0) { + return isInStack = true; + } + }); + if (!isInStack) { + return groups.push([series]); + } + }); + groups.forEach(function(group) { + group = group.filter(Boolean); + minY = Math.min(minY, d3.min(data, function(d) { + return group.reduce((function(a, s) { + return Math.min(a, d[s.y]); + }), Number.POSITIVE_INFINITY); + })); + return maxY = Math.max(maxY, d3.max(data, function(d) { + return group.reduce((function(a, s) { + return a + d[s.y]; + }), 0); + })); + }); + if (minY === maxY) { + if (minY > 0) { + return [0, minY * 2]; + } else { + return [minY * 2, 0]; + } + } + return [minY, maxY]; + }, + setXScale: function(xScale, data, series, axesOptions) { + var domain, o; + domain = this.xExtent(data, axesOptions.x.key); + if (series.filter(function(s) { + return s.type === 'column'; + }).length) { + this.adjustXDomainForColumns(domain, data, axesOptions.x.key); + } + o = axesOptions.x; + if (o.min != null) { + domain[0] = o.min; + } + if (o.max != null) { + domain[1] = o.max; + } + return xScale.domain(domain); + }, + xExtent: function(data, key) { + var from, to, _ref; + _ref = d3.extent(data, function(d) { + return d[key]; + }), from = _ref[0], to = _ref[1]; + if (from === to) { + if (from > 0) { + return [0, from * 2]; + } else { + return [from * 2, 0]; + } + } + return [from, to]; + }, + adjustXDomainForColumns: function(domain, data, field) { + var step; + step = this.getAverageStep(data, field); + if (angular.isDate(domain[0])) { + domain[0] = new Date(domain[0].getTime() - step); + return domain[1] = new Date(domain[1].getTime() + step); + } else { + domain[0] = domain[0] - step; + return domain[1] = domain[1] + step; + } + }, + getAverageStep: function(data, field) { + var i, n, sum; + if (!(data.length > 1)) { + return 0; + } + sum = 0; + n = data.length - 1; + i = 0; + while (i < n) { + sum += data[i + 1][field] - data[i][field]; + i++; + } + return sum / n; + }, + haveSecondYAxis: function(series) { + return !series.every(function(s) { + return s.axis !== 'y2'; + }); + }, + showScrubber: function(svg, glass, axes, data, options, dispatch, columnWidth) { + var that; + that = this; + glass.on('mousemove', function() { + svg.selectAll('.glass-container').attr('opacity', 1); + return that.updateScrubber(svg, d3.mouse(this), axes, data, options, dispatch, columnWidth); + }); + return glass.on('mouseout', function() { + glass.on('mousemove', null); + return svg.selectAll('.glass-container').attr('opacity', 0); + }); + }, + getClosestPoint: function(values, xValue) { + var d, d0, d1, i, xBisector; + xBisector = d3.bisector(function(d) { + return d.x; + }).left; + i = xBisector(values, xValue); + if (i === 0) { + return values[0]; + } + if (i > values.length - 1) { + return values[values.length - 1]; + } + d0 = values[i - 1]; + d1 = values[i]; + d = xValue - d0.x > d1.x - xValue ? d1 : d0; + return d; + }, + updateScrubber: function(svg, _arg, axes, data, options, dispatch, columnWidth) { + var ease, positions, that, tickLength, x, y; + x = _arg[0], y = _arg[1]; + ease = function(element) { + return element.transition().duration(50); + }; + that = this; + positions = []; + data.forEach(function(series, index) { + var color, item, lText, left, rText, right, side, sizes, text, v, xInvert, xPos, yInvert; + item = svg.select(".scrubberItem.series_" + index); + if (options.series[index].visible === false) { + item.attr('opacity', 0); + return; + } + item.attr('opacity', 1); + xInvert = axes.xScale.invert(x); + yInvert = axes.yScale.invert(y); + v = that.getClosestPoint(series.values, xInvert); + dispatch.focus(v, series.values.indexOf(v), [xInvert, yInvert]); + text = v.x + ' : ' + v.y; + if (options.tooltip.formatter) { + text = options.tooltip.formatter(v.x, v.y, options.series[index]); + } + right = item.select('.rightTT'); + rText = right.select('text'); + rText.text(text); + left = item.select('.leftTT'); + lText = left.select('text'); + lText.text(text); + sizes = { + right: that.getTextBBox(rText[0][0]).width + 5, + left: that.getTextBBox(lText[0][0]).width + 5 + }; + side = series.axis === 'y2' ? 'right' : 'left'; + xPos = axes.xScale(v.x); + if (side === 'left') { + if (xPos + that.getTextBBox(lText[0][0]).x - 10 < 0) { + side = 'right'; + } + } else if (side === 'right') { + if (xPos + sizes.right > that.getTextBBox(svg.select('.glass')[0][0]).width) { + side = 'left'; + } + } + if (side === 'left') { + ease(right).attr('opacity', 0); + ease(left).attr('opacity', 1); + } else { + ease(right).attr('opacity', 1); + ease(left).attr('opacity', 0); + } + positions[index] = { + index: index, + x: xPos, + y: axes[v.axis + 'Scale'](v.y + v.y0), + side: side, + sizes: sizes + }; + color = angular.isFunction(series.color) ? series.color(v, series.values.indexOf(v)) : series.color; + item.selectAll('circle').attr('stroke', color); + return item.selectAll('path').attr('fill', color); + }); + positions = this.preventOverlapping(positions); + tickLength = Math.max(15, 100 / columnWidth); + return data.forEach(function(series, index) { + var item, p, tt, xOffset; + if (options.series[index].visible === false) { + return; + } + p = positions[index]; + item = svg.select(".scrubberItem.series_" + index); + tt = item.select("." + p.side + "TT"); + xOffset = (p.side === 'left' ? series.xOffset : -series.xOffset); + tt.select('text').attr('transform', function() { + if (p.side === 'left') { + return "translate(" + (-3 - tickLength - xOffset) + ", " + (p.labelOffset + 3) + ")"; + } else { + return "translate(" + (4 + tickLength + xOffset) + ", " + (p.labelOffset + 3) + ")"; + } + }); + tt.select('path').attr('d', that.getScrubberPath(p.sizes[p.side] + 1, p.labelOffset, p.side, tickLength + xOffset)); + return ease(item).attr({ + 'transform': "translate(" + (positions[index].x + series.xOffset) + ", " + positions[index].y + ")" + }); + }); + }, + getScrubberPath: function(w, yOffset, side, padding) { + var h, p, xdir, ydir; + h = 18; + p = padding; + w = w; + xdir = side === 'left' ? 1 : -1; + ydir = 1; + if (yOffset !== 0) { + ydir = Math.abs(yOffset) / yOffset; + } + yOffset || (yOffset = 0); + return ["m0 0", "l" + xdir + " 0", "l0 " + (yOffset + ydir), "l" + (-xdir * (p + 1)) + " 0", "l0 " + (-h / 2 - ydir), "l" + (-xdir * w) + " 0", "l0 " + h, "l" + (xdir * w) + " 0", "l0 " + (-h / 2 - ydir), "l" + (xdir * (p - 1)) + " 0", "l0 " + (-yOffset + ydir), "l1 0", "z"].join(''); + }, + preventOverlapping: function(positions) { + var abscissas, getNeighbours, h, offset; + h = 18; + abscissas = {}; + positions.forEach(function(p) { + var _name; + abscissas[_name = p.x] || (abscissas[_name] = { + left: [], + right: [] + }); + return abscissas[p.x][p.side].push(p); + }); + getNeighbours = function(side) { + var foundNeighbour, neighbourhood, neighbours, neighboursForX, p, sides, x, y, _ref; + neighbours = []; + for (x in abscissas) { + sides = abscissas[x]; + if (sides[side].length === 0) { + continue; + } + neighboursForX = {}; + while (sides[side].length > 0) { + p = sides[side].pop(); + foundNeighbour = false; + for (y in neighboursForX) { + neighbourhood = neighboursForX[y]; + if ((+y - h <= (_ref = p.y) && _ref <= +y + h)) { + neighbourhood.push(p); + foundNeighbour = true; + } + } + if (!foundNeighbour) { + neighboursForX[p.y] = [p]; + } + } + neighbours.push(neighboursForX); + } + return neighbours; + }; + offset = function(neighboursForAbscissas) { + var abs, n, neighbours, start, step, xNeighbours, y; + step = 20; + for (abs in neighboursForAbscissas) { + xNeighbours = neighboursForAbscissas[abs]; + for (y in xNeighbours) { + neighbours = xNeighbours[y]; + n = neighbours.length; + if (n === 1) { + neighbours[0].labelOffset = 0; + continue; + } + neighbours = neighbours.sort(function(a, b) { + return a.y - b.y; + }); + if (n % 2 === 0) { + start = -(step / 2) * (n / 2); + } else { + start = -(n - 1) / 2 * step; + } + neighbours.forEach(function(neighbour, i) { + return neighbour.labelOffset = start + step * i; + }); + } + } + }; + offset(getNeighbours('left')); + offset(getNeighbours('right')); + return positions; + }, + getTooltipHandlers: function(options) { + if (options.tooltip.mode === 'scrubber') { + return { + onChartHover: angular.bind(this, this.showScrubber) + }; + } else { + return { + onMouseOver: angular.bind(this, this.onMouseOver), + onMouseOut: angular.bind(this, this.onMouseOut) + }; + } + }, + styleTooltip: function(d3TextElement) { + return d3TextElement.attr({ + 'font-family': 'monospace', + 'font-size': 10, + 'fill': 'white', + 'text-rendering': 'geometric-precision' + }); + }, + addTooltips: function(svg, dimensions, axesOptions) { + var h, height, p, w, width, xTooltip, y2Tooltip, yTooltip; + width = dimensions.width; + height = dimensions.height; + width = width - dimensions.left - dimensions.right; + height = height - dimensions.top - dimensions.bottom; + w = 24; + h = 18; + p = 5; + xTooltip = svg.append('g').attr({ + 'id': 'xTooltip', + 'class': 'xTooltip', + 'opacity': 0 + }); + xTooltip.append('path').attr('transform', "translate(0," + (height + 1) + ")"); + this.styleTooltip(xTooltip.append('text').style('text-anchor', 'middle').attr({ + 'width': w, + 'height': h, + 'transform': 'translate(0,' + (height + 19) + ')' + })); + yTooltip = svg.append('g').attr({ + id: 'yTooltip', + "class": 'yTooltip', + opacity: 0 + }); + yTooltip.append('path'); + this.styleTooltip(yTooltip.append('text').attr({ + 'width': h, + 'height': w + })); + if (axesOptions.y2 != null) { + y2Tooltip = svg.append('g').attr({ + 'id': 'y2Tooltip', + 'class': 'y2Tooltip', + 'opacity': 0, + 'transform': 'translate(' + width + ',0)' + }); + y2Tooltip.append('path'); + return this.styleTooltip(y2Tooltip.append('text').attr({ + 'width': h, + 'height': w + })); + } + }, + onMouseOver: function(svg, event, axesOptions) { + this.updateXTooltip(svg, event, axesOptions.x); + if (event.series.axis === 'y2') { + return this.updateY2Tooltip(svg, event, axesOptions.y2); + } else { + return this.updateYTooltip(svg, event, axesOptions.y); + } + }, + onMouseOut: function(svg) { + return this.hideTooltips(svg); + }, + updateXTooltip: function(svg, _arg, xAxisOptions) { + var color, datum, label, series, textX, x, xTooltip, _f; + x = _arg.x, datum = _arg.datum, series = _arg.series; + xTooltip = svg.select("#xTooltip"); + xTooltip.transition().attr({ + 'opacity': 1.0, + 'transform': "translate(" + x + ",0)" + }); + _f = xAxisOptions.tooltipFormatter; + textX = _f ? _f(datum.x) : datum.x; + label = xTooltip.select('text'); + label.text(textX); + color = angular.isFunction(series.color) ? series.color(datum, series.values.indexOf(datum)) : series.color; + return xTooltip.select('path').style('fill', color).attr('d', this.getXTooltipPath(label[0][0])); + }, + getXTooltipPath: function(textElement) { + var h, p, w; + w = Math.max(this.getTextBBox(textElement).width, 15); + h = 18; + p = 5; + return 'm-' + w / 2 + ' ' + p + ' ' + 'l0 ' + h + ' ' + 'l' + w + ' 0 ' + 'l0 ' + '' + (-h) + 'l' + (-w / 2 + p) + ' 0 ' + 'l' + (-p) + ' -' + h / 4 + ' ' + 'l' + (-p) + ' ' + h / 4 + ' ' + 'l' + (-w / 2 + p) + ' 0z'; + }, + updateYTooltip: function(svg, _arg, yAxisOptions) { + var color, datum, label, series, textY, w, y, yTooltip, _f; + y = _arg.y, datum = _arg.datum, series = _arg.series; + yTooltip = svg.select("#yTooltip"); + yTooltip.transition().attr({ + 'opacity': 1.0, + 'transform': "translate(0, " + y + ")" + }); + _f = yAxisOptions.tooltipFormatter; + textY = _f ? _f(datum.y) : datum.y; + label = yTooltip.select('text'); + label.text(textY); + w = this.getTextBBox(label[0][0]).width + 5; + label.attr({ + 'transform': 'translate(' + (-w - 2) + ',3)', + 'width': w + }); + color = angular.isFunction(series.color) ? series.color(datum, series.values.indexOf(datum)) : series.color; + return yTooltip.select('path').style('fill', color).attr('d', this.getYTooltipPath(w)); + }, + updateY2Tooltip: function(svg, _arg, yAxisOptions) { + var color, datum, label, series, textY, w, y, y2Tooltip, _f; + y = _arg.y, datum = _arg.datum, series = _arg.series; + y2Tooltip = svg.select("#y2Tooltip"); + y2Tooltip.transition().attr('opacity', 1.0); + _f = yAxisOptions.tooltipFormatter; + textY = _f ? _f(datum.y) : datum.y; + label = y2Tooltip.select('text'); + label.text(textY); + w = this.getTextBBox(label[0][0]).width + 5; + label.attr({ + 'transform': 'translate(7, ' + (parseFloat(y) + 3) + ')', + 'w': w + }); + color = angular.isFunction(series.color) ? series.color(datum, series.values.indexOf(datum)) : series.color; + return y2Tooltip.select('path').style('fill', color).attr({ + 'd': this.getY2TooltipPath(w), + 'transform': 'translate(0, ' + y + ')' + }); + }, + getYTooltipPath: function(w) { + var h, p; + h = 18; + p = 5; + return 'm0 0' + 'l' + (-p) + ' ' + (-p) + ' ' + 'l0 ' + (-h / 2 + p) + ' ' + 'l' + (-w) + ' 0 ' + 'l0 ' + h + ' ' + 'l' + w + ' 0 ' + 'l0 ' + (-h / 2 + p) + 'l' + (-p) + ' ' + p + 'z'; + }, + getY2TooltipPath: function(w) { + var h, p; + h = 18; + p = 5; + return 'm0 0' + 'l' + p + ' ' + p + ' ' + 'l0 ' + (h / 2 - p) + ' ' + 'l' + w + ' 0 ' + 'l0 ' + (-h) + ' ' + 'l' + (-w) + ' 0 ' + 'l0 ' + (h / 2 - p) + ' ' + 'l' + (-p) + ' ' + p + 'z'; + }, + hideTooltips: function(svg) { + svg.select("#xTooltip").transition().attr('opacity', 0); + svg.select("#yTooltip").transition().attr('opacity', 0); + return svg.select("#y2Tooltip").transition().attr('opacity', 0); + } + }; + } +]);