├── LICENSE
├── .gitignore
├── img
└── angular-slopegraph.png
├── README.md
└── js
└── angular-slopegraph.js
/LICENSE:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 |
--------------------------------------------------------------------------------
/img/angular-slopegraph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bentorfs/angular-slopegraph/master/img/angular-slopegraph.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AngularJS slopegraphs
2 |
3 | An AngularJS directive to easily create slopegraphs (for more info on slopegraphs: http://charliepark.org/slopegraphs/)
4 |
5 | The rendering code is based on an implementation by Kevin Marks, found here: https://github.com/kevinmarks/slopegraph. It depends on the Raphael SVG library
6 |
7 | ## Example rendering
8 | 
9 |
10 | ## Example usage
11 |
12 | Import the necessary JS. angular-slopegraph.js depends on Raphael (http://raphaeljs.com/)
13 | ```
14 |
15 |
16 | ```
17 |
18 | Create a slopegraph as follows
19 | ```
20 |
21 | ```
22 | slopeData refers to a scope variable that contains the data. The data must be a 2-dimensional array, where each element is a list of 4 elements:
23 | - The left label
24 | - The value behind the left label
25 | - The right label
26 | - The value behind the right label
27 |
28 | Higher values will be displayed lower on the graph. For example, the example rendering above is backed by this data:
29 |
30 | ```javascript
31 | angular.module('myApp', ['ngSlopeGraph']).
32 | controller('MyController', ['$scope', function ($scope) {
33 |
34 | $scope.slopeData = [
35 | ['Sweden', 46.9, 'Sweden', 57.4],
36 | ['Netherlands', 44.0, 'Netherlands', 55.8],
37 | ['Britain', 40.7, 'Britain', 39.0],
38 | ['Belgium', 35.2, 'Belgium', 43.2]
39 | ];
40 |
41 | }]);
42 | ```
43 |
--------------------------------------------------------------------------------
/js/angular-slopegraph.js:
--------------------------------------------------------------------------------
1 | angular.module('ngSlopeGraph', [])
2 | .directive('slopegraph', function () {
3 |
4 | function drawSlopeGraph(container, data, leftLabel, rightLabel, width, height) {
5 |
6 | container.html(""); // Clear current slopegraph
7 | var seenBoundingBoxes = [];
8 |
9 | var r = Raphael(container[0], width, height),
10 | leftLabelCss = { 'text-anchor': 'end', fill: "#000", 'font-size': "12px"},
11 | rightLabelCss = { 'text-anchor': 'start', fill: "#000", 'font-size': "12px"},
12 | leftHeaderCss = { 'text-anchor': 'end', fill: "#000", 'font-size': "12px", 'font-weight': 'bold'},
13 | rightHeaderCss = { 'text-anchor': 'start', fill: "#000", 'font-size': "12px", 'font-weight': 'bold'},
14 | labelWidth = 120,
15 | headerHeight = 50,
16 | footerHeight = 100,
17 | contentHeight = height - headerHeight - footerHeight,
18 | max = getMaxValue(data),
19 | min = getMinValue(data),
20 | scale = (contentHeight / (max - min));
21 | r.text(labelWidth, 10, leftLabel).attr(leftHeaderCss);
22 | r.text(width - labelWidth, 10, rightLabel).attr(rightHeaderCss);
23 | for (var i = 0; i < data.length; i++) {
24 | var leftKey = data[i] [0],
25 | leftVal = data[i] [1],
26 | rightKey = data[i] [2],
27 | rightVal = data[i] [3];
28 | var startY = headerHeight + scale * (leftVal - min);
29 | var endY = headerHeight + scale * (rightVal - min);
30 | while (collides({x: 0, y: startY, w: labelWidth, h: 12}, seenBoundingBoxes)) startY++;
31 | while (collides({x: width - labelWidth, y: endY, w: labelWidth, h: 12}, seenBoundingBoxes)) endY++;
32 | if (leftKey) {
33 | r.text(labelWidth, startY, leftKey).attr(leftLabelCss);
34 | }
35 | if (rightKey) {
36 | r.text(width - labelWidth, endY, rightKey).attr(rightLabelCss);
37 | }
38 | if (leftVal && rightVal) {
39 | var line = ["M", labelWidth + 5, " ", startY, "L", width - labelWidth - 5, " ", endY].join("");
40 | }
41 | var p = r.path(line);
42 | }
43 | };
44 |
45 | function collides(box, seenBoundingBoxes) {
46 | for (var i = 0, il = seenBoundingBoxes.length; i < il; i++) {
47 | var seen = seenBoundingBoxes[i];
48 | if (!(box.x > seen.x + seen.w || box.y > seen.y + seen.h || box.x + box.w < seen.x || box.y + box.h < seen.y)) return true;
49 | }
50 | seenBoundingBoxes.push(box);
51 | return false;
52 | }
53 |
54 | function getMinValue(data) {
55 | var result = Number.MAX_VALUE;
56 | for (var i = 0; i < data.length; i++) {
57 | if (data[i][1] && data[i][1] < result) {
58 | result = data[i][1];
59 | }
60 | if (data[i][3] && data[i][3] < result) {
61 | result = data[i][3];
62 | }
63 | }
64 | return result;
65 | }
66 |
67 |
68 | function getMaxValue(data) {
69 | var result = Number.MIN_VALUE;
70 | for (var i = 0; i < data.length; i++) {
71 | if (data[i][1] && data[i][1] > result) {
72 | result = data[i][1];
73 | }
74 | if (data[i][3] && data[i][3] > result) {
75 | result = data[i][3];
76 | }
77 | }
78 | return result;
79 | }
80 |
81 | return {
82 | restrict: 'E',
83 | replace: true,
84 | link: function compile(scope, element, attrs, controller) {
85 | var scopeVarWithData = attrs['slopedata'];
86 | scope.$watch(scopeVarWithData, function () {
87 | var dataSet = scope.$eval(attrs['slopedata']);
88 | if (dataSet) {
89 | var leftLabel = attrs['leftlabel'];
90 | var rightLabel = attrs['rightlabel'];
91 | var width = attrs['width'];
92 | var height = attrs['height'];
93 | drawSlopeGraph(element, dataSet, leftLabel, rightLabel, width, height);
94 | }
95 | }
96 | );
97 | }
98 | }
99 | ;
100 | })
101 | ;
102 |
--------------------------------------------------------------------------------