diff --git a/bower.json b/bower.json index 3385707..b5ecc01 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "angular-charts", - "version": "0.0.1", + "version": "0.1.0", "authors": [ "chinmaymk" ], diff --git a/dist/angular-charts.js b/dist/angular-charts.js new file mode 100644 index 0000000..3e840f7 --- /dev/null +++ b/dist/angular-charts.js @@ -0,0 +1,872 @@ +/** +* Main module +*/ +angular.module('angularCharts', ['angularChartsTemplates']); +/** +* Main directive handling drawing of all charts +*/ +angular.module('angularCharts').directive('acChart', [ + '$templateCache', + '$compile', + '$window', + '$timeout', + function ($templateCache, $compile, $window, $timeout) { + /** + * Initialize some constants + * @type Array + */ + var tooltip = [ + 'display:none;', + 'position:absolute;', + 'border:1px solid #333;', + 'background-color:#161616;', + 'border-radius:5px;', + 'padding:5px;', + 'color:#fff;' + ].join(''); + /** + * Utility function to call when we run out of colors! + * @return {[type]} [description] + */ + function getRandomColor() { + var letters = '0123456789ABCDEF'.split(''); + var color = '#'; + for (var i = 0; i < 6; i++) { + color += letters[Math.round(Math.random() * 15)]; + } + return color; + } + /** + * Main link function + * @param {[type]} scope [description] + * @param {[type]} element [description] + * @param {[type]} attrs [description] + * @return {[type]} [description] + */ + function link(scope, element, attrs) { + var config = { + title: '', + tooltips: true, + labels: false, + mouseover: function () { + }, + mouseout: function () { + }, + click: function () { + }, + legend: { + display: true, + position: 'left' + }, + colors: [ + 'steelBlue', + 'rgb(255,153,0)', + 'rgb(220,57,18)', + 'rgb(70,132,238)', + 'rgb(73,66,204)', + 'rgb(0,128,0)' + ], + innerRadius: 0, + lineLegend: 'lineEnd' + }; + var totalWidth = element.width(), totalHeight = element.height(); + var data, series, points, height, width, chartContainer, legendContainer, chartType, isAnimate = true, defaultColors = config.colors; + if (totalHeight === 0 || totalWidth === 0) { + throw new Error('Please set height and width for the chart element'); + } + /** + * All the magic happens here + * handles extracting chart type + * getting data + * validating data + * drawing the chart + * @return {[type]} [description] + */ + function init() { + prepareData(); + setHeightWidth(); + setContainers(); + var chartFunc = getChartFunction(chartType); + chartFunc(); + drawLegend(); + } + /** + * Sets height and width of chart area based on legend + * used for setting radius, bar width of chart + */ + function setHeightWidth() { + if (!config.legend.display) { + height = totalHeight; + width = totalWidth; + return; + } + switch (config.legend.position) { + case 'top': + case 'bottom': + height = totalHeight * 0.75; + width = totalWidth; + break; + case 'left': + case 'right': + height = totalHeight; + width = totalWidth * 0.75; + break; + } + } + /** + * Creates appropriate DOM structure for legend + chart + */ + function setContainers() { + var container = $templateCache.get(config.legend.position); + element.html($compile(container)(scope)); + chartContainer = element.find('.ac-chart'); + legendContainer = element.find('.ac-legend'); + height -= element.find('.ac-title').height(); + } + /** + * Parses data from attributes + * @return {[type]} [description] + */ + function prepareData() { + data = scope.acData; + chartType = scope.acChart; + series = data ? data.series || [] : []; + points = data ? data.data || [] : []; + if (scope.acConfig) { + angular.extend(config, scope.acConfig); + config.colors = config.colors.concat(defaultColors); + } + } + /** + * Returns appropriate chart function to call + * @param {[type]} type [description] + * @return {[type]} [description] + */ + function getChartFunction(type) { + var charts = { + 'pie': pieChart, + 'bar': barChart, + 'line': lineChart, + 'area': areaChart, + 'point': pointChart + }; + return charts[type]; + } + /** + * Filters down the x axis labels if a limit is specified + */ + function filterXAxis(xAxis, x) { + var allTicks = x.domain(); + if (config.xAxisMaxTicks && allTicks.length > config.xAxisMaxTicks) { + var mod = Math.ceil(allTicks.length / config.xAxisMaxTicks); + xAxis.tickValues(allTicks.filter(function (e, i) { + return i % mod == 0; + })); + } + } + /** + * Draws a bar chart, grouped with negative value handling + * @return {[type]} [description] + */ + function barChart() { + /** + * Setup date attributes + * @type {Object} + */ + var margin = { + top: 0, + right: 20, + bottom: 30, + left: 40 + }; + width -= margin.left + margin.right; + height -= margin.top + margin.bottom; + var x = d3.scale.ordinal().rangeRoundBands([ + 0, + width + ], 0.1); + var y = d3.scale.linear().range([ + height, + 10 + ]); + var x0 = d3.scale.ordinal().rangeRoundBands([ + 0, + width + ], 0.1); + var yData = [0]; + points.forEach(function (d) { + d.nicedata = d.y.map(function (e, i) { + yData.push(e); + return { + x: d.x, + y: e, + s: i, + tooltip: angular.isArray(d.tooltip) ? d.tooltip[i] : d.tooltip + }; + }); + }); + var yMaxPoints = d3.max(points.map(function (d) { + return d.y.length; + })); + scope.yMaxData = yMaxPoints; + x.domain(points.map(function (d) { + return d.x; + })); + var padding = d3.max(yData) * 0.2; + y.domain([ + d3.min(yData), + d3.max(yData) + padding + ]); + x0.domain(d3.range(yMaxPoints)).rangeRoundBands([ + 0, + x.rangeBand() + ]); + /** + * Create scales using d3 + * @type {[type]} + */ + var xAxis = d3.svg.axis().scale(x).orient('bottom'); + filterXAxis(xAxis, x); + var yAxis = d3.svg.axis().scale(y).orient('left').ticks(10).tickFormat(d3.format('s')); + /** + * Start drawing the chart! + * @type {[type]} + */ + var svg = d3.select(chartContainer[0]).append('svg').attr('width', width + margin.left + margin.right).attr('height', height + margin.top + margin.bottom).append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + svg.append('g').attr('class', 'x axis').attr('transform', 'translate(0,' + height + ')').call(xAxis); + svg.append('g').attr('class', 'y axis').call(yAxis); + /** + * Add bars + * @type {[type]} + */ + var barGroups = svg.selectAll('.state').data(points).enter().append('g').attr('class', 'g').attr('transform', function (d) { + return 'translate(' + x(d.x) + ',0)'; + }); + var bars = barGroups.selectAll('rect').data(function (d) { + return d.nicedata; + }).enter().append('rect'); + bars.attr('width', x0.rangeBand()); + bars.attr('x', function (d, i) { + return x0(i); + }).attr('y', height).style('fill', function (d) { + return getColor(d.s); + }).attr('height', 0).transition().ease('cubic-in-out').duration(1000).attr('y', function (d) { + return y(Math.max(0, d.y)); + }).attr('height', function (d) { + return Math.abs(y(d.y) - y(0)); + }); + /** + * Add events for tooltip + * @param {[type]} d [description] + * @return {[type]} [description] + */ + bars.on('mouseover', function (d) { + makeToolTip({ + value: d.y, + series: series[d.s], + index: d.x + }, d3.event); + config.mouseover(d, d3.event); + scope.$apply(); + }).on('mouseleave', function (d) { + removeToolTip(); + config.mouseout(d, d3.event); + scope.$apply(); + }).on('mousemove', function (d) { + updateToolTip(d3.event); + }).on('click', function (d) { + config.click.call(d, d3.event); + scope.$apply(); + }); + /** + * Create labels + */ + if (config.labels) { + barGroups.selectAll('not-a-class').data(function (d) { + return d.nicedata; + }).enter().append('text').attr('x', function (d, i) { + return x0(i); + }).attr('y', function (d) { + return height - Math.abs(y(d.y) - y(0)); + }).text(function (d) { + return d.y; + }); + } + /** + * Draw one zero line in case negative values exist + */ + svg.append('line').attr('x1', width).attr('y1', y(0)).attr('y2', y(0)).style('stroke', 'silver'); + } + /** + * Draws a line chart + * @return {[type]} [description] + */ + function lineChart() { + var margin = { + top: 0, + right: 40, + bottom: 20, + left: 40 + }; + width -= margin.left + margin.right; + height -= margin.top + margin.bottom; + var x = d3.scale.ordinal().domain(points.map(function (d) { + return d.x; + })).rangeRoundBands([ + 0, + width + ]); + var y = d3.scale.linear().range([ + height, + 10 + ]); + var xAxis = d3.svg.axis().scale(x).orient('bottom'); + filterXAxis(xAxis, x); + var yAxis = d3.svg.axis().scale(y).orient('left').ticks(5).tickFormat(d3.format('s')); + var line = d3.svg.line().interpolate('cardinal').x(function (d) { + return getX(d.x); + }).y(function (d) { + return y(d.y); + }); + var yData = [0]; + var linedata = []; + points.forEach(function (d) { + d.y.map(function (e, i) { + yData.push(e); + }); + }); + var yMaxPoints = d3.max(points.map(function (d) { + return d.y.length; + })); + scope.yMaxData = yMaxPoints; + series.slice(0, yMaxPoints).forEach(function (value, index) { + var d = {}; + d.series = value; + d.values = points.map(function (point) { + return point.y.map(function (e) { + return { + x: point.x, + y: e + }; + })[index] || { + x: points[index].x, + y: 0 + }; + }); + linedata.push(d); + }); + var svg = d3.select(chartContainer[0]).append('svg').attr('width', width + margin.left + margin.right).attr('height', height + margin.top + margin.bottom).append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + var padding = d3.max(yData) * 0.2; + y.domain([ + d3.min(yData), + d3.max(yData) + padding + ]); + svg.append('g').attr('class', 'x axis').attr('transform', 'translate(0,' + height + ')').call(xAxis); + svg.append('g').attr('class', 'y axis').call(yAxis); + var point = svg.selectAll('.points').data(linedata).enter().append('g'); + path = point.attr('points', 'points').append('path').attr('class', 'ac-line').style('stroke', function (d, i) { + return getColor(i); + }).attr('d', function (d) { + return line(d.values); + }).attr('stroke-width', '2').attr('fill', 'none'); + /** Animation function + * [last description] + * @type {[type]} + */ + var last = linedata[linedata.length - 1].values; + var totalLength = path.node().getTotalLength() + getX(last[last.length - 1].x); + path.attr('stroke-dasharray', totalLength + ' ' + totalLength).attr('stroke-dashoffset', totalLength).transition().duration(1500).ease('linear').attr('stroke-dashoffset', 0).attr('d', function (d) { + return line(d.values); + }); + /** + * Add points + * @param {[type]} value [description] + * @param {[type]} key [description] + * @return {[type]} [description] + */ + angular.forEach(linedata, function (value, key) { + var points = svg.selectAll('.circle').data(value.values).enter(); + points.append('circle').attr('cx', function (d) { + return getX(d.x); + }).attr('cy', function (d) { + return y(d.y); + }).attr('r', 3).style('fill', getColor(linedata.indexOf(value))).style('stroke', getColor(linedata.indexOf(value))).on('mouseover', function (series) { + return function (d) { + makeToolTip({ + index: d.x, + value: d.y, + series: series + }, d3.event); + config.mouseover(d, d3.event); + scope.$apply(); + }; + }(value.series)).on('mouseleave', function (d) { + removeToolTip(); + config.mouseout(d, d3.event); + scope.$apply(); + }).on('mousemove', function (d) { + updateToolTip(d3.event); + }).on('click', function (d) { + config.click(d, d3.event); + scope.$apply(); + }); + if (config.labels) { + points.append('text').attr('x', function (d) { + return getX(d.x); + }).attr('y', function (d) { + return y(d.y); + }).text(function (d) { + return d.y; + }); + } + }); + /** + * Labels at the end of line + */ + if (config.lineLegend === 'lineEnd') { + point.append('text').datum(function (d) { + return { + name: d.series, + value: d.values[d.values.length - 1] + }; + }).attr('transform', function (d) { + return 'translate(' + getX(d.value.x) + ',' + y(d.value.y) + ')'; + }).attr('x', 3).text(function (d) { + return d.name; + }); + } + /** + * Returns x point of line point + * @param {[type]} d [description] + * @return {[type]} [description] + */ + function getX(d) { + return Math.round(x(d)) + x.rangeBand() / 2; + } + ; + return linedata; + } + /** + * Creates a nice area chart + * @return {[type]} [description] + */ + function areaChart() { + var margin = { + top: 0, + right: 40, + bottom: 20, + left: 40 + }; + width -= margin.left + margin.right; + height -= margin.top + margin.bottom; + var x = d3.scale.ordinal().domain(points.map(function (d) { + return d.x; + })).rangeRoundBands([ + 0, + width + ]); + var y = d3.scale.linear().range([ + height, + 10 + ]); + var xAxis = d3.svg.axis().scale(x).orient('bottom'); + filterXAxis(xAxis, x); + var yAxis = d3.svg.axis().scale(y).orient('left').ticks(5).tickFormat(d3.format('s')); + var line = d3.svg.line().interpolate('cardinal').x(function (d) { + return getX(d.x); + }).y(function (d) { + return y(d.y); + }); + var yData = [0]; + var linedata = []; + points.forEach(function (d) { + d.y.map(function (e, i) { + yData.push(e); + }); + }); + var yMaxPoints = d3.max(points.map(function (d) { + return d.y.length; + })); + /** + * Important to set for legend + * @type {[type]} + */ + scope.yMaxData = yMaxPoints; + series.slice(0, yMaxPoints).forEach(function (value, index) { + var d = {}; + d.series = value; + d.values = points.map(function (point) { + return point.y.map(function (e) { + return { + x: point.x, + y: e + }; + })[index] || { + x: points[index].x, + y: 0 + }; + }); + linedata.push(d); + }); + var svg = d3.select(chartContainer[0]).append('svg').attr('width', width + margin.left + margin.right).attr('height', height + margin.top + margin.bottom).append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + var padding = d3.max(yData) * 0.2; + y.domain([ + d3.min(yData), + d3.max(yData) + padding + ]); + svg.append('g').attr('class', 'x axis').attr('transform', 'translate(0,' + height + ')').call(xAxis); + svg.append('g').attr('class', 'y axis').call(yAxis); + var point = svg.selectAll('.points').data(linedata).enter().append('g'); + var area = d3.svg.area().interpolate('basis').x(function (d) { + return getX(d.x); + }).y0(function (d) { + return y(0); + }).y1(function (d) { + return y(0 + d.y); + }); + point.append('path').attr('class', 'area').attr('d', function (d) { + return area(d.values); + }).style('fill', function (d, i) { + return getColor(i); + }).style('opacity', '0.7'); + function getX(d) { + return Math.round(x(d)) + x.rangeBand() / 2; + } + ; + } + /** + * Draws a beautiful pie chart + * @return {[type]} [description] + */ + function pieChart() { + var radius = Math.min(width, height) / 2; + var svg = d3.select(chartContainer[0]).append('svg').attr('width', width).attr('height', height).append('g').attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')'); + var innerRadius = 0; + if (config.innerRadius) { + var configRadius = config.innerRadius; + if (typeof configRadius === 'string' && configRadius.indexOf('%') > 0) { + configRadius = radius * (1 - parseFloat(configRadius) * 0.01); + } + if (configRadius) { + innerRadius = radius - Number(configRadius); + } + } + scope.yMaxData = points.length; + var arc = d3.svg.arc().outerRadius(radius - 10).innerRadius(innerRadius); + var arcOver = d3.svg.arc().outerRadius(radius + 5).innerRadius(0); + var pie = d3.layout.pie().sort(null).value(function (d) { + return d.y[0]; + }); + var path = svg.selectAll('.arc').data(pie(points)).enter().append('g'); + var arcs = path.append('path').style('fill', function (d, i) { + return getColor(i); + }).transition().ease('linear').duration(500).attrTween('d', tweenPie).attr('class', 'arc'); + path.on('mouseover', function (d) { + makeToolTip({ value: d.data.y[0] }, d3.event); + d3.select(this).select('path').transition().duration(200).style('stroke', 'white').style('stroke-width', '2px'); + config.mouseover(d, d3.event); + scope.$apply(); + }).on('mouseleave', function (d) { + d3.select(this).select('path').transition().duration(200).style('stroke', '').style('stroke-width', ''); + removeToolTip(); + config.mouseout(d, d3.event); + scope.$apply(); + }).on('mousemove', function (d) { + updateToolTip(d3.event); + }).on('click', function (d) { + config.click(d, d3.event); + scope.$apply(); + }); + if (!!config.labels) { + path.append('text').attr('transform', function (d) { + return 'translate(' + arc.centroid(d) + ')'; + }).attr('dy', '.35em').style('text-anchor', 'middle').text(function (d) { + return d.data.y[0]; + }); + } + function tweenPie(b) { + b.innerRadius = 0; + var i = d3.interpolate({ + startAngle: 0, + endAngle: 0 + }, b); + return function (t) { + return arc(i(t)); + }; + } + } + function pointChart() { + var margin = { + top: 0, + right: 40, + bottom: 20, + left: 40 + }; + width -= margin.left - margin.right; + height -= margin.top - margin.bottom; + var x = d3.scale.ordinal().domain(points.map(function (d) { + return d.x; + })).rangeRoundBands([ + 0, + width + ]); + var y = d3.scale.linear().range([ + height, + 10 + ]); + var xAxis = d3.svg.axis().scale(x).orient('bottom'); + filterXAxis(xAxis, x); + var yAxis = d3.svg.axis().scale(y).orient('left').ticks(5).tickFormat(d3.format('s')); + var yData = [0]; + var linedata = []; + points.forEach(function (d) { + d.y.map(function (e, i) { + yData.push(e); + }); + }); + var yMaxPoints = d3.max(points.map(function (d) { + return d.y.length; + })); + scope.yMaxPoints = yMaxPoints; + series.slice(0, yMaxPoints).forEach(function (value, index) { + var d = {}; + d.series = value; + d.values = points.map(function (point) { + return point.y.map(function (e) { + return { + x: point.x, + y: e + }; + })[index] || { + x: points[index].x, + y: 0 + }; + }); + linedata.push(d); + }); + var svg = d3.select(chartContainer[0]).append('svg').attr('width', width + margin.left + margin.right).attr('height', height + margin.top + margin.bottom).append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + var padding = d3.max(yData) * 0.2; + y.domain([ + d3.min(yData), + d3.max(yData) + padding + ]); + svg.append('g').attr('class', 'x axis').attr('transform', 'translate(0,' + height + ')').call(xAxis); + svg.append('g').attr('class', 'y axis').call(yAxis); + var point = svg.selectAll('.points').data(linedata).enter().append('g'); + /** + * Add points + * @param {[type]} value [description] + * @param {[type]} key [description] + * @return {[type]} [description] + */ + angular.forEach(linedata, function (value, key) { + var points = svg.selectAll('.circle').data(value.values).enter(); + points.append('circle').attr('cx', function (d) { + return getX(d.x); + }).attr('cy', function (d) { + return y(d.y); + }).attr('r', 3).style('fill', getColor(linedata.indexOf(value))).style('stroke', getColor(linedata.indexOf(value))).on('mouseover', function (series) { + return function (d) { + makeToolTip({ + index: d.x, + value: d.y, + series: series + }, d3.event); + config.mouseover(d, d3.event); + scope.$apply(); + }; + }(value.series)).on('mouseleave', function (d) { + removeToolTip(); + config.mouseout(d, d3.event); + scope.$apply(); + }).on('mousemove', function (d) { + updateToolTip(d3.event); + }).on('click', function (d) { + config.click(d, d3.event); + scope.$apply(); + }); + if (config.labels) { + points.append('text').attr('x', function (d) { + return getX(d.x); + }).attr('y', function (d) { + return y(d.y); + }).text(function (d) { + return d.y; + }); + } + }); + /** + * Returns x point of line point + * @param {[type]} d [description] + * @return {[type]} [description] + */ + function getX(d) { + return Math.round(x(d)) + x.rangeBand() / 2; + } + ; + } + /** + * Creates and displays tooltip + * @return {[type]} [description] + */ + function makeToolTip(data, event) { + if (!config.tooltips) { + return; + } + if (Object.prototype.toString.call(config.tooltips) == '[object Function]') { + data = config.tooltips(data); + } else { + data = data.value; + } + angular.element('

').html(data).appendTo('body').fadeIn('slow').css({ + left: event.pageX + 20, + top: event.pageY - 30 + }); + } + /** + * Clears the tooltip from body + * @return {[type]} [description] + */ + function removeToolTip() { + angular.element('.ac-tooltip').remove(); + } + function updateToolTip(event) { + angular.element('.ac-tooltip').css({ + left: event.pageX + 20, + top: event.pageY - 30 + }); + } + /** + * Adds data to legend + * @return {[type]} [description] + */ + function drawLegend() { + scope.legends = []; + if (chartType == 'pie') { + angular.forEach(points, function (value, key) { + scope.legends.push({ + color: config.colors[key], + title: value.x + }); + }); + } + if (chartType == 'bar' || chartType == 'area' || chartType == 'point' || chartType == 'line' && config.lineLegend === 'traditional') { + angular.forEach(series, function (value, key) { + scope.legends.push({ + color: config.colors[key], + title: value + }); + }); + } + } + /** + * Checks if index is available in color + * else returns a random color + * @param {[type]} i [description] + * @return {[type]} [description] + */ + function getColor(i) { + if (i < config.colors.length) { + return config.colors[i]; + } else { + var color = getRandomColor(); + config.colors.push(color); + return color; + } + } + var w = angular.element($window); + var resizePromise = null; + w.bind('resize', function (ev) { + resizePromise && $timeout.cancel(resizePromise); + resizePromise = $timeout(function () { + totalWidth = element.width(); + totalHeight = element.height(); + init(); + }, 100); + }); + scope.getWindowDimensions = function () { + return { + 'h': w.height(), + 'w': w.width() + }; + }; + //let the party begin! + //add some watchers + scope.$watch('acChart', function () { + init(); + }, true); + scope.$watch('acData', function () { + init(); + }, true); + scope.$watch('acConfig', function () { + init(); + }, true); + } + return { + restrict: 'EA', + link: link, + transclude: 'true', + scope: { + acChart: '=', + acData: '=', + acConfig: '=' + } + }; + } +]); +angular.module('angularChartsTemplates', ['left', 'right']); + +angular.module("left", []).run(["$templateCache", function($templateCache) { + $templateCache.put("left", + "\n" + + "\n" + + "\n" + + "
{{acConfig.title}}
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "
\n" + + "
\n" + + "
"); +}]); + +angular.module("right", []).run(["$templateCache", function($templateCache) { + $templateCache.put("right", + "\n" + + "\n" + + "
{{acConfig.title}}
\n" + + "
\n" + + "
\n" + + "
\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + "
"); +}]); diff --git a/dist/angular-charts.min.js b/dist/angular-charts.min.js new file mode 100644 index 0000000..a9c853a --- /dev/null +++ b/dist/angular-charts.min.js @@ -0,0 +1 @@ +angular.module("angularCharts",["angularChartsTemplates"]),angular.module("angularCharts").directive("acChart",["$templateCache","$compile","$window","$timeout",function(a,b,c,d){function e(){for(var a="0123456789ABCDEF".split(""),b="#",c=0;6>c;c++)b+=a[Math.round(15*Math.random())];return b}function f(f,h){function i(){l(),j(),k();var a=m(F);a(),w()}function j(){if(!G.legend.display)return B=I,void(C=H);switch(G.legend.position){case"top":case"bottom":B=.75*I,C=H;break;case"left":case"right":B=I,C=.75*H}}function k(){var c=a.get(G.legend.position);h.html(b(c)(f)),D=h.find(".ac-chart"),E=h.find(".ac-legend"),B-=h.find(".ac-title").height()}function l(){y=f.acData,F=f.acChart,z=y?y.series||[]:[],A=y?y.data||[]:[],f.acConfig&&(angular.extend(G,f.acConfig),G.colors=G.colors.concat(J))}function m(a){var b={pie:r,bar:o,line:p,area:q,point:s};return b[a]}function n(a,b){var c=b.domain();if(G.xAxisMaxTicks&&c.length>G.xAxisMaxTicks){var d=Math.ceil(c.length/G.xAxisMaxTicks);a.tickValues(c.filter(function(a,b){return b%d==0}))}}function o(){var a={top:0,right:20,bottom:30,left:40};C-=a.left+a.right,B-=a.top+a.bottom;var b=d3.scale.ordinal().rangeRoundBands([0,C],.1),c=d3.scale.linear().range([B,10]),d=d3.scale.ordinal().rangeRoundBands([0,C],.1),e=[0];A.forEach(function(a){a.nicedata=a.y.map(function(b,c){return e.push(b),{x:a.x,y:b,s:c,tooltip:angular.isArray(a.tooltip)?a.tooltip[c]:a.tooltip}})});var g=d3.max(A.map(function(a){return a.y.length}));f.yMaxData=g,b.domain(A.map(function(a){return a.x}));var h=.2*d3.max(e);c.domain([d3.min(e),d3.max(e)+h]),d.domain(d3.range(g)).rangeRoundBands([0,b.rangeBand()]);var i=d3.svg.axis().scale(b).orient("bottom");n(i,b);var j=d3.svg.axis().scale(c).orient("left").ticks(10).tickFormat(d3.format("s")),k=d3.select(D[0]).append("svg").attr("width",C+a.left+a.right).attr("height",B+a.top+a.bottom).append("g").attr("transform","translate("+a.left+","+a.top+")");k.append("g").attr("class","x axis").attr("transform","translate(0,"+B+")").call(i),k.append("g").attr("class","y axis").call(j);var l=k.selectAll(".state").data(A).enter().append("g").attr("class","g").attr("transform",function(a){return"translate("+b(a.x)+",0)"}),m=l.selectAll("rect").data(function(a){return a.nicedata}).enter().append("rect");m.attr("width",d.rangeBand()),m.attr("x",function(a,b){return d(b)}).attr("y",B).style("fill",function(a){return x(a.s)}).attr("height",0).transition().ease("cubic-in-out").duration(1e3).attr("y",function(a){return c(Math.max(0,a.y))}).attr("height",function(a){return Math.abs(c(a.y)-c(0))}),m.on("mouseover",function(a){t({value:a.y,series:z[a.s],index:a.x},d3.event),G.mouseover(a,d3.event),f.$apply()}).on("mouseleave",function(a){u(),G.mouseout(a,d3.event),f.$apply()}).on("mousemove",function(){v(d3.event)}).on("click",function(a){G.click.call(a,d3.event),f.$apply()}),G.labels&&l.selectAll("not-a-class").data(function(a){return a.nicedata}).enter().append("text").attr("x",function(a,b){return d(b)}).attr("y",function(a){return B-Math.abs(c(a.y)-c(0))}).text(function(a){return a.y}),k.append("line").attr("x1",C).attr("y1",c(0)).attr("y2",c(0)).style("stroke","silver")}function p(){function a(a){return Math.round(c(a))+c.rangeBand()/2}var b={top:0,right:40,bottom:20,left:40};C-=b.left+b.right,B-=b.top+b.bottom;var c=d3.scale.ordinal().domain(A.map(function(a){return a.x})).rangeRoundBands([0,C]),d=d3.scale.linear().range([B,10]),e=d3.svg.axis().scale(c).orient("bottom");n(e,c);var g=d3.svg.axis().scale(d).orient("left").ticks(5).tickFormat(d3.format("s")),h=d3.svg.line().interpolate("cardinal").x(function(b){return a(b.x)}).y(function(a){return d(a.y)}),i=[0],j=[];A.forEach(function(a){a.y.map(function(a){i.push(a)})});var k=d3.max(A.map(function(a){return a.y.length}));f.yMaxData=k,z.slice(0,k).forEach(function(a,b){var c={};c.series=a,c.values=A.map(function(a){return a.y.map(function(b){return{x:a.x,y:b}})[b]||{x:A[b].x,y:0}}),j.push(c)});var l=d3.select(D[0]).append("svg").attr("width",C+b.left+b.right).attr("height",B+b.top+b.bottom).append("g").attr("transform","translate("+b.left+","+b.top+")"),m=.2*d3.max(i);d.domain([d3.min(i),d3.max(i)+m]),l.append("g").attr("class","x axis").attr("transform","translate(0,"+B+")").call(e),l.append("g").attr("class","y axis").call(g);var o=l.selectAll(".points").data(j).enter().append("g");path=o.attr("points","points").append("path").attr("class","ac-line").style("stroke",function(a,b){return x(b)}).attr("d",function(a){return h(a.values)}).attr("stroke-width","2").attr("fill","none");var p=j[j.length-1].values,q=path.node().getTotalLength()+a(p[p.length-1].x);return path.attr("stroke-dasharray",q+" "+q).attr("stroke-dashoffset",q).transition().duration(1500).ease("linear").attr("stroke-dashoffset",0).attr("d",function(a){return h(a.values)}),angular.forEach(j,function(b){var c=l.selectAll(".circle").data(b.values).enter();c.append("circle").attr("cx",function(b){return a(b.x)}).attr("cy",function(a){return d(a.y)}).attr("r",3).style("fill",x(j.indexOf(b))).style("stroke",x(j.indexOf(b))).on("mouseover",function(a){return function(b){t({index:b.x,value:b.y,series:a},d3.event),G.mouseover(b,d3.event),f.$apply()}}(b.series)).on("mouseleave",function(a){u(),G.mouseout(a,d3.event),f.$apply()}).on("mousemove",function(){v(d3.event)}).on("click",function(a){G.click(a,d3.event),f.$apply()}),G.labels&&c.append("text").attr("x",function(b){return a(b.x)}).attr("y",function(a){return d(a.y)}).text(function(a){return a.y})}),"lineEnd"===G.lineLegend&&o.append("text").datum(function(a){return{name:a.series,value:a.values[a.values.length-1]}}).attr("transform",function(b){return"translate("+a(b.value.x)+","+d(b.value.y)+")"}).attr("x",3).text(function(a){return a.name}),j}function q(){function a(a){return Math.round(c(a))+c.rangeBand()/2}var b={top:0,right:40,bottom:20,left:40};C-=b.left+b.right,B-=b.top+b.bottom;var c=d3.scale.ordinal().domain(A.map(function(a){return a.x})).rangeRoundBands([0,C]),d=d3.scale.linear().range([B,10]),e=d3.svg.axis().scale(c).orient("bottom");n(e,c);var g=d3.svg.axis().scale(d).orient("left").ticks(5).tickFormat(d3.format("s")),h=(d3.svg.line().interpolate("cardinal").x(function(b){return a(b.x)}).y(function(a){return d(a.y)}),[0]),i=[];A.forEach(function(a){a.y.map(function(a){h.push(a)})});var j=d3.max(A.map(function(a){return a.y.length}));f.yMaxData=j,z.slice(0,j).forEach(function(a,b){var c={};c.series=a,c.values=A.map(function(a){return a.y.map(function(b){return{x:a.x,y:b}})[b]||{x:A[b].x,y:0}}),i.push(c)});var k=d3.select(D[0]).append("svg").attr("width",C+b.left+b.right).attr("height",B+b.top+b.bottom).append("g").attr("transform","translate("+b.left+","+b.top+")"),l=.2*d3.max(h);d.domain([d3.min(h),d3.max(h)+l]),k.append("g").attr("class","x axis").attr("transform","translate(0,"+B+")").call(e),k.append("g").attr("class","y axis").call(g);var m=k.selectAll(".points").data(i).enter().append("g"),o=d3.svg.area().interpolate("basis").x(function(b){return a(b.x)}).y0(function(){return d(0)}).y1(function(a){return d(0+a.y)});m.append("path").attr("class","area").attr("d",function(a){return o(a.values)}).style("fill",function(a,b){return x(b)}).style("opacity","0.7")}function r(){function a(a){a.innerRadius=0;var b=d3.interpolate({startAngle:0,endAngle:0},a);return function(a){return g(b(a))}}var b=Math.min(C,B)/2,c=d3.select(D[0]).append("svg").attr("width",C).attr("height",B).append("g").attr("transform","translate("+C/2+","+B/2+")"),d=0;if(G.innerRadius){var e=G.innerRadius;"string"==typeof e&&e.indexOf("%")>0&&(e=b*(1-.01*parseFloat(e))),e&&(d=b-Number(e))}f.yMaxData=A.length;{var g=d3.svg.arc().outerRadius(b-10).innerRadius(d),h=(d3.svg.arc().outerRadius(b+5).innerRadius(0),d3.layout.pie().sort(null).value(function(a){return a.y[0]})),i=c.selectAll(".arc").data(h(A)).enter().append("g");i.append("path").style("fill",function(a,b){return x(b)}).transition().ease("linear").duration(500).attrTween("d",a).attr("class","arc")}i.on("mouseover",function(a){t({value:a.data.y[0]},d3.event),d3.select(this).select("path").transition().duration(200).style("stroke","white").style("stroke-width","2px"),G.mouseover(a,d3.event),f.$apply()}).on("mouseleave",function(a){d3.select(this).select("path").transition().duration(200).style("stroke","").style("stroke-width",""),u(),G.mouseout(a,d3.event),f.$apply()}).on("mousemove",function(){v(d3.event)}).on("click",function(a){G.click(a,d3.event),f.$apply()}),G.labels&&i.append("text").attr("transform",function(a){return"translate("+g.centroid(a)+")"}).attr("dy",".35em").style("text-anchor","middle").text(function(a){return a.data.y[0]})}function s(){function a(a){return Math.round(c(a))+c.rangeBand()/2}var b={top:0,right:40,bottom:20,left:40};C-=b.left-b.right,B-=b.top-b.bottom;var c=d3.scale.ordinal().domain(A.map(function(a){return a.x})).rangeRoundBands([0,C]),d=d3.scale.linear().range([B,10]),e=d3.svg.axis().scale(c).orient("bottom");n(e,c);var g=d3.svg.axis().scale(d).orient("left").ticks(5).tickFormat(d3.format("s")),h=[0],i=[];A.forEach(function(a){a.y.map(function(a){h.push(a)})});var j=d3.max(A.map(function(a){return a.y.length}));f.yMaxPoints=j,z.slice(0,j).forEach(function(a,b){var c={};c.series=a,c.values=A.map(function(a){return a.y.map(function(b){return{x:a.x,y:b}})[b]||{x:A[b].x,y:0}}),i.push(c)});var k=d3.select(D[0]).append("svg").attr("width",C+b.left+b.right).attr("height",B+b.top+b.bottom).append("g").attr("transform","translate("+b.left+","+b.top+")"),l=.2*d3.max(h);d.domain([d3.min(h),d3.max(h)+l]),k.append("g").attr("class","x axis").attr("transform","translate(0,"+B+")").call(e),k.append("g").attr("class","y axis").call(g);k.selectAll(".points").data(i).enter().append("g");angular.forEach(i,function(b){var c=k.selectAll(".circle").data(b.values).enter();c.append("circle").attr("cx",function(b){return a(b.x)}).attr("cy",function(a){return d(a.y)}).attr("r",3).style("fill",x(i.indexOf(b))).style("stroke",x(i.indexOf(b))).on("mouseover",function(a){return function(b){t({index:b.x,value:b.y,series:a},d3.event),G.mouseover(b,d3.event),f.$apply()}}(b.series)).on("mouseleave",function(a){u(),G.mouseout(a,d3.event),f.$apply()}).on("mousemove",function(){v(d3.event)}).on("click",function(a){G.click(a,d3.event),f.$apply()}),G.labels&&c.append("text").attr("x",function(b){return a(b.x)}).attr("y",function(a){return d(a.y)}).text(function(a){return a.y})})}function t(a,b){G.tooltips&&(a="[object Function]"==Object.prototype.toString.call(G.tooltips)?G.tooltips(a):a.value,angular.element('

').html(a).appendTo("body").fadeIn("slow").css({left:b.pageX+20,top:b.pageY-30}))}function u(){angular.element(".ac-tooltip").remove()}function v(a){angular.element(".ac-tooltip").css({left:a.pageX+20,top:a.pageY-30})}function w(){f.legends=[],"pie"==F&&angular.forEach(A,function(a,b){f.legends.push({color:G.colors[b],title:a.x})}),("bar"==F||"area"==F||"point"==F||"line"==F&&"traditional"===G.lineLegend)&&angular.forEach(z,function(a,b){f.legends.push({color:G.colors[b],title:a})})}function x(a){if(a\n .axis path,\n .axis line {\n fill: none;\n stroke: #333;\n }\n .ac-line {\n fill:none;\n stroke-width:2px;\n }\n\n\n
{{acConfig.title}}
\n
\n \n \n \n \n \n
\n
\n
\n
")}]),angular.module("right",[]).run(["$templateCache",function(a){a.put("right","\n\n
{{acConfig.title}}
\n
\n
\n
\n \n \n \n \n \n
\n
")}]); \ No newline at end of file diff --git a/package.json b/package.json index cefd529..77e2c75 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "utopia", - "version": "0.0.1", - "description": "ERROR: No README.md file found!", + "version": "0.1.0", + "description": "angular directives for common charts using d3, for more information visit", "main": "Gruntfile.js", "dependencies": { "grunt": "~0.4.1",