├── .bithoundrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── addons.js ├── app.js ├── bower.json ├── build-tools ├── addons.expose.build.js ├── expose.build.js └── pre-require-es.js ├── components.js ├── demo ├── base.css ├── bundle.js ├── demo-webpack.js ├── example-webpack.js ├── example.css ├── example.html ├── example.js ├── example │ ├── react-bubble-hover-tooltip │ │ ├── README.md │ │ ├── app.jsx │ │ ├── bubble-point.jsx │ │ ├── bubble-scatter.jsx │ │ ├── start.sh │ │ └── webpack.config.js │ └── react-line-hover-tooltip │ │ ├── README.md │ │ ├── app.jsx │ │ ├── graph.jsx │ │ ├── start.sh │ │ └── webpack.config.js ├── index.html ├── lib │ ├── prism.css │ └── prism.js └── style.css ├── dev ├── base.jsx ├── comparison.jsx ├── event-base.js └── reactApp.jsx ├── doc.md ├── gulpfile.js ├── index.js ├── line-timeSeries.js ├── package.json ├── src ├── API_DRAFT_README.md ├── README.md ├── addons │ ├── Events.es6 │ ├── Events.feedback.js │ ├── Label.js │ ├── ReturnAsObject.js │ ├── index.es6 │ └── react-components │ │ ├── bar.jsx │ │ ├── bubble.js │ │ ├── bubble │ │ ├── bubble-point.jsx │ │ └── bubble-scatter.jsx │ │ ├── donut.jsx │ │ ├── event-ready │ │ └── spark-event.js │ │ ├── index.js │ │ ├── legend.jsx │ │ ├── line.jsx │ │ ├── pie.jsx │ │ ├── simpleSpark.jsx │ │ ├── spark.jsx │ │ └── utils │ │ └── GraphPureRenderMixin.js ├── chart.js ├── classes │ ├── arc.es6 │ ├── common.es6 │ └── default.es6 ├── components │ ├── api.js │ ├── bar.es6 │ ├── bubble.api.es6 │ ├── bubble.point.es6 │ ├── bubble.scatter.es6 │ ├── donut.es6 │ ├── line.api.js │ ├── line.es6 │ ├── line.timeSeries.es6 │ ├── pie.es6 │ └── spark.es6 ├── index.es6 ├── svg │ ├── arc.js │ ├── composer.es6 │ ├── draw.es6 │ ├── line.js │ ├── path.js │ ├── rect.js │ └── svg.es6 └── utils │ ├── adjustDecimal.js │ ├── error.js │ ├── extend.js │ ├── polyfill.es6 │ ├── randomColor.es6 │ ├── sort.js │ └── utility.es6 ├── test ├── addons │ └── label-test.js ├── classes │ ├── arc-test.js │ ├── common-test.js │ └── default-test.js ├── components │ ├── bar-test.js │ ├── bubble.api-test.js │ ├── donut-test.js │ ├── pie-test.js │ └── spark-test.js ├── svg │ ├── arc-test.js │ └── path-test.js └── utils │ └── adjustDecimal-test.js ├── webpack.config.js └── wercker.yml /.bithoundrc: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "dist/**", 4 | "demo/**", 5 | "dev/**", 6 | "build-tools/**", 7 | "./app.js", 8 | "**/deps/**", 9 | "**/node_modules/**", 10 | "**/thirdparty/**", 11 | "**/third_party/**", 12 | "**/vendor/**", 13 | "**/**-min-**", 14 | "**/**-min.**", 15 | "**/**.min.**", 16 | "**/**jquery.?(ui|effects)-*.*.?(*).?(cs|j)s", 17 | "**/**jquery-*.*.?(*).?(cs|j)s", 18 | "**/prototype?(*).js", 19 | "**/**?(*).ts", 20 | "**/mootools*.*.*.js", 21 | "**/dojo.js", 22 | "**/MochiKit.js", 23 | "**/yahoo-*.js", 24 | "**/yui*.js", 25 | "**/ckeditor*.js", 26 | "**/tiny_mce*.js", 27 | "**/tiny_mce/?(langs|plugins|themes|utils)/**", 28 | "**/MathJax/**", 29 | "**/shBrush*.js", 30 | "**/shCore.js", 31 | "**/shLegacy.js", 32 | "**/modernizr.custom.?(*).js", 33 | "**/knockout-*.*.*.debug.js", 34 | "**/extjs/*.js", 35 | "**/extjs/*.xml", 36 | "**/extjs/*.txt", 37 | "**/extjs/*.html", 38 | "**/extjs/*.properties", 39 | "**/extjs/.sencha", 40 | "**/extjs/docs/**", 41 | "**/extjs/builds/**", 42 | "**/extjs/cmd/**", 43 | "**/extjs/examples/**", 44 | "**/extjs/locale/**", 45 | "**/extjs/packages/**", 46 | "**/extjs/plugins/**", 47 | "**/extjs/resources/**", 48 | "**/extjs/src/**", 49 | "**/extjs/welcome/**", 50 | "bower_components/**" 51 | ], 52 | "test": [ 53 | "**/test/**", 54 | "**/tests/**", 55 | "**/spec/**", 56 | "**/specs/**" 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | 4 | "ecmaFeatures": { 5 | "jsx": true 6 | }, 7 | 8 | "plugins": [ 9 | "react" 10 | ], 11 | 12 | "rules": { 13 | "strict": 0, 14 | "no-underscore-dangle": 0, 15 | "no-unused-vars": 1, 16 | "eqeqeq": 0, 17 | "curly": 0, 18 | "no-multi-spaces": 0, 19 | "key-spacing": 0, 20 | "no-return-assign": 0, 21 | "consistent-return": 0, 22 | "no-shadow": 0, 23 | "no-comma-dangle": 0, 24 | "no-use-before-define": 1, 25 | "no-empty": 0, 26 | "no-trailing-spaces": 0, 27 | "new-parens": 0, 28 | "no-cond-assign": 0, 29 | "quotes": 0, 30 | "no-process-exit": 0, 31 | "new-cap": 0, 32 | "no-unused-vars": [2, { "vars": "all", "args": "none"}], 33 | "react/jsx-uses-react": 2, 34 | "react/jsx-uses-vars": 2, 35 | "react/react-in-jsx-scope": 2, 36 | }, 37 | 38 | "env": { 39 | "node": true, 40 | "browser": true, 41 | "mocha": true, 42 | "es6": true, 43 | "jasmine": true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .coveralls.yml 4 | *.sublime-* 5 | .DS_Store 6 | .tmp 7 | .build 8 | npm-debug.log 9 | testData/ 10 | testData.json 11 | dist/ 12 | lib/ 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | ._* 3 | .DS_Store 4 | .git 5 | .hg 6 | .lock-wscript 7 | .svn 8 | .wafpickle-* 9 | CVS 10 | npm-debug.log 11 | dev 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "4.0" 5 | after_script: 6 | - npm run coveralls 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alfred Kam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Yako 2 | [![npm version](https://badge.fury.io/js/yako.svg)](http://badge.fury.io/js/yako) 3 | [![Stories in Ready](https://badge.waffle.io/alfredkam/yakojs.png?label=ready&title=Ready)](https://waffle.io/alfredkam/yakojs) 4 | [![Dependencies](https://david-dm.org/alfredkam/yakojs.svg)](https://david-dm.org/alfredkam/yakojs) 5 | [![Build Status](https://travis-ci.org/alfredkam/yakojs.svg?branch=master)](https://travis-ci.org/alfredkam/yakojs) 6 | [![Coverage Status](https://coveralls.io/repos/alfredkam/yakojs/badge.svg?branch=master)](https://coveralls.io/r/alfredkam/yakojs?branch=master) 7 | [![bitHound Score](https://www.bithound.io/github/alfredkam/yakojs/badges/score.svg)](https://www.bithound.io/github/alfredkam/yakojs/master) 8 | 9 | A tiny **DOM-less** graph library, build for fast front end and server side rendering in CommonJs pattern. 10 | This library is intend to generate light weight and simple SVG graphs, and is more performable compare to highcharts / flot / c3 / d3 when **front end matters**. 11 | 12 | This library also works great with webpack & react w/ [prepared react graph components](https://github.com/alfredkam/yakojs/blob/master/doc.md#react-components) 13 | 14 | Documentation [https://github.com/alfredkam/yakojs/blob/master/doc.md](https://github.com/alfredkam/yakojs/blob/master/doc.md) 15 | 16 | Demo [http://github.alfredkam.com/yakojs](http://github.alfredkam.com/yakojs) or ```gulp dev``` and visit ```http://localhost:5000``` 17 | 18 | Supports Chrome, Firefox, Safari, and IE 9+; 19 | 20 | ###Note on upgrading 0.4.X to 0.5.X 21 | - Deprecate the old bubble usage and with a new interface 22 | - Simplify ```.timeseries``` require usage - It is now no longer needed to include ```.timeseries``` to use those graphs 23 | - Simplify chart configuration by flattern out ```.attr({})``` 24 | 25 | ### Install 26 | ```npm install yako```
Alternatively
28 | ```bower install yako``` 29 | 30 | ### Building the package 31 | ```gulp build```
32 | This will build the ```lib``` directory, by converting ```src``` directory from es6 to es5. 33 | 34 | ### API Ready 35 | - Simple Graphs 36 | - Spark 37 | - Scattered 38 | - Line 39 | - Area Graphs 40 | - Pie Charts 41 | - Donut Charts 42 | - Bubble Point (Bubble Line) Chart for Time Flow Representation (Represents actions across a period of time) 43 | - Bubble Graph (Represents a cohort / segment activty) 44 | - Bar Graphs (stack & non stack) 45 | - MixIn / Inheritance 46 | - Multi axis for spark graphs 47 | - Labels 48 | - Bar graph 49 | - Line Graph 50 | - Bubble Graph 51 | - Programmatic point of access (Only Avaliable for React) 52 | - Events & Emitters (Only Avaliable for React) 53 | 54 | ### Road Map 55 | - Complex Graphs 56 | - eg. time series w/ auto fill 57 | - Expose math functions 58 | - Bring svg to live 59 | - Real time graphs 60 | 61 | 62 | ### How to run unit test 63 | ```npm test``` 64 | 65 | ### Latest Release 66 | Please refer to the release branch - [https://github.com/alfredkam/yakojs/tree/release](https://github.com/alfredkam/yakojs/tree/release) 67 | 68 | [![Analytics](https://ga-beacon.appspot.com/UA-25416273-3/yakojs/readme)](https://github.com/igrigorik/ga-beacon) 69 | [![wercker status](https://app.wercker.com/status/a74eda189271b3b148197e07ad6fa9f1/s "wercker status")](https://app.wercker.com/project/bykey/a74eda189271b3b148197e07ad6fa9f1) 70 | -------------------------------------------------------------------------------- /addons.js: -------------------------------------------------------------------------------- 1 | var yako = require('./index'); 2 | yako.addons = require('./lib/addons'); 3 | 4 | module.exports = yako; 5 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yako", 3 | "main": "yako.js", 4 | "version": "0.3.15", 5 | "homepage": "https://github.com/alfredkam/yakojs", 6 | "authors": [ 7 | "@alfredkam" 8 | ], 9 | "description": "A minimal and light weight graph", 10 | "moduleType": [ 11 | "globals" 12 | ], 13 | "keywords": [ 14 | "graph", 15 | "graph", 16 | "library", 17 | "graph", 18 | "api", 19 | "beautiful", 20 | "graphs", 21 | "minimal", 22 | "graph" 23 | ], 24 | "license": "MIT", 25 | "ignore": [ 26 | "src", 27 | "**/.*", 28 | "node_modules", 29 | "bower_components", 30 | "test", 31 | "tests", 32 | "dev", 33 | "coverage", 34 | "build-tools", 35 | "wercker.yml", 36 | "webpack.config.js" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /build-tools/addons.expose.build.js: -------------------------------------------------------------------------------- 1 | require("expose?yako!../addons.js"); 2 | -------------------------------------------------------------------------------- /build-tools/expose.build.js: -------------------------------------------------------------------------------- 1 | require("expose?yako!../index.js"); 2 | -------------------------------------------------------------------------------- /build-tools/pre-require-es.js: -------------------------------------------------------------------------------- 1 | function disableStyles() { 2 | require.extensions = require.extensions || {}; 3 | require.extensions['.less'] = Function.prototype; 4 | require.extensions['.css'] = Function.prototype; 5 | require.extensions['.sass'] = Function.prototype; 6 | require.extensions['.svg'] = Function.prototype; 7 | } 8 | 9 | // disableStyles(); 10 | require('babel-core/register')({ 11 | ignore: /(node_modules|__tests__)/, 12 | optional : [ 13 | 'es7.asyncFunctions', 14 | 'es7.classProperties', 15 | 'es7.comprehensions', 16 | 'es7.decorators', 17 | 'es7.doExpressions', 18 | 'es7.exponentiationOperator', 19 | 'es7.exportExtensions', 20 | 'es7.objectRestSpread', 21 | 'es7.trailingFunctionCommas' 22 | ], 23 | 24 | blacklist: ['strict'], 25 | 26 | extensions: [".es6", ".es", ".jsx", ".es6.js", ".js" ] 27 | }); 28 | 29 | disableStyles(); 30 | -------------------------------------------------------------------------------- /components.js: -------------------------------------------------------------------------------- 1 | var yako = require('./addons'); // Require yako with addons 2 | var components = require('./lib/addons/react-components'); // Require react components build with yako 3 | yako.components = components; 4 | module.exports = yako; 5 | -------------------------------------------------------------------------------- /demo/base.css: -------------------------------------------------------------------------------- 1 | 2 | /* base */ 3 | body { 4 | background: #fff; 5 | font-family: Helvetica, Arial, sans-serif; 6 | margin: 0; 7 | padding: 0; 8 | } 9 | a { 10 | text-decoration: none; 11 | color: #333; 12 | } 13 | 14 | header { 15 | width: 96%; 16 | background: #FFEB3B; 17 | padding: 0 2%; 18 | } 19 | 20 | header > * { 21 | display: inline-block; 22 | 23 | } 24 | 25 | header .brand { 26 | width: 20%; 27 | } 28 | 29 | header .brand a { 30 | font-weight: bold; 31 | color: #333; 32 | } 33 | 34 | header .module { 35 | width: 60%; 36 | text-align: center; 37 | } 38 | 39 | header .module a { 40 | font-weight: bold; 41 | color: #333; 42 | } 43 | 44 | header .module span { 45 | font-weight: bold; 46 | color: #666; 47 | text-decoration: none; 48 | padding: 0 10px; 49 | font-size: 12px; 50 | } 51 | 52 | header .links { 53 | width: 19%; 54 | text-align: right; 55 | } 56 | 57 | header .links ul { 58 | list-style-type: none; 59 | } -------------------------------------------------------------------------------- /demo/example-webpack.js: -------------------------------------------------------------------------------- 1 | var yako = require('../addons'); 2 | var Label = yako.addons.Label; 3 | var spark = yako.spark; 4 | var pie = yako.pie; 5 | var donut = yako.donut; 6 | var bubble = yako.bubble; 7 | var bar = yako.bar; 8 | 9 | var append = function (className, content) { 10 | document.getElementsByClassName(className)[0].innerHTML = content; 11 | }; 12 | 13 | var spark = yako.spark; 14 | var component = spark({ 15 | mixin: Label 16 | }); 17 | 18 | var set = [ { data: 19 | [ 494, 306, 350, 389, 367, 295, 281, 404, 256, 378, 389, 127, 214, 103, 425, 99, 413, 320, 204, 276, 307, 107, 436, 485, 227, 42, 439, 167, 55, 33 ], 20 | strokeColor: '#f2ee2', 21 | fill: '#424c2d', 22 | scattered: 23 | { strokeColor: '#38c98f', 24 | fill: 'white', 25 | strokeWidth: 2, 26 | radius: 3 } }, 27 | { data: 28 | [ 282, 336, 181, 329, 209, 338, 16, 215, 251, 270, 49, 389, 216, 218, 11, 485, 145, 60, 33, 299, 333, 126, 464, 69, 329, 257, 328, 282, 247, 397 ], 29 | strokeColor: '#ab2ab1', 30 | fill: '#be514', 31 | scattered: 32 | { strokeColor: '#ab2ab1', 33 | fill: 'white', 34 | strokeWidth: 2, 35 | radius: 3 } } ]; 36 | 37 | var singleSet = [ { label: 'Auto Generated 3', 38 | data: 39 | [ 187, 292, 117, 391, 250, 325, 358, 236, 497, 125, 132, 446, 267, 86, 431, 186, 13, 328, 258, 88, 359, 293, 127, 229, 137, 422, 144, 95, 397, 485 ], 40 | strokeColor: '#ac6583'} ]; 41 | 42 | var svg = component.attr({ 43 | chart : { 44 | width: 800, 45 | height: 100 46 | }, 47 | title: 'just a test', 48 | data: set 49 | }); 50 | 51 | var multiSet = [ { data: [ 36, 409, 109, 245, 355, 410, 257, 316, 179, 19 ], 52 | strokeColor: 'red', 53 | strokeWidth: 2, 54 | scattered: { strokeColor: 'red', fill: 'white', strokeWidth: 2, radius: 2 } }, 55 | { data: [ 273, 354, 307, 68, 483, 70, 253, 507, 325, 474 ], 56 | strokeColor: 'blue', 57 | strokeWidth: 2, 58 | scattered: { strokeColor: 'blue', fill: 'white', strokeWidth: 2, radius: 2 } } ]; 59 | 60 | append('spark-spark', svg); 61 | 62 | 63 | var svg = component.attr({ 64 | chart : { 65 | width: 800, 66 | height: 100 67 | }, 68 | title: 'just a test', 69 | data: singleSet 70 | }); 71 | append('spark-line', svg); 72 | 73 | var svg = component.attr({ 74 | chart : { 75 | width: 800, 76 | height: 100, 77 | line: false 78 | }, 79 | title: 'just a test', 80 | data: set 81 | }); 82 | append('spark-area', svg); 83 | 84 | 85 | var svg = component.attr({ 86 | chart : { 87 | width: 800, 88 | height: 100 89 | // scattered: true 90 | }, 91 | title: 'just a test', 92 | data: multiSet, 93 | yAxis: { 94 | multi: true 95 | }, 96 | xAxis : { 97 | // including format will show the xAxis Label 98 | format : 'dateTime', 99 | // interval indicates the data interval, the number of the interval indicates the label tick interval 100 | // same representation is also used for `dateTimeLabelFormat` 101 | // s - seconds 102 | // m - minutes 103 | // h - hours 104 | // D - days 105 | // M - months 106 | // Y - years 107 | interval: '4h', //[1-9]s, [1-9]m, [1-9]h, [1-9]D, [1-9]M, [1-9]Y 108 | // uses the min start date and increment the label by the set interval. interval will be converted to miliseconds 109 | minUTC: Date.UTC(2013,8,7), 110 | //this controls the dateTime label format 111 | //depending on the format, it will affect the label, try :: dateTimeLabelFormat: 'hhh' 112 | dateTimeLabelFormat: 'MM/DD hh ap' 113 | // or if wanted custom label 114 | // format: 'label', 115 | // labels: [Array of label], this label must match the data value length, if not the data will be limited. We will not aggregate the data for you. 116 | } 117 | }); 118 | 119 | append('spark-multi', svg); 120 | 121 | 122 | var svg = component.attr({ 123 | chart : { 124 | width: 800, 125 | height: 100, 126 | line: false, 127 | fill: false, 128 | scattered: true, 129 | }, 130 | title: 'just a test', 131 | data: set 132 | }); 133 | 134 | append('spark-scattered', svg); 135 | 136 | var svg = component.attr({ 137 | chart : { 138 | width: 800, 139 | height: 100, 140 | scattered: true 141 | }, 142 | title: 'just a test', 143 | data: multiSet, 144 | yAxis: true, 145 | xAxis : { 146 | // including format will show the xAxis Label 147 | format : 'dateTime', 148 | // interval indicates the data interval, the number of the interval indicates the label tick interval 149 | // same representation is also used for `dateTimeLabelFormat` 150 | // s - seconds 151 | // m - minutes 152 | // h - hours 153 | // D - days 154 | // M - months 155 | // Y - years 156 | interval: '1D', //[1-9]s, [1-9]m, [1-9]h, [1-9]D, [1-9]M, [1-9]Y 157 | // uses the min start date and increment the label by the set interval. interval will be converted to miliseconds 158 | minUTC: Date.UTC(2013,8,7), 159 | //this controls the dateTime label format 160 | //depending on the format, it will affect the label, try :: dateTimeLabelFormat: 'hhh' 161 | dateTimeLabelFormat: 'MM/DD hh ap' 162 | // or if wanted custom label 163 | // format: 'label', 164 | // label: [Array of label], this label must match the data value length, if not the data will be limited. We will not aggregate the data for you. 165 | } 166 | }); 167 | 168 | append('spark-label-scattered', svg); 169 | -------------------------------------------------------------------------------- /demo/example.css: -------------------------------------------------------------------------------- 1 | .graph { 2 | display:inline-block; 3 | } 4 | 5 | .col { 6 | width: 50%; 7 | display: inline-block; 8 | float: left; 9 | } 10 | 11 | .row { 12 | width: 100%; 13 | display: inline-block; 14 | } 15 | 16 | pre { 17 | word-wrap: break-word; 18 | font-size: 12px; 19 | } 20 | 21 | section { 22 | margin-left:auto; 23 | margin-right: auto; 24 | width: 800px; 25 | } -------------------------------------------------------------------------------- /demo/example/react-bubble-hover-tooltip/README.md: -------------------------------------------------------------------------------- 1 | An example of the code usage could be found here that takes full advantage of tooltip hovering with events 2 | 3 | ###Usage 4 | In terminal run 5 | ```bash 6 | ../../../../node_modules/webpack-dev-server/bin/webpack-dev-server.js --hot 7 | ``` 8 | or 9 | ```bash 10 | sh start.sh 11 | ``` 12 | and visit ```localhost:8080/webpack-dev-server/bundle``` 13 | Entry point is app.jsx 14 | -------------------------------------------------------------------------------- /demo/example/react-bubble-hover-tooltip/app.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var BubblePoint = require('./bubble-point'); 3 | var BubbleScatter = require('./bubble-scatter'); 4 | 5 | var dataPoints = 10; 6 | var dataSet = []; 7 | var dataSet5 = []; 8 | var dataSet6 = []; 9 | 10 | for (var i=0;i < dataPoints;i++) { 11 | dataSet.push(Math.floor((Math.random() * 500) + 10)); 12 | } 13 | 14 | for (var i = 0; i < dataPoints; i++) { 15 | var temp = []; 16 | for (var j = 0; j < 3; j++) { 17 | temp.push(Math.floor((Math.random() * 500) + 10)); 18 | } 19 | dataSet5.push({ 20 | data: temp, 21 | meta: {} 22 | }); 23 | } 24 | 25 | var strokColorFirst = 'red'; 26 | var strokeColorSecond = 'blue'; 27 | var strokeColorThird = 'pink'; 28 | var strokeColorFourth = 'green'; 29 | 30 | var bubbleSet = dataSet5 31 | 32 | var bubblePoint =[ 33 | { 34 | // date: new Date(2015,2,1), 35 | data: 300, 36 | fill : '#000', 37 | meta: {} 38 | // meta: Object 39 | }, 40 | { 41 | //date: new Date(2015, 4, 30), 42 | data: 150, 43 | fill : '#000', 44 | meta: {} 45 | // meta: Object 46 | }, 47 | { 48 | //date: new Date(2015, 9, 30), 49 | data: 200, 50 | fill : '#000', 51 | meta: {} 52 | // meta: Object 53 | }]; 54 | 55 | React.render( 56 |
57 | 58 | 59 |
, 60 | document.getElementsByTagName('body')[0]); 61 | -------------------------------------------------------------------------------- /demo/example/react-bubble-hover-tooltip/bubble-point.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * TEMPLATE for hovering with multiple axis in react 3 | */ 4 | var React = require('react'); 5 | var yako = require('../../../src'); 6 | var Bubble = require('../../../src/addons/react-components/bubble/bubble-point'); 7 | var PureRenderMixin = require('react-addons-pure-render-mixin'); 8 | 9 | /* Tool Tip Component */ 10 | var ToolTip = React.createClass({ 11 | mixin: [PureRenderMixin], 12 | render: function () { 13 | var html; 14 | var content = this.props.content; 15 | /** 16 | * You would expect this.props.content to include 17 | * { 18 | * exactPoint : { // only included if hovered on a path / circle 19 | * data : { 20 | x : Number (eg. time), 21 | y : Number (sample size), 22 | meta : {} 23 | }, 24 | * eY : mouse event y, 25 | * eX : mouse event x, 26 | * cY : center y of the bubble that is relative to the chart 27 | * cX : center x of the bubble that is relative to the chart 28 | * r : radius of the bubble 29 | * }, 30 | * _data : Object, // reference to user data 31 | * _scale : Object // reference to the mathematical values used to calculate the graph 32 | * } 33 | */ 34 | if (Object.keys(content).length !== 0) { 35 | if (content.exactPoint) { 36 | html = 'point at value : ' + JSON.stringify(content.exactPoint); 37 | } 38 | } 39 | 40 | var style = { 41 | position: 'absolute' 42 | }; 43 | 44 | return ( 45 |
46 | {html} 47 |
48 | ); 49 | } 50 | }); 51 | 52 | /* Legend Component */ 53 | var Legend = React.createClass({ 54 | mixin: [PureRenderMixin], 55 | render: function () { 56 | return ( 57 |
58 | I am a legend 59 |
60 | ); 61 | } 62 | }); 63 | 64 | /* Graph Component */ 65 | module.exports = React.createClass({ 66 | 67 | getInitialState: function () { 68 | return { 69 | shouldShow: false, 70 | }; 71 | }, 72 | 73 | componentWillMount: function () { 74 | var onActivity = function (e, props) { 75 | self.setState({ 76 | shouldShow: true 77 | }); 78 | } 79 | var onLeave = function (e, props) { 80 | self.setState({ 81 | shouldShow: false 82 | }); 83 | } 84 | var self = this; 85 | self.events = { 86 | // Register events for call back 87 | on: { 88 | 'circle:mouseMove': onActivity, 89 | 'circle:mouseOut': onLeave, 90 | 'container:mouseLeave': onLeave 91 | } 92 | }; 93 | }, 94 | 95 | render: function () { 96 | var self = this; 97 | var attr = { 98 | // Width & height controls the svg view box 99 | width: 1200, 100 | height: 100, 101 | points: self.props.set, 102 | autoFit: false, 103 | /* Optional parameters */ 104 | /* Options for the straight line */ 105 | axis: { 106 | strokeColor: '#000', // sets stroke color, 107 | strokeWidth: 2 108 | }, 109 | maxRadius: 10, // Overrides default & sets a cap for a max radius for the bubble 110 | strokeColor: '#000', // Set default stroke color 111 | strokeWidth: 2, // Set default stroke width 112 | fill: '#333', // Sets default fill color 113 | }; 114 | 115 | var toolTip = ToolTip || 0; 116 | var legend = Legend || 0; 117 | 118 | if (!toolTip) { 119 | self.events = {}; 120 | } else { 121 | toolTip = { 122 | shouldShow: self.state.shouldShow, 123 | reactElement: ToolTip 124 | } 125 | } 126 | 127 | var self = this; 128 | return ( 129 | 134 | ); 135 | } 136 | }); 137 | -------------------------------------------------------------------------------- /demo/example/react-bubble-hover-tooltip/bubble-scatter.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * TEMPLATE for hovering with multiple axis in react 3 | */ 4 | var React = require('react'); 5 | var yako = require('../../../src'); 6 | var Bubble = require('../../../src/addons/react-components/bubble/bubble-scatter'); 7 | var PureRenderMixin = require('react-addons-pure-render-mixin'); 8 | 9 | /* Tool Tip Component */ 10 | var ToolTip = React.createClass({ 11 | mixin: [PureRenderMixin], 12 | render: function () { 13 | var html; 14 | var content = this.props.content; 15 | /** 16 | * You would expect this.props.content to include 17 | * { 18 | * exactPoint : { // only included if hovered on a path / circle 19 | * data : { 20 | x : Number, 21 | y : Number, 22 | z : Number, 23 | meta: {} 24 | }, 25 | * eY : mouse event y, 26 | * eX : mouse event x, 27 | * cY : center y of the bubble that is relative to the chart 28 | * cX : center x of the bubble that is relative to the chart 29 | * r : radius of the bubble 30 | * }, 31 | * _segmentXRef : Number, // reference to X segment 32 | * _data : Object, // reference to user data 33 | * _scale : Object // reference to the mathematical values used to calculate the graph 34 | * } 35 | */ 36 | if (Object.keys(content).length !== 0) { 37 | if (content.exactPoint) { 38 | html = 'point at value : ' + JSON.stringify(content.exactPoint); 39 | } 40 | } 41 | var style = { 42 | position: 'absolute' 43 | }; 44 | return ( 45 |
46 | {html} 47 |
48 | ); 49 | } 50 | }); 51 | 52 | /* Legend Component */ 53 | var Legend = React.createClass({ 54 | mixin: [PureRenderMixin], 55 | render: function () { 56 | return ( 57 |
58 | I am a legend 59 |
60 | ); 61 | } 62 | }); 63 | 64 | /* Graph Component */ 65 | module.exports = React.createClass({ 66 | 67 | getInitialState: function () { 68 | return { 69 | shouldShow: false, 70 | }; 71 | }, 72 | 73 | componentWillMount: function () { 74 | var onActivity = function (e, props) { 75 | self.setState({ 76 | shouldShow: true 77 | }); 78 | } 79 | var onLeave = function (e, props) { 80 | self.setState({ 81 | shouldShow: false 82 | }); 83 | } 84 | var self = this; 85 | self.events = { 86 | // Register events for call back 87 | on: { 88 | 'circle:mouseMove': onActivity, 89 | 'circle:mouseOut': onLeave, 90 | 'container:mouseLeave': onLeave 91 | } 92 | }; 93 | }, 94 | 95 | render: function () { 96 | var self = this; 97 | var attr = { 98 | width: 1200, 99 | height: 100, 100 | points: self.props.set, 101 | /* Optional parameters */ 102 | /* Options for the circle */ 103 | maxRadius: 10, // Overrides default & sets a cap for a max radius for the bubble 104 | fill: '#000', // Sets the default fill color 105 | 106 | /* Padding options for the chart */ 107 | paddingLeft: 0, 108 | paddingRight: 0, 109 | paddingTop: 0, 110 | paddingBottom: 0 111 | }; 112 | 113 | var toolTip = ToolTip || 0; 114 | var legend = Legend || 0; 115 | 116 | if (!toolTip) { 117 | self.events = {}; 118 | } else { 119 | toolTip = { 120 | shouldShow: self.state.shouldShow, 121 | reactElement: ToolTip 122 | }; 123 | } 124 | 125 | var self = this; 126 | return ( 127 | 132 | ); 133 | } 134 | }); 135 | -------------------------------------------------------------------------------- /demo/example/react-bubble-hover-tooltip/start.sh: -------------------------------------------------------------------------------- 1 | ../../../node_modules/webpack-dev-server/bin/webpack-dev-server.js --hot 2 | -------------------------------------------------------------------------------- /demo/example/react-bubble-hover-tooltip/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | context : __dirname, 5 | entry : [ 6 | './app', 7 | ], 8 | output : { 9 | path: __dirname, 10 | filename: 'bundle.js' 11 | }, 12 | plugins: [ 13 | new webpack.HotModuleReplacementPlugin(), 14 | new webpack.NoErrorsPlugin() 15 | ], 16 | module: { 17 | loaders: [ 18 | {test: /\.jsx$/, loader: 'babel-loader?blacklist=strict'}, 19 | {test: /\.es6\.js$/, loader: 'babel-loader?blacklist=strict'}, 20 | {test: /\.es6$/, loader: 'babel-loader?blacklist=strict'}, 21 | 22 | // compile and include less files 23 | {test: /\.less$/, loader: 'style-loader!css-loader!autoprefixer-loader!less-loader'}, 24 | 25 | // allow less files to load urls pointing to font assets 26 | // @TODO: figure out why this is necessary and do it better 27 | {test: /\.(woff|ttf|eot|svg)$/, loader: 'file-loader' } 28 | ] 29 | }, 30 | resolve : { 31 | extensions: ['', '.js', '.es6.js', '.jsx', '.es6'] 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /demo/example/react-line-hover-tooltip/README.md: -------------------------------------------------------------------------------- 1 | An example of the code usage could be found here that takes full advantage of tooltip hovering with events 2 | 3 | ###Usage 4 | In terminal run 5 | ```bash 6 | ../../../node_modules/webpack-dev-server/bin/webpack-dev-server.js --hot 7 | ``` 8 | or 9 | ```bash 10 | sh start.sh 11 | ``` 12 | and visit ```localhost:8080/webpack-dev-server/bundle``` 13 | Entry point is app.jsx -------------------------------------------------------------------------------- /demo/example/react-line-hover-tooltip/app.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Line = require('./graph'); 3 | 4 | var dataPoints = 10; 5 | var dataSet = []; 6 | var dataSet2 = []; 7 | var dataSet3 = []; 8 | var dataSet4 = []; 9 | for (var i=0;i < dataPoints;i++) { 10 | dataSet.push(Math.floor((Math.random() * 500) + 10)); 11 | dataSet2.push(Math.floor((Math.random() * 500) + 10)); 12 | dataSet3.push(Math.floor((Math.random() * 500) + 10)); 13 | dataSet4.push(Math.floor((Math.random() * 500) + 10)); 14 | } 15 | 16 | var strokColorFirst = 'red'; 17 | var strokeColorSecond = 'blue'; 18 | var strokeColorThird = 'pink'; 19 | var strokeColorFourth = 'green'; 20 | var set = [ 21 | { 22 | data: dataSet, 23 | strokeColor: strokColorFirst, 24 | strokeWidth: 2, 25 | scattered : { 26 | strokeColor: strokColorFirst, 27 | fill: 'white', 28 | strokeWidth: 2, 29 | radius: 5 30 | }, 31 | label: 'red' 32 | }, 33 | { 34 | data: dataSet2, 35 | strokeColor: strokeColorSecond, 36 | strokeWidth: 2, 37 | scattered : { 38 | strokeColor: strokeColorSecond, 39 | fill: 'white', 40 | strokeWidth: 2, 41 | radius: 5 42 | }, 43 | label: 'blue' 44 | } 45 | ]; 46 | var set2 = [ 47 | { 48 | data: dataSet3, 49 | strokeColor: strokeColorThird, 50 | strokeWidth: 2, 51 | scattered : { 52 | strokeColor: strokeColorThird, 53 | fill: 'white', 54 | strokeWidth: 2, 55 | radius: 5 56 | }, 57 | label: 'pink', 58 | meta : { 59 | // your own content 60 | } 61 | }, 62 | { 63 | data: dataSet4, 64 | strokeColor: strokeColorFourth, 65 | strokeWidth: 2, 66 | scattered : { 67 | strokeColor: strokeColorFourth, 68 | fill: 'white', 69 | strokeWidth: 2, 70 | radius: 5 71 | }, 72 | label: 'green', 73 | meta: { 74 | 75 | } 76 | } 77 | ]; 78 | 79 | React.render( 80 |
81 | 82 | 83 |
, 84 | document.getElementsByTagName('body')[0]); 85 | -------------------------------------------------------------------------------- /demo/example/react-line-hover-tooltip/graph.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * TEMPLATE for hovering with multiple axis in react 3 | */ 4 | var React = require('react'); 5 | var yako = require('../../../src'); 6 | var Line = require('../../../src/addons/react-components/line'); 7 | var svg = yako.svg; 8 | 9 | var PureRenderMixin = require('react-addons-pure-render-mixin'); 10 | 11 | /* Tool Tip Component */ 12 | var ToolTip = React.createClass({ 13 | mixin: [PureRenderMixin], 14 | render: function () { 15 | var html; 16 | var content = this.props.content; 17 | /** 18 | * You would expect this.props.content to include 19 | * { 20 | * points : [ // values under X segment 21 | * { 22 | * label : String, // data label 23 | * data : { 24 | x: Number // value at X segment 25 | y: Number // Value at Y segment, 26 | * } 27 | * ], 28 | * exactPoint : { // only included if hovered on a path / circle 29 | * data : { 30 | x : Number (eg. time), 31 | y : Number (sample size), 32 | meta : {} 33 | }, 34 | * eY : mouse event y, 35 | * eX : mouse event x, 36 | * r : radius of the bubble 37 | * }, 38 | * segmentXRef : Number, // reference to X segment 39 | * _scale : Object // reference to the mathematical values used to calculate the graph, 40 | event : event object 41 | * } 42 | */ 43 | if (Object.keys(content).length !== 0) { 44 | if (content.exactPoint) { 45 | html = 'point at value : ' + content.exactPoint.label + ',' + content.exactPoint.value; 46 | } else { 47 | html = content.points.map(function (key) { 48 | return key.label + ':' + key.value; 49 | }); 50 | } 51 | } 52 | return ( 53 |
54 | {html} 55 |
56 | ); 57 | } 58 | }); 59 | 60 | /* Legend Component */ 61 | var Legend = React.createClass({ 62 | mixin: [PureRenderMixin], 63 | render: function () { 64 | return ( 65 |
66 | I am a legend 67 |
68 | ); 69 | } 70 | }); 71 | 72 | /* Graph Component */ 73 | module.exports = React.createClass({ 74 | getInitialState: function () { 75 | return { 76 | shouldShow: false, 77 | }; 78 | }, 79 | componentWillMount: function () { 80 | var onActivity = function (e, props) { 81 | self.setState({ 82 | shouldShow: true 83 | }); 84 | } 85 | var self = this; 86 | self.events = { 87 | // Register events for call back 88 | on: { 89 | 'path:mouseMove': onActivity, 90 | 'svg:mouseMove': onActivity, 91 | 'circle:mouseOver': onActivity, 92 | 'container:mouseLeave': function (e) { 93 | self.setState({ 94 | shouldShow: false 95 | }); 96 | } 97 | } 98 | }; 99 | }, 100 | render: function () { 101 | var self = this; 102 | var attr = { 103 | width: 1200, 104 | height: 150, 105 | points: self.props.set, 106 | yAxis: { 107 | multi: true 108 | }, 109 | xAxis : { 110 | // including format will show the xAxis Label 111 | format : 'dateTime', 112 | // interval indicates the data interval, the number of the interval indicates the label tick interval 113 | // same representation is also used for `dateTimeLabelFormat` 114 | // s - seconds 115 | // m - minutes 116 | // h - hours 117 | // D - days 118 | // M - months 119 | // Y - years 120 | interval: '4h', //[1-9]s, [1-9]m, [1-9]h, [1-9]D, [1-9]M, [1-9]Y 121 | // uses the min start date and increment the label by the set interval. interval will be converted to miliseconds 122 | minUTC: Date.UTC(2013,8,7), 123 | //this controls the dateTime label format 124 | //depending on the format, it will affect the label, try :: dateTimeLabelFormat: 'hhh' 125 | dateTimeLabelFormat: 'MM/DD hh ap' 126 | // or if wanted custom label 127 | // format: 'label', 128 | // labels: [Array of label], this label must match the data value length, if not the data will be limited. We will not aggregate the data for you. 129 | }, 130 | prepend: function (svgString, scale) { 131 | var layout = scale.layout; 132 | var lines = [svg.create('line').attr({ 133 | "stroke": '#000', 134 | "stroke-width": 2, 135 | x1: layout.x, 136 | y1: Math.floor(layout.height / 3), 137 | x2: layout.width + layout.x, 138 | y2: Math.floor(layout.height / 3) 139 | }), 140 | svg.create('line').attr({ 141 | "stroke": '#000', 142 | "stroke-width": 2, 143 | x1: layout.x, 144 | x2: layout.width + layout.x, 145 | y1: Math.floor(2 * layout.height / 3), 146 | y2: Math.floor(2 * layout.height / 3) 147 | }) 148 | ]; 149 | 150 | var grouping = svg.create('g').append(lines); 151 | return grouping; 152 | } 153 | }; 154 | 155 | var toolTip = ToolTip || 0; 156 | var legend = Legend || 0; 157 | 158 | if (!toolTip) { 159 | self.events = {}; 160 | } else { 161 | toolTip = { 162 | shouldShow: self.state.shouldShow, 163 | reactElement: ToolTip 164 | } 165 | } 166 | 167 | var self = this; 168 | return ( 169 | 174 | ); 175 | } 176 | }); 177 | -------------------------------------------------------------------------------- /demo/example/react-line-hover-tooltip/start.sh: -------------------------------------------------------------------------------- 1 | ../../../node_modules/webpack-dev-server/bin/webpack-dev-server.js --hot 2 | -------------------------------------------------------------------------------- /demo/example/react-line-hover-tooltip/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | context : __dirname, 5 | entry : [ 6 | './app', 7 | ], 8 | output : { 9 | path: __dirname, 10 | filename: 'bundle.js' 11 | }, 12 | plugins: [ 13 | new webpack.HotModuleReplacementPlugin(), 14 | new webpack.NoErrorsPlugin() 15 | ], 16 | module: { 17 | loaders: [ 18 | {test: /\.jsx$/, loader: 'babel-loader?blacklist=strict'}, 19 | {test: /\.es6\.js$/, loader: 'babel-loader?blacklist=strict'}, 20 | {test: /\.es6$/, loader: 'babel-loader?blacklist=strict'}, 21 | 22 | // compile and include less files 23 | {test: /\.less$/, loader: 'style-loader!css-loader!autoprefixer-loader!less-loader'}, 24 | 25 | // allow less files to load urls pointing to font assets 26 | // @TODO: figure out why this is necessary and do it better 27 | {test: /\.(woff|ttf|eot|svg)$/, loader: 'file-loader' } 28 | ] 29 | }, 30 | resolve : { 31 | extensions: ['', '.js', '.es6.js', '.jsx', '.es6'] 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /demo/lib/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | text-shadow: 0 1px white; 12 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | line-height: 1.5; 19 | 20 | -moz-tab-size: 4; 21 | -o-tab-size: 4; 22 | tab-size: 4; 23 | 24 | -webkit-hyphens: none; 25 | -moz-hyphens: none; 26 | -ms-hyphens: none; 27 | hyphens: none; 28 | } 29 | 30 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 31 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 32 | text-shadow: none; 33 | background: #b3d4fc; 34 | } 35 | 36 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 37 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 38 | text-shadow: none; 39 | background: #b3d4fc; 40 | } 41 | 42 | @media print { 43 | code[class*="language-"], 44 | pre[class*="language-"] { 45 | text-shadow: none; 46 | } 47 | } 48 | 49 | /* Code blocks */ 50 | pre[class*="language-"] { 51 | padding: 1em; 52 | margin: .5em 0; 53 | overflow: auto; 54 | } 55 | 56 | :not(pre) > code[class*="language-"], 57 | pre[class*="language-"] { 58 | background: #f5f2f0; 59 | } 60 | 61 | /* Inline code */ 62 | :not(pre) > code[class*="language-"] { 63 | padding: .1em; 64 | border-radius: .3em; 65 | } 66 | 67 | .token.comment, 68 | .token.prolog, 69 | .token.doctype, 70 | .token.cdata { 71 | color: slategray; 72 | } 73 | 74 | .token.punctuation { 75 | color: #999; 76 | } 77 | 78 | .namespace { 79 | opacity: .7; 80 | } 81 | 82 | .token.property, 83 | .token.tag, 84 | .token.boolean, 85 | .token.number, 86 | .token.constant, 87 | .token.symbol, 88 | .token.deleted { 89 | color: #905; 90 | } 91 | 92 | .token.selector, 93 | .token.attr-name, 94 | .token.string, 95 | .token.char, 96 | .token.builtin, 97 | .token.inserted { 98 | color: #690; 99 | } 100 | 101 | .token.operator, 102 | .token.entity, 103 | .token.url, 104 | .language-css .token.string, 105 | .style .token.string { 106 | color: #a67f59; 107 | background: hsla(0, 0%, 100%, .5); 108 | } 109 | 110 | .token.atrule, 111 | .token.attr-value, 112 | .token.keyword { 113 | color: #07a; 114 | } 115 | 116 | .token.function { 117 | color: #DD4A68; 118 | } 119 | 120 | .token.regex, 121 | .token.important, 122 | .token.variable { 123 | color: #e90; 124 | } 125 | 126 | .token.important, 127 | .token.bold { 128 | font-weight: bold; 129 | } 130 | .token.italic { 131 | font-style: italic; 132 | } 133 | 134 | .token.entity { 135 | cursor: help; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /demo/lib/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */ 2 | self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),N=[p,1];b&&N.push(b);var O=new a(l,g?t.tokenize(m,g):m,h);N.push(O),w&&N.push(w),Array.prototype.splice.apply(r,N)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; 3 | Prism.languages.markup={comment://g,prolog:/<\?.+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&#?[\da-z]{1,8};/gi},Prism.hooks.add("wrap",function(t){"entity"===t.type&&(t.attributes.title=t.content.replace(/&/,"&"))});; 4 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/gi,inside:{punctuation:/[;:]/g}},url:/url\((?:(["'])(\\\n|\\?.)*?\1|.*?)\)/gi,selector:/[^\{\}\s][^\{\};]*(?=\s*\{)/g,string:/("|')(\\\n|\\?.)*?\1/g,property:/(\b|\B)[\w-]+(?=\s*:)/gi,important:/\B!important\b/gi,punctuation:/[\{\};:]/g,"function":/[-a-z0-9]+(?=\()/gi},Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/[\w\W]*?<\/style>/gi,inside:{tag:{pattern:/|<\/style>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css},alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/gi,inside:{"attr-name":{pattern:/^\s*style/gi,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/gi,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag));; 5 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//g,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*?(\r?\n|$)/g,lookbehind:!0}],string:/("|')(\\\n|\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; 6 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|-?Infinity)\b/g,"function":/(?!\d)[a-z0-9_$]+(?=\()/gi}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript},alias:"language-javascript"}});; 7 | -------------------------------------------------------------------------------- /demo/style.css: -------------------------------------------------------------------------------- 1 | .graph { 2 | display:inline-block; 3 | } 4 | 5 | .body { 6 | padding: 10px; 7 | } -------------------------------------------------------------------------------- /dev/base.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * TEMPLATE for hovering with multiple axis in react 3 | * @type {[type]} 4 | */ 5 | var React = require('react'); 6 | var Spark = require('../addons/react-components/spark'); 7 | var PureRenderMixin = require('react-addons-pure-render-mixin'); 8 | 9 | var ToolTip = React.createClass({ 10 | mixin: [PureRenderMixin], 11 | render: function () { 12 | var html = {}; 13 | if (Object.keys(this.props.content).length !== 0) { 14 | if (this.props.content.exactPoint) { 15 | html = 'point at value : ' + this.props.content.exactPoint.label + ',' + this.props.content.exactPoint.value; 16 | } else { 17 | html = this.props.content.points.map(function (key) { 18 | return key.label + ':' + key.value; 19 | }); 20 | } 21 | } 22 | return ( 23 |
24 | {html} 25 |
26 | ); 27 | } 28 | }); 29 | 30 | var Legend = React.createClass({ 31 | mixin: [PureRenderMixin], 32 | render: function () { 33 | return ( 34 |
35 | I am a legend 36 |
37 | ); 38 | } 39 | }); 40 | 41 | 42 | module.exports = React.createClass({ 43 | getInitialState: function () { 44 | // Normally this should be controlled by props 45 | return { 46 | toolTip: { 47 | shouldShow: false, 48 | }, 49 | useSetOne: true 50 | }; 51 | }, 52 | useSetOne: true, 53 | componentWillMount: function () { 54 | var onActivity = function (e, props) { 55 | self.setState({ 56 | toolTip: { 57 | shouldShow: true 58 | } 59 | }); 60 | } 61 | var self = this; 62 | self.events = { 63 | // Event call backs base on bind 64 | on: { 65 | 'path:mouseMove': onActivity, 66 | 'svg:mouseMove': onActivity, 67 | 'container:mouseLeave': function (e) { 68 | self.setState({ 69 | toolTip: { 70 | shouldShow: false 71 | } 72 | }); 73 | } 74 | } 75 | }; 76 | }, 77 | componentDidMount: function () { 78 | // var self = this; 79 | // setTimeout(function () { 80 | // self.setState({ 81 | // useSetOne: false 82 | // }); 83 | // },5000); 84 | }, 85 | render: function () { 86 | var self = this; 87 | var chart = { 88 | width: 1200, 89 | height: 150, 90 | yAxis: { 91 | multi: true 92 | }, 93 | xAxis : { 94 | // including format will show the xAxis Label 95 | format : 'dateTime', 96 | // interval indicates the data interval, the number of the interval indicates the label tick interval 97 | // same representation is also used for `dateTimeLabelFormat` 98 | // s - seconds 99 | // m - minutes 100 | // h - hours 101 | // D - days 102 | // M - months 103 | // Y - years 104 | interval: '4h', //[1-9]s, [1-9]m, [1-9]h, [1-9]D, [1-9]M, [1-9]Y 105 | // uses the min start date and increment the label by the set interval. interval will be converted to miliseconds 106 | minUTC: Date.UTC(2013,8,7), 107 | //this controls the dateTime label format 108 | //depending on the format, it will affect the label, try :: dateTimeLabelFormat: 'hhh' 109 | dateTimeLabelFormat: 'MM/DD hh ap' 110 | // or if wanted custom label 111 | // format: 'label', 112 | // labels: [Array of label], this label must match the data value length, if not the data will be limited. We will not aggregate the data for you. 113 | } 114 | }; 115 | 116 | var toolTip = ToolTip || 0; 117 | var legend = Legend || 0; 118 | 119 | if (!toolTip) { 120 | self.events = {}; 121 | } else { 122 | toolTip = { 123 | shouldShow: self.state.toolTip.shouldShow, 124 | reactElement: ToolTip 125 | } 126 | } 127 | 128 | 129 | var self = this; 130 | // if (self.state.useSetOne) { 131 | return ( 132 | 138 | ); 139 | // } else { 140 | // return ( 141 | // 147 | // ); 148 | // } 149 | 150 | } 151 | }); 152 | -------------------------------------------------------------------------------- /dev/comparison.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Graph ready comparison 3 | */ 4 | var React = require('react'); 5 | var Spark = require('./base'); 6 | var SimpleSpark = require('../addons/react-components/simpleSpark'); 7 | var NoReactSpark = require('../index').spark; 8 | 9 | var dataPoints = 10; 10 | var dataSet = []; 11 | var dataSet2 = []; 12 | for (var i=0;i < dataPoints;i++) { 13 | dataSet.push(Math.floor((Math.random() * 500) + 10)); 14 | dataSet2.push(Math.floor((Math.random() * 500) + 10)); 15 | } 16 | 17 | var strokColorFirst = 'red'; 18 | var strokeColorSecond = 'blue'; 19 | var set = [ 20 | { 21 | data: dataSet, 22 | //color controls the line 23 | strokeColor: strokColorFirst, 24 | strokeWidth: 2, 25 | scattered : { 26 | strokeColor: strokColorFirst, 27 | fill: 'white', 28 | strokeWidth: 2, 29 | radius: 5 30 | }, 31 | label: 'red' 32 | //nodeColor controls the pointer color 33 | }, 34 | { 35 | data: dataSet2, 36 | strokeColor: strokeColorSecond, 37 | strokeWidth: 2, 38 | scattered : { 39 | strokeColor: strokeColorSecond, 40 | fill: 'white', 41 | strokeWidth: 2, 42 | radius: 5 43 | }, 44 | label: 'blue' 45 | } 46 | ]; 47 | 48 | var chart = { 49 | height: 100, 50 | width: 1200 51 | } 52 | 53 | var start = Date.now(); 54 | React.render( 55 | // , 56 | , 57 | document.getElementsByTagName('body')[0]); 58 | console.log('REACT:' + (Date.now() - start) + 'ms'); 59 | 60 | var start = Date.now(); 61 | var nodes = NoReactSpark('.graph').attr({ 62 | chart : { 63 | width: 1200, 64 | height: 100, 65 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif' 66 | }, 67 | title: 'just a test', 68 | data: set 69 | }); 70 | document.getElementsByTagName('body')[0].innerHTML += nodes; 71 | console.log('NO REACT:' + (Date.now() - start) + 'ms'); 72 | -------------------------------------------------------------------------------- /dev/event-base.js: -------------------------------------------------------------------------------- 1 | var spark = require('../index').spark; 2 | var Events = require('../addons/Events'); 3 | var Label = require('../addons/Label'); 4 | 5 | var dataPoints = 10; 6 | var dataSet = []; 7 | var dataSet2 = []; 8 | var dataSet3 = []; 9 | var dataSet4 = []; 10 | for (var i=0;i < dataPoints;i++) { 11 | dataSet.push(Math.floor((Math.random() * 500) + 10)); 12 | dataSet2.push(Math.floor((Math.random() * 500) + 10)); 13 | dataSet3.push(Math.floor((Math.random() * 500) + 10)); 14 | dataSet4.push(Math.floor((Math.random() * 500) + 10)); 15 | } 16 | 17 | var strokColorFirst = 'red'; 18 | var strokeColorSecond = 'blue'; 19 | var strokeColorThird = 'pink'; 20 | var strokeColorFourth = 'green'; 21 | var set = [ 22 | { 23 | data: dataSet, 24 | //color controls the line 25 | strokeColor: strokColorFirst, 26 | strokeWidth: 2, 27 | scattered : { 28 | strokeColor: strokColorFirst, 29 | fill: 'white', 30 | strokeWidth: 2, 31 | radius: 5 32 | }, 33 | label: 'red' 34 | //nodeColor controls the pointer color 35 | }, 36 | { 37 | data: dataSet2, 38 | strokeColor: strokeColorSecond, 39 | strokeWidth: 2, 40 | scattered : { 41 | strokeColor: strokeColorSecond, 42 | fill: 'white', 43 | strokeWidth: 2, 44 | radius: 5 45 | }, 46 | label: 'blue' 47 | } 48 | ]; 49 | var set2 = [ 50 | { 51 | data: dataSet3, 52 | //color controls the line 53 | strokeColor: strokeColorThird, 54 | strokeWidth: 2, 55 | scattered : { 56 | strokeColor: strokeColorThird, 57 | fill: 'white', 58 | strokeWidth: 2, 59 | radius: 5 60 | }, 61 | label: 'pink' 62 | //nodeColor controls the pointer color 63 | }, 64 | { 65 | data: dataSet4, 66 | strokeColor: strokeColorFourth, 67 | strokeWidth: 2, 68 | scattered : { 69 | strokeColor: strokeColorFourth, 70 | fill: 'white', 71 | strokeWidth: 2, 72 | radius: 5 73 | }, 74 | label: 'green' 75 | } 76 | ]; 77 | 78 | var handler = new Events(); 79 | var node = '
' + spark({ 80 | mixin: [Label], 81 | _call: function (scale) { 82 | 83 | handler.setProps(scale, this.attributes.data); 84 | // self.props.events.setProps(scale, this.attributes.data); 85 | }, 86 | }).attr({ 87 | chart: { 88 | width: 1200, 89 | height: 150, 90 | yAxis: { 91 | multi: true 92 | }, 93 | xAxis : { 94 | // including format will show the xAxis Label 95 | format : 'dateTime', 96 | // interval indicates the data interval, the number of the interval indicates the label tick interval 97 | // same representation is also used for `dateTimeLabelFormat` 98 | // s - seconds 99 | // m - minutes 100 | // h - hours 101 | // D - days 102 | // M - months 103 | // Y - years 104 | interval: '4h', //[1-9]s, [1-9]m, [1-9]h, [1-9]D, [1-9]M, [1-9]Y 105 | // uses the min start date and increment the label by the set interval. interval will be converted to miliseconds 106 | minUTC: Date.UTC(2013,8,7), 107 | //this controls the dateTime label format 108 | //depending on the format, it will affect the label, try :: dateTimeLabelFormat: 'hhh' 109 | dateTimeLabelFormat: 'MM/DD hh ap' 110 | // or if wanted custom label 111 | // format: 'label', 112 | // labels: [Array of label], this label must match the data value length, if not the data will be limited. We will not aggregate the data for you. 113 | } 114 | }, 115 | data: set 116 | }) + '
' + spark({ 117 | mixin: [Label] 118 | }).attr({ 119 | chart: { 120 | width: 1200, 121 | height: 150, 122 | yAxis: { 123 | multi: true 124 | }, 125 | xAxis : { 126 | // including format will show the xAxis Label 127 | format : 'dateTime', 128 | // interval indicates the data interval, the number of the interval indicates the label tick interval 129 | // same representation is also used for `dateTimeLabelFormat` 130 | // s - seconds 131 | // m - minutes 132 | // h - hoursp 133 | // D - days 134 | // M - months 135 | // Y - years 136 | interval: '4h', //[1-9]s, [1-9]m, [1-9]h, [1-9]D, [1-9]M, [1-9]Y 137 | // uses the min start date and increment the label by the set interval. interval will be converted to miliseconds 138 | minUTC: Date.UTC(2013,8,7), 139 | //this controls the dateTime label format 140 | //depending on the format, it will affect the label, try :: dateTimeLabelFormat: 'hhh' 141 | dateTimeLabelFormat: 'MM/DD hh ap' 142 | // or if wanted custom label 143 | // format: 'label', 144 | // labels: [Array of label], this label must match the data value length, if not the data will be limited. We will not aggregate the data for you. 145 | } 146 | }, 147 | data: set2 148 | }) + "
"; 149 | 150 | handler.on = { 151 | 'svg:mouseMove': function (e, props) { 152 | var pos = handler.getToolTipPosition(props); 153 | console.log('relative tooltip position', pos); 154 | } 155 | }; 156 | document.getElementsByTagName("body")[0].innerHTML = node; 157 | handler.listen(document.getElementsByClassName("graph")[0]); -------------------------------------------------------------------------------- /dev/reactApp.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Base = require('./base'); 3 | 4 | var dataPoints = 10; 5 | var dataSet = []; 6 | var dataSet2 = []; 7 | var dataSet3 = []; 8 | var dataSet4 = []; 9 | for (var i=0;i < dataPoints;i++) { 10 | dataSet.push(Math.floor((Math.random() * 500) + 10)); 11 | dataSet2.push(Math.floor((Math.random() * 500) + 10)); 12 | dataSet3.push(Math.floor((Math.random() * 500) + 10)); 13 | dataSet4.push(Math.floor((Math.random() * 500) + 10)); 14 | } 15 | 16 | var strokColorFirst = 'red'; 17 | var strokeColorSecond = 'blue'; 18 | var strokeColorThird = 'pink'; 19 | var strokeColorFourth = 'green'; 20 | var set = [ 21 | { 22 | data: dataSet, 23 | //color controls the line 24 | strokeColor: strokColorFirst, 25 | strokeWidth: 2, 26 | scattered : { 27 | strokeColor: strokColorFirst, 28 | fill: 'white', 29 | strokeWidth: 2, 30 | radius: 5 31 | }, 32 | label: 'red' 33 | //nodeColor controls the pointer color 34 | }, 35 | { 36 | data: dataSet2, 37 | strokeColor: strokeColorSecond, 38 | strokeWidth: 2, 39 | scattered : { 40 | strokeColor: strokeColorSecond, 41 | fill: 'white', 42 | strokeWidth: 2, 43 | radius: 5 44 | }, 45 | label: 'blue' 46 | } 47 | ]; 48 | var set2 = [ 49 | { 50 | data: dataSet3, 51 | //color controls the line 52 | strokeColor: strokeColorThird, 53 | strokeWidth: 2, 54 | scattered : { 55 | strokeColor: strokeColorThird, 56 | fill: 'white', 57 | strokeWidth: 2, 58 | radius: 5 59 | }, 60 | label: 'pink' 61 | //nodeColor controls the pointer color 62 | }, 63 | { 64 | data: dataSet4, 65 | strokeColor: strokeColorFourth, 66 | strokeWidth: 2, 67 | scattered : { 68 | strokeColor: strokeColorFourth, 69 | fill: 'white', 70 | strokeWidth: 2, 71 | radius: 5 72 | }, 73 | label: 'green' 74 | } 75 | ]; 76 | 77 | React.render( 78 |
79 | 80 | 81 |
, 82 | document.getElementsByTagName('body')[0]); 83 | 84 | // var Profiler = React.createClass({ 85 | // getInitialState: function (){ 86 | // return { 87 | // gogogo: false 88 | // } 89 | // }, 90 | // componentDidMount() { 91 | // var self = this; 92 | // setTimeout(function () { 93 | // self.setState({ 94 | // gogogo: true 95 | // }); 96 | // }, 1e3) 97 | // }, 98 | 99 | // render() { 100 | // if (!this.state.gogogo) { 101 | // return null; 102 | // } 103 | 104 | // return ( 105 | //
106 | // 107 | // 108 | //
109 | // ); 110 | // } 111 | // }) 112 | // React.render( 113 | // , 114 | // document.getElementsByTagName('body')[0]); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var webpack = require('webpack'); 3 | var plugins = require('gulp-load-plugins')(); 4 | var _ = require('lodash'); 5 | var gWebpack = plugins.webpack; 6 | 7 | gulp.task('pack:lite', function () { 8 | var webpackConfig = _.clone(require('./webpack.config.js')); 9 | 10 | return gulp.src('./index') 11 | .pipe(plugins.plumber()) 12 | .pipe(gWebpack(webpackConfig, webpack)) 13 | .pipe(gulp.dest('dist')); 14 | }); 15 | 16 | gulp.task('minify:lite', function () { 17 | var webpackConfig = _.clone(require('./webpack.config.js')); 18 | _.assign(webpackConfig, { 19 | entry: { 20 | 'yako.min': './build-tools/expose.build' 21 | }, 22 | plugins: [ 23 | new webpack.NoErrorsPlugin(), 24 | new webpack.optimize.UglifyJsPlugin({ 25 | compress: { 26 | warnings: false 27 | } 28 | }) 29 | ] 30 | }); 31 | 32 | return gulp.src('./index') 33 | .pipe(plugins.plumber()) 34 | .pipe(gWebpack(webpackConfig, webpack)) 35 | .pipe(gulp.dest('dist')); 36 | }); 37 | 38 | gulp.task('pack:addons', function () { 39 | var webpackConfig = _.clone(require('./webpack.config.js')); 40 | _.assign(webpackConfig, { 41 | entry: { 42 | 'yako.addons': './build-tools/addons.expose.build' 43 | } 44 | }); 45 | 46 | return gulp.src('./addons/index') 47 | .pipe(plugins.plumber()) 48 | .pipe(gWebpack(webpackConfig, webpack)) 49 | .pipe(gulp.dest('dist')); 50 | }); 51 | 52 | gulp.task('minify:addons', function () { 53 | var webpackConfig = _.clone(require('./webpack.config.js')); 54 | _.assign(webpackConfig, { 55 | entry: { 56 | 'yako.addons.min': './build-tools/addons.expose.build' 57 | }, 58 | plugins: [ 59 | new webpack.NoErrorsPlugin(), 60 | new webpack.optimize.UglifyJsPlugin({ 61 | compress: { 62 | warnings: false 63 | } 64 | }) 65 | ] 66 | }); 67 | 68 | return gulp.src('./addons/index') 69 | .pipe(plugins.plumber()) 70 | .pipe(gWebpack(webpackConfig, webpack)) 71 | .pipe(gulp.dest('dist')); 72 | }); 73 | 74 | gulp.task('pack:demo', function () { 75 | var webpackConfig = _.clone(require('./webpack.config.js')); 76 | _.assign(webpackConfig, { 77 | entry: { 78 | 'bundle': './demo/demo-webpack' 79 | }, 80 | plugins: [ 81 | new webpack.NoErrorsPlugin(), 82 | new webpack.optimize.UglifyJsPlugin({ 83 | compress: { 84 | warnings: false 85 | } 86 | }) 87 | ] 88 | }); 89 | 90 | return gulp.src(['./index.js', './demo/demo-webpack.js']) 91 | .pipe(plugins.plumber()) 92 | .pipe(gWebpack(webpackConfig, webpack)) 93 | .pipe(gulp.dest('demo')); 94 | }); 95 | 96 | gulp.task('pack:example', function () { 97 | var webpackConfig = _.clone(require('./webpack.config.js')); 98 | _.assign(webpackConfig, { 99 | entry: { 100 | 'bundle': './demo/example-webpack' 101 | }, 102 | plugins: [ 103 | new webpack.NoErrorsPlugin(), 104 | new webpack.optimize.UglifyJsPlugin({ 105 | compress: { 106 | warnings: false 107 | } 108 | }) 109 | ] 110 | }); 111 | 112 | return gulp.src(['./index.js', './demo/demo-webpack.js']) 113 | .pipe(plugins.plumber()) 114 | .pipe(gWebpack(webpackConfig, webpack)) 115 | .pipe(gulp.dest('demo')); 116 | }); 117 | 118 | gulp.task("build", function () { 119 | var babel = plugins.babel; 120 | return gulp.src(["src/**/*.js", "src/**/*.es6", "src/**/*.jsx"]) 121 | .pipe(babel({ 122 | blacklist: ["strict"] 123 | })) 124 | .pipe(gulp.dest("lib")); 125 | }); 126 | 127 | gulp.task("clean:build", function (cb) { 128 | var del = require('del'); 129 | del([ 130 | 'lib/' 131 | ], cb); 132 | }); 133 | 134 | gulp.task('pack', ['pack:lite', 'minify:lite', 'pack:addons', 'minify:addons', 'pack:demo', 'pack:example']); 135 | 136 | gulp.task('dev', function () { 137 | var nodemon = plugins.nodemon; 138 | 139 | nodemon({ 140 | scripts: 'app', 141 | ignore: ['node_modules'], 142 | ext: 'js jsx html es6' 143 | }) 144 | .on('restart', function () { 145 | console.log('[Nodemon] Restarting'); 146 | }); 147 | }); 148 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 3 | MIT LICENSE 4 | Alfred Kam (@alfredkam) 5 | */ 6 | 7 | module.exports = require('./lib/index'); 8 | -------------------------------------------------------------------------------- /line-timeSeries.js: -------------------------------------------------------------------------------- 1 | require("babel-core/register")({ 2 | blacklist: ["strict"], 3 | only: /(src|addons)/, 4 | extensions: [".es6"] 5 | }); 6 | 7 | var util = require('util'); 8 | 9 | var yako = require('./src/index'); 10 | var lineGraph = yako.line; 11 | 12 | var http = require('http'); 13 | var express = require('express'); 14 | var app = express(); 15 | var fs = require('fs'); 16 | 17 | var Label = require('./src/addons/Label'); 18 | var testData = {}; 19 | try { 20 | testData = require('./testData/testData.json'); 21 | } catch (err) { 22 | console.warn(err); 23 | } 24 | 25 | // TODO:: fix edge case of 1 data Point 26 | var nOfGraphs = 0; 27 | 28 | var now = Date.now(); 29 | var nodes = ''; 30 | 31 | var diff = (Date.now() - now); 32 | 33 | for (var i = 0; i < testData.length; i++) { 34 | if (!(testData[i].content instanceof Array)) { 35 | continue; 36 | } 37 | nOfGraphs++; 38 | testData[i].content.sort(function (a, b) { 39 | var a = new Date(a.timestamp), 40 | b = new Date(b.timestamp); 41 | return (a.getTime() - b.getTime()); 42 | }); 43 | 44 | var keys = Object.keys(testData[i].content[0]); 45 | var labels = {}; 46 | for (var x = 0; x < keys.length; x++) { 47 | if ((keys[x] == 'publisher_id') || (keys[x] == 'timestamp')) { 48 | continue; 49 | } 50 | labels[keys[x]] = { 51 | strokeColor: yako.spark()._randomColor(), 52 | strokeWidth: '2', 53 | scattered: { 54 | strokeWidth: '1', 55 | radius: '1.5' 56 | } 57 | // fill: yako.spark()._randomColor() 58 | }; 59 | } 60 | 61 | nodes += "
" + lineGraph({ 62 | mixin: [Label] 63 | }).attr({ 64 | chart : { 65 | width: 600, 66 | height: 100, 67 | scattered: true, 68 | // fill: true, 69 | yAxis: (Object.keys(labels).length == 2 ? {multi: true} : true), 70 | xAxis: { 71 | format : 'dateTime', 72 | interval: '1M', //[1-9]s, [1-9]m, [1-9]h, [1-9]D, [1-9]M, [1-9]Y 73 | // minUTC: Date.UTC(2014,4,1), 74 | dateTimeLabelFormat: 'MM / DD' 75 | } 76 | }, 77 | title: 'just a test', 78 | points: { 79 | data: testData[i].content, 80 | labels: labels 81 | } 82 | }) + "
"; 83 | } 84 | 85 | console.log('Took ' + diff + 'ms to generate ' + (nOfGraphs) + ' graphs with avg of ' + (diff/nOfGraphs) + 'ms'); 86 | nodes = '
' + 'Took ' + diff + 'ms to generate ' + (nOfGraphs) +' graphs with avg of ' + (diff/nOfGraphs) + 'ms' + '
' + nodes; 87 | 88 | // test optimization => round all numbers to 1 decimal place 89 | nodes = nodes.replace(/([0-9]+\.[0-9]+)/g, function (match, p1) { 90 | return Math.round10(p1, -1); 91 | }); 92 | 93 | var str = ''+ 94 | ""+ 95 | '' + nodes + ''; 96 | 97 | 98 | var proxy = http.createServer(function (req, res) { 99 | res.writeHead(200, {'Content-Type': 'text/plain'}); 100 | }); 101 | 102 | app.get('/', function (req, res) { 103 | res.send(str); 104 | }); 105 | 106 | app.listen(5000); 107 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yako", 3 | "version": "0.6.0", 4 | "description": "A minimal and light weight graph", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/alfredkam/yakojs.git" 8 | }, 9 | "scripts": { 10 | "test": "./node_modules/istanbul/lib/cli.js cover node_modules/mocha/bin/_mocha test -- --require ./build-tools/pre-require-es.js --recursive", 11 | "start": "node app", 12 | "coveralls": "./node_modules/istanbul/lib/cli.js cover node_modules/mocha/bin/_mocha test -- --require ./build-tools/pre-require-es.js --recursive --report loconly -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", 13 | "pack": "gulp build && gulp pack", 14 | "prepublish": "gulp build && gulp pack", 15 | "postpublish": "gulp clean:build", 16 | "tag": "git tag -a v$npm_package_version" 17 | }, 18 | "keywords": [ 19 | "graph", 20 | "graph library", 21 | "graph api", 22 | "beautiful graph", 23 | "minimal graph", 24 | "serverside rendering", 25 | "chart", 26 | "react" 27 | ], 28 | "engines": { 29 | "node": ">=0.8.0" 30 | }, 31 | "licenses": [ 32 | { 33 | "type": "MIT", 34 | "url": "https://github.com/alfredkam/yakojs/blob/master/LICENSE.md" 35 | } 36 | ], 37 | "author": "Alfred Kam", 38 | "readmeFilename": "README.md", 39 | "devDependencies": { 40 | "autoprefixer-loader": "^3.1.0", 41 | "babel-core": "^5.8.22", 42 | "babel-loader": "^5.3.2", 43 | "chai": "^3.3.0", 44 | "coveralls": "^2.11.2", 45 | "del": "^2.0.2", 46 | "eslint": "^1.5.1", 47 | "expect": "^1.6.0", 48 | "expose-loader": "^0.6.0", 49 | "express": "^4.13.3", 50 | "file-loader": "^0.8.1", 51 | "gulp": "^3.8.11", 52 | "gulp-babel": "^5.1.0", 53 | "gulp-load-plugins": "^1.0.0-rc.1", 54 | "gulp-nodemon": "^2.0.3", 55 | "gulp-plumber": "^1.0.0", 56 | "gulp-webpack": "^1.4.0", 57 | "istanbul": "^0.3.14", 58 | "jsx-loader": "^0.13.2", 59 | "jymin": "^0.5.1", 60 | "less": "^2.4.0", 61 | "less-loader": "^2.1.0", 62 | "lodash": "^3.5.0", 63 | "mocha": "^2.1.0", 64 | "mocha-lcov-reporter": "1.0.0", 65 | "react": "^15.0.0", 66 | "react-addons-pure-render-mixin": "^15.2.1", 67 | "react-dom": "^15.0.0", 68 | "react-hot-loader": "^1.2.3", 69 | "style-loader": "^0.12.4", 70 | "webpack": "^1.11.0", 71 | "webpack-dev-middleware": "^1.0.11", 72 | "webpack-dev-server": "^1.7.0" 73 | }, 74 | "dependencies": {} 75 | } 76 | -------------------------------------------------------------------------------- /src/API_DRAFT_README.md: -------------------------------------------------------------------------------- 1 | #Yako's next version graphing api (draft) 2 | Building upon v0.3.X experiences and feedbacks. 3 | 4 | ##Usage 5 | Simplifying the entry point and consumable entry points. To define a graph in a chart, simpily include the chart prefix as your key and its data set into the mix. The avaliable charts are listed below. 6 | 7 | ```javascript 8 | var yako = require('yako'); 9 | yako({ 10 | mixins: [ ... ] 11 | }).attr({ 12 | width: 300, 13 | height: 100, 14 | points: { 15 | spark: [ 16 | ... 17 | ], 18 | bubble: [ 19 | ... 20 | ], 21 | bar: [ 22 | ... 23 | ], 24 | pie: [ 25 | ... 26 | ], 27 | donut: [ 28 | ... 29 | ], 30 | /* Charts below are time series base */ 31 | line: [ 32 | ... 33 | ], 34 | bubbleLine: [ 35 | ... 36 | ] 37 | } 38 | }) 39 | ``` 40 | 41 | ##Spark 42 | Spark is designed for simple trivial graph, and works with a simple set of data. 43 | 44 | ```javascript 45 | spark: [ 46 | { 47 | data: [Number, Number, Number], 48 | strokeWidth: 2, 49 | strokeColor: '#f0f0f0' 50 | fill: '#000', 51 | scatter: { 52 | strokeWidth: 2 53 | strokeColor: 'red', 54 | fill: 'white' 55 | } 56 | }, 57 | { 58 | ... 59 | } 60 | ] 61 | ``` 62 | 63 | ##Bubble 64 | Bubble graph, best for representing a cohort of sample size 65 | 66 | ```javascript 67 | bubble: { 68 | maxRadius: 10, // Caps the maxRadius 69 | fill: '#000', // Sets the default fill color 70 | strokeWidth: '2', // Sets the default stroke width, 71 | strokeColor: '#000', // Sets the default stroke color 72 | /* Data Set */ 73 | points: [{ 74 | data: [0,1,3], 75 | fill: '#000', 76 | /* Optional Params */ 77 | strokeWidth: 2, 78 | strokeColor: '#000' 79 | metaData: {} 80 | },{ 81 | ... 82 | }] 83 | } 84 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | ## Note 2 | This directory (es6) gets transpiled into ```lib/``` (es5) 3 | -------------------------------------------------------------------------------- /src/addons/Events.es6: -------------------------------------------------------------------------------- 1 | /** 2 | * An add on to interact and trigger events 3 | */ 4 | 5 | import eventFeedback from './Events.feedback'; 6 | import error from '../utils/error'; 7 | /** 8 | * Event filter definitions 9 | */ 10 | var shortHandBindFilterDefinitions = { 11 | 'hover': ['onMouseOver','onMouseLeave'], 12 | 'click': ['onClick'], 13 | 'mouseMove': ['onMouseMove'], 14 | 'mouseEnter': ['onMouseEnter'], 15 | 'mouseOver': ['onMouseOver'], 16 | 'mouseOut': ['onMouseOut'], 17 | 'mouseUp': ['onMouseUp'], 18 | 'mouseLeave': ['onMouseLeave'], 19 | 'doubleClick': ['onDoubleClick'] 20 | }; 21 | var ignore = () => {}; 22 | 23 | export default class Events { 24 | 25 | constructor () { 26 | // A list of tagName w/ event combination in key - value format for fast filtering. Hydrate from `hydrate` function 27 | this._events = {}; 28 | this._props = {}; 29 | // The events should register with the top level binding 30 | this._toRegister = {}; 31 | /** 32 | * A user defined event map, eg: 33 | * 'container:mouseLeave': function (e) { 34 | * // do something 35 | * }, 36 | * 'svg:mousemove': function (e) { 37 | * // do something 38 | * } 39 | */ 40 | this.on = {}; 41 | return this; 42 | } 43 | 44 | // Sets props 45 | setProps (scale, data) { 46 | this._props = { 47 | scale: scale, 48 | data: data 49 | }; 50 | } 51 | 52 | // The external call back for the top level event binding to emit the event 53 | _emit (e) { 54 | this._associateTriggers(e); 55 | } 56 | 57 | // Manually pass the domObj thats pass in, and add the events 58 | listen (domObj) { 59 | var self = this; 60 | self._element = domObj; 61 | self.hydrate(); 62 | var props = self._toRegister; 63 | var keys = Object.keys(props); 64 | for(var i = 0; i < keys.length; i++) { 65 | domObj.addEventListener( 66 | keys[i].replace('on','').toLowerCase(), 67 | props[keys[i]], 68 | false 69 | ); 70 | } 71 | } 72 | 73 | // Manually removes the event listener 74 | removeListener () { 75 | var self = this; 76 | var domObj = self._element; 77 | var props = self._toRegister; 78 | var keys = Object.keys(props); 79 | for(var i = 0; i < keys.length; i++) { 80 | domObj.removeEventListener(keys[i].replace('on','').toLowerCase(),props[keys[i]], false); 81 | } 82 | } 83 | 84 | // Registers the events list we want to listen to 85 | hydrate () { 86 | var self = this; 87 | var filters = Object.keys(self.on); 88 | var list = {}; 89 | var eventsToRegister = {}; 90 | 91 | for (var i = 0; i < filters.length; i++) { 92 | filters[i].replace(/(.*):(.*)/, function (match, tagName, eventName) { 93 | if (shortHandBindFilterDefinitions[eventName]) { 94 | for (var x = 0; x < shortHandBindFilterDefinitions[eventName].length; x++) { 95 | var e = shortHandBindFilterDefinitions[eventName][x]; 96 | var eLower = e.toLowerCase(); 97 | list[tagName + ':' + eLower] = list[tagName + ':' + eLower] || []; 98 | list[tagName + ':' + eLower].push(eventName); 99 | eventsToRegister[e] = function (e) { 100 | self._emit(e); 101 | }; 102 | } 103 | } 104 | }); 105 | } 106 | self._events = list; 107 | self._toRegister = eventsToRegister; 108 | } 109 | 110 | // TODO:: handle case when child event provides a stop pragation 111 | // Entry point for top level event binding that will distribute to rest of binding 112 | _associateTriggers (e, next) { 113 | e = e || window.event; 114 | var self = this; 115 | var events = self._events; 116 | var props = self._props; 117 | 118 | var tagNames = []; 119 | var target = e.target || e.srcElement; 120 | tagNames.push(e.target.tagName.toLowerCase() == 'div' ? 'container' : e.target.tagName.toLowerCase()); 121 | tagNames.push(e.currentTarget.tagName.toLowerCase() == 'div' ? 'container' : e.currentTarget.tagName.toLowerCase()); 122 | 123 | for (var i = 0; i < tagNames.length; i++) { 124 | var eventProps = events[tagNames[i] + ':on' + e.type] || 0; 125 | if (eventProps) { 126 | for (var x = 0; x < eventProps.length; x++) { 127 | self._trigger(tagNames[i] + ':' + eventProps[x], e, props, next); 128 | } 129 | } 130 | } 131 | } 132 | 133 | 134 | // Common entry point for each _associateTrigger once the eventName is associated 135 | // Here it provides the material at those event points - if the data is avaliable 136 | _trigger (eventName, e, props, next) { 137 | var self = this; 138 | var scale = props.scale; 139 | // For react, uses nativeEvent 140 | e.nativeEvent = e.nativeEvent || e; 141 | var eX = e.nativeEvent.offsetX; 142 | var eY = e.nativeEvent.offsetY; 143 | next = next || ignore; 144 | var properties = {}; 145 | 146 | if (eventFeedback[scale.componentName]) { 147 | properties = eventFeedback[scale.componentName](e, props, eX, eY); 148 | } else { 149 | error.eventFeedback(scale.componentName || ''); 150 | } 151 | 152 | self.on[eventName](e, properties); 153 | next(properties); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/addons/Events.feedback.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Expected feedback for different chart types 3 | */ 4 | 5 | module.exports = { 6 | 7 | spark: function (e, props, eX, eY) { 8 | var target = e.target; 9 | var self = this; 10 | var ref = ((target.dataset || '')._ref || target.getAttribute('data-_ref')) || 0; 11 | var points = []; 12 | var scale = props.scale; 13 | var data = props.data; 14 | // if out of quadrant should return 15 | var quadrantX = (eX - scale.paddingLeft - scale.innerPaddingLeft + (scale.tickSize / 2)) / (scale.tickSize * scale.len); 16 | quadrantX = Math.floor(quadrantX * scale.len); 17 | 18 | var properties = { 19 | _scale: scale, 20 | _segmentXRef: quadrantX 21 | }; 22 | 23 | if (ref && (data[ref])) { 24 | properties.exactPoint = { 25 | label: data[ref].label, 26 | value: data[ref].data[quadrantX] 27 | }; 28 | properties._data = data[ref]; 29 | } 30 | 31 | for (var i in data) { 32 | points.push({ 33 | label: data[i].label, 34 | value: data[i].data[quadrantX] 35 | }); 36 | } 37 | 38 | properties.points = points; 39 | 40 | if(!ref) { 41 | properties._data = data; 42 | } 43 | return properties; 44 | }, 45 | 46 | line: function (e, props, eX, eY) { 47 | var target = e.target; 48 | var self = this; 49 | var ref = ((target.dataset || '')._ref || target.getAttribute('data-_ref')) || 0; 50 | var points = []; 51 | var scale = props.scale; 52 | var data = props.data; 53 | // if out of quadrant should return 54 | var quadrantX = (eX - scale.paddingLeft - scale.innerPaddingLeft + (scale.tickSize / 2)) / (scale.tickSize * scale.len); 55 | quadrantX = Math.floor(quadrantX * scale.len); 56 | 57 | var properties = { 58 | _scale: scale, 59 | _segmentXRef: quadrantX 60 | }; 61 | 62 | if (ref && (data[ref])) { 63 | properties.exactPoint = { 64 | label: data[ref].label, 65 | value: data[ref].data[quadrantX] 66 | }; 67 | properties._data = data[ref]; 68 | } 69 | 70 | for (var i in data) { 71 | points.push({ 72 | label: data[i].label, 73 | value: data[i].data[quadrantX] 74 | }); 75 | } 76 | 77 | properties.points = points; 78 | 79 | if(!ref) { 80 | properties._data = data; 81 | } 82 | return properties; 83 | }, 84 | 85 | // Alternatively Bubble line 86 | 'bubble.point': function (e, props, eX, eY) { 87 | var scale = props.scale; 88 | var data = props.data; 89 | var target = e.target; 90 | var column = ((target.dataset || '').c || target.getAttribute('data-c')); 91 | var point = data[column] || 0; 92 | var tickSize = scale.tickSize; 93 | var startTick = scale.startTick; 94 | var minRadius = scale.minRadius || 0; 95 | var radius = (scale.maxRadius - minRadius) * point.data / scale.max; 96 | var cx; 97 | radius = radius ? radius + minRadius : 0; 98 | 99 | if (scale.autoFit == false) { 100 | var column = ((target.dataset || '').c || target.getAttribute('data-c')); 101 | cx = (column * tickSize) + scale.paddingLeft + scale.innerPaddingLeft; 102 | } else { 103 | cx = ((point.date.getTime() - startTick) * tickSize) + scale.paddingLeft + scale.innerPaddingLeft; 104 | } 105 | 106 | return { 107 | scale: scale, 108 | _segmentXRef: column, 109 | exactPoint: { 110 | data: { 111 | x : point.data, 112 | meta: point 113 | }, 114 | eY : eY, 115 | eX : eX, 116 | cY : scale.height / 2, 117 | cX : cx, 118 | r : radius 119 | } 120 | }; 121 | }, 122 | 123 | 'bubble.scatter': function (e, props, eX, eY) { 124 | var scale = props.scale; 125 | var data = props.data; 126 | var target = e.target; 127 | var row = ((target.dataset || '').r || target.getAttribute('data-r')); 128 | var column = ((target.dataset || '').c || target.getAttribute('data-c')); 129 | var point = data[column].data; 130 | var minRadius = scale.minRadius || 0; 131 | var radius = (scale.maxRadius - minRadius) * (point[2]/scale.max[2]); 132 | radius = radius ? radius + minRadius : 0; 133 | return { 134 | _scale: scale, 135 | exactPoint: { 136 | data: { 137 | x : point[0], 138 | y : point[1], 139 | z : point[2], 140 | meta: data[column] 141 | }, 142 | eY: eY, 143 | eX: eX, 144 | cX: scale.hasInverse.x ? (point[0] * scale.widthRatio) + scale.paddingLeft + scale.innerPaddingLeft : scale.width - (point[0] * scale.widthRatio) - scale.paddingLeft - scale.innerPaddingLeft, 145 | cY: scale.hasInverse.y ? scale.paddingTop + scale.innerPaddingTop + (point[1] * scale.heightRatio) : scale.height - (point[1] * scale.heightRatio) - scale.paddingTop - scale.innerPaddingTop, 146 | r: radius 147 | } 148 | }; 149 | } 150 | }; 151 | -------------------------------------------------------------------------------- /src/addons/ReturnAsObject.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An addon to return content as an object 3 | * Usage documentation under https://github.com/alfredkam/yakojs/blob/master/doc.md#returnasobject 4 | */ 5 | 6 | var isArray = function (obj) { 7 | return obj instanceof Array; 8 | }; 9 | 10 | var obj = module.exports = { 11 | // Extends default make from lib/classes/common.js 12 | make: function (tagName, attribute, dataAttribute, content){ 13 | var json = {}; 14 | json[tagName] = attribute; 15 | if (content) { 16 | json[tagName].textContent = content; 17 | } 18 | return json; 19 | }, 20 | // Extends default append from lib/base/common.js 21 | append: function (parent, childs){ 22 | if (parent === '') return childs; 23 | 24 | if (!isArray(childs)) { 25 | childs = [childs]; 26 | } 27 | var tagName = Object.keys(parent)[0]; 28 | if (isArray(parent)) { 29 | parent[tagName].push(childs); 30 | } else { 31 | parent[tagName] = childs; 32 | } 33 | return parent; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/addons/index.es6: -------------------------------------------------------------------------------- 1 | import Label from './Label'; 2 | import ReturnAsObject from './ReturnAsObject'; 3 | import Events from './Events'; 4 | 5 | export default { 6 | 7 | Label: Label, 8 | 9 | ReturnAsObject: ReturnAsObject, 10 | 11 | Events: Events 12 | }; 13 | -------------------------------------------------------------------------------- /src/addons/react-components/bar.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var bar = require('../../index').bar; 3 | 4 | module.exports = React.createClass({ 5 | 6 | displayName: 'YakoBar', 7 | 8 | render: function () { 9 | var self = this; 10 | var svg = bar() 11 | .attr(self.props.attr); 12 | 13 | return ( 14 |
15 | ) 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /src/addons/react-components/bubble.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | Point: require('./bubble/bubble-point'), 4 | 5 | Scatter: require('./bubble/bubble-scatter') 6 | } -------------------------------------------------------------------------------- /src/addons/react-components/bubble/bubble-point.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var EventsClass = require('../../Events'); 3 | var cssPrefix = ['Moz','Webkit','ms','O']; 4 | var PureRenderMixin = require('react-addons-pure-render-mixin'); 5 | var GraphPureRenderMixin = require('../utils/GraphPureRenderMixin'); 6 | 7 | /* Bubble Component */ 8 | var Bubble = React.createClass({ 9 | 10 | displayName: 'YakoBubblePoint', 11 | 12 | mixin: [ 13 | PureRenderMixin, 14 | GraphPureRenderMixin 15 | ], 16 | 17 | render: function () { 18 | var self = this; 19 | var bubblePoint = require('../../../index').bubble.point; 20 | var attr = self.props.attr || {}; 21 | var svg = bubblePoint({ 22 | mixin: { 23 | _call: function (scale) { 24 | scale.hasEvents = true; 25 | scale.parentType = 'bubble'; 26 | self.props.events.setProps(scale, this.attributes.data); 27 | } 28 | } 29 | }).attr(attr); 30 | return React.createElement("span", { 31 | dangerouslySetInnerHTML: { 32 | __html: svg 33 | } 34 | }); 35 | } 36 | }); 37 | 38 | // TODO:: Decouple tooltip logics 39 | /* EventHandling Component */ 40 | module.exports = React.createClass({ 41 | 42 | displayName: 'YakoBubblePointEventHandler', 43 | 44 | _eventData: {}, 45 | 46 | eventsHandler: '', 47 | 48 | setScale: function (scale) { 49 | this._scale = scale; 50 | }, 51 | 52 | // base 53 | componentWillMount: function () { 54 | var self = this; 55 | this.eventsHandler = Events = new EventsClass(); 56 | var userEvents = self.props.events; 57 | Events._emit = self.triggers; 58 | Events.on = userEvents.on || {}; 59 | Events.hydrate(); 60 | }, 61 | 62 | triggers: function (e) { 63 | var self = this; 64 | this.eventsHandler._associateTriggers(e, function (props) { 65 | self._eventData = props; 66 | }); 67 | }, 68 | 69 | render: function () { 70 | var self = this; 71 | var Events = this.eventsHandler; 72 | var props = Events._toRegister; 73 | var attr = self.props.attr || {}; 74 | var content = []; 75 | var style = { 76 | width: attr.width || 200, 77 | position: 'relative' 78 | }; 79 | props.style = style; 80 | 81 | var userDefinedToolTip = self.props.toolTip || {}; 82 | var Legend = self.props.legend || 0; 83 | var ToolTipReactElement = userDefinedToolTip.reactElement || 0; 84 | var CustomComponent = self.props.customComponent || 0; 85 | 86 | if (ToolTipReactElement) { 87 | content.push( 88 | 91 | ); 92 | } 93 | 94 | content.push(); 98 | 99 | if (Legend){ 100 | content.push(); 101 | } 102 | 103 | if (CustomComponent) { 104 | content = content.concat(CustomComponent); 105 | } 106 | 107 | var factory = React.createFactory("div"); 108 | return factory(props, 109 | content 110 | ); 111 | } 112 | }); 113 | -------------------------------------------------------------------------------- /src/addons/react-components/bubble/bubble-scatter.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var EventsClass = require('../../Events'); 3 | var cssPrefix = ['Moz','Webkit','ms','O']; 4 | var PureRenderMixin = require('react-addons-pure-render-mixin'); 5 | var GraphPureRenderMixin = require('../utils/GraphPureRenderMixin'); 6 | 7 | /* Bubble Component */ 8 | var Bubble = React.createClass({ 9 | 10 | displayName: 'YakoBubbleScatter', 11 | 12 | mixin: [ 13 | PureRenderMixin, 14 | GraphPureRenderMixin 15 | ], 16 | 17 | render: function () { 18 | var self = this; 19 | var bubblePoint = require('../../../index').bubble.scatter; 20 | var attr = self.props.attr || {}; 21 | var svg = bubblePoint({ 22 | mixin: { 23 | _call: function (scale) { 24 | scale.hasEvents = true; 25 | scale.parentType = 'bubble'; 26 | self.props.events.setProps(scale, this.attributes.data); 27 | } 28 | } 29 | }).attr(attr); 30 | return React.createElement("span", { 31 | dangerouslySetInnerHTML: { 32 | __html: svg 33 | } 34 | }); 35 | } 36 | }); 37 | 38 | // TODO:: Decouple tooltip logics 39 | /* EventHandling Component */ 40 | module.exports = React.createClass({ 41 | 42 | displayName: 'YakoBubbleScatterEventHandler', 43 | 44 | _eventData: {}, 45 | 46 | eventsHandler: '', 47 | 48 | setScale: function (scale) { 49 | this._scale = scale; 50 | }, 51 | 52 | // base 53 | componentWillMount: function () { 54 | var self = this; 55 | this.eventsHandler = Events = new EventsClass(); 56 | var userEvents = self.props.events; 57 | Events._emit = self.triggers; 58 | Events.on = userEvents.on || {}; 59 | Events.hydrate(); 60 | }, 61 | 62 | triggers: function (e) { 63 | var self = this; 64 | this.eventsHandler._associateTriggers(e, function (props) { 65 | self._eventData = props; 66 | }); 67 | }, 68 | 69 | render: function () { 70 | var self = this; 71 | var Events = this.eventsHandler; 72 | var props = Events._toRegister; 73 | var attr = self.props.attr || {}; 74 | var content = []; 75 | var style = { 76 | width: attr.width || 200, 77 | position: 'relative' 78 | }; 79 | props.style = style; 80 | 81 | var userDefinedToolTip = self.props.toolTip || {}; 82 | var Legend = self.props.legend || 0; 83 | var ToolTipReactElement = userDefinedToolTip.reactElement || 0; 84 | var CustomComponent = self.props.customComponent || 0; 85 | 86 | if (ToolTipReactElement) { 87 | content.push( 88 | 91 | ); 92 | } 93 | 94 | content.push(); 98 | 99 | if (Legend){ 100 | content.push(); 101 | } 102 | 103 | if (CustomComponent) { 104 | content = content.concat(CustomComponent); 105 | } 106 | 107 | var factory = React.createFactory("div"); 108 | 109 | return factory(props, 110 | content 111 | ); 112 | } 113 | }); 114 | -------------------------------------------------------------------------------- /src/addons/react-components/donut.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var donut = require('../../index').donut; 3 | 4 | module.exports = React.createClass({ 5 | 6 | displayName: 'YakoDonut', 7 | 8 | render: function () { 9 | var self = this; 10 | var svg = donut() 11 | .attr(self.props.attr); 12 | 13 | return ( 14 |
15 | ) 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /src/addons/react-components/event-ready/spark-event.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Label = require('../../Label'); 3 | var PureRenderMixin = require('react-addons-pure-render-mixin'); 4 | 5 | module.exports = React.createClass({ 6 | displayName: 'YakoSparkEventHandler', 7 | 8 | mixin: [PureRenderMixin], 9 | 10 | shouldComponentUpdate: function (nextProps) { 11 | if (this.props.data == nextProps.data) { 12 | return false; 13 | } 14 | return true; 15 | }, 16 | 17 | render: function () { 18 | var self = this; 19 | var spark = require('../../../index').spark; 20 | var chart = self.props.chart || {}; 21 | 22 | var svg = spark({ 23 | mixin: [Label], 24 | _call: function (scale) { 25 | self.props.events.setProps(scale, this.attributes.data); 26 | }, 27 | }).attr({ 28 | 'chart': chart, 29 | 'data' : self.props.data || [] 30 | }); 31 | return React.createElement("span", { 32 | dangerouslySetInnerHTML: { 33 | __html: svg 34 | } 35 | }); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /src/addons/react-components/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Bar: require('./bar'), 3 | Bubble: require('./bubble'), 4 | Donut: require('./donut'), 5 | Pie: require('./pie'), 6 | Spark: require('./spark'), 7 | Line: require('./line'), 8 | SimpleSpark: require('./simpleSpark') 9 | }; 10 | -------------------------------------------------------------------------------- /src/addons/react-components/legend.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var PureRenderMixin = require('react-addons-pure-render-mixin'); 3 | var extend = require('../../utils/extend'); 4 | 5 | module.exports = React.createClass({ 6 | 7 | displayName: 'YakoLegend', 8 | 9 | mixins: [PureRenderMixin], 10 | 11 | render: function () { 12 | var self = this; 13 | var defaultStyle = { 14 | visibility: (self.props.shouldShow ? 'visible' : 'hidden'), 15 | position: 'absolute', 16 | top: 0, 17 | left: 0 18 | }; 19 | var style = self.props.style || {}; 20 | extend(defaultStyle, style); 21 | return ( 22 | 23 | {self.props.children} 24 | 25 | ); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /src/addons/react-components/line.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var EventsClass = require('../Events'); 3 | var Legend = require('./legend'); 4 | var GraphPureRenderMixin = require('./utils/GraphPureRenderMixin'); 5 | var cssPrefix = ['Moz','Webkit','ms','O']; 6 | 7 | /* Line Component */ 8 | var Line = React.createClass({ 9 | 10 | displayName: 'YakoLineChart', 11 | 12 | mixins: [ 13 | GraphPureRenderMixin 14 | ], 15 | 16 | render: function () { 17 | var self = this; 18 | var line = require('../../index').line; 19 | var attr = self.props.attr || {}; 20 | 21 | var svg = line({ 22 | mixin: { 23 | _call: function (scale) { 24 | scale.hasEvents = true; 25 | scale.parentType = 'line'; 26 | self.props.events.setProps(scale, this.attributes.data); 27 | } 28 | } 29 | }).attr(attr); 30 | return React.createElement("span", { 31 | dangerouslySetInnerHTML: { 32 | __html: svg 33 | } 34 | }); 35 | } 36 | }); 37 | 38 | module.exports = React.createClass({ 39 | 40 | displayName: 'YakoLineEventHandler', 41 | 42 | _eventData: {}, 43 | 44 | eventsHandler: '', 45 | 46 | setScale: function (scale) { 47 | this._scale = scale; 48 | }, 49 | 50 | componentWillMount: function () { 51 | var self = this; 52 | this.eventsHandler = Events = new EventsClass(); 53 | var userEvents = self.props.events; 54 | Events._emit = self.triggers; 55 | Events.on = userEvents.on || {}; 56 | Events.hydrate(); 57 | }, 58 | 59 | triggers: function (e) { 60 | var self = this; 61 | this.eventsHandler._associateTriggers(e, function (props) { 62 | self._eventData = props; 63 | }); 64 | }, 65 | 66 | render: function () { 67 | var self = this; 68 | var Events = this.eventsHandler; 69 | var props = Events._toRegister; 70 | var attr = self.props.attr || {}; 71 | var content = []; 72 | var style = { 73 | width: attr.width || 200, 74 | position: 'relative' 75 | }; 76 | props.style = style; 77 | 78 | var userDefinedToolTip = self.props.toolTip || {}; 79 | var Legend = self.props.legend || 0; 80 | var ToolTipReactElement = userDefinedToolTip.reactElement || 0; 81 | var CustomComponent = self.props.customComponent || 0; 82 | 83 | if (ToolTipReactElement) { 84 | content.push( 85 | 88 | ); 89 | } 90 | 91 | content.push(); 95 | 96 | if (Legend){ 97 | content.push(); 98 | } 99 | 100 | if (CustomComponent) { 101 | content = content.concat(CustomComponent); 102 | } 103 | 104 | var factory = React.createFactory("div"); 105 | 106 | return factory(props, 107 | content 108 | ); 109 | } 110 | }); 111 | -------------------------------------------------------------------------------- /src/addons/react-components/pie.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var pie = require('../../index').pie; 3 | 4 | module.exports = React.createClass({ 5 | displayName: 'YakoPie', 6 | 7 | render: function () { 8 | var self = this; 9 | var svg = pie() 10 | .attr(self.props.attr); 11 | 12 | return ( 13 |
14 | ) 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /src/addons/react-components/simpleSpark.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var spark = require('../../index').spark; 3 | 4 | module.exports = React.createClass({ 5 | displayName: 'YakoSimpleSpark', 6 | 7 | render: function () { 8 | var self = this; 9 | var svg = spark() 10 | .attr(self.props.attr); 11 | 12 | return ( 13 |
14 | ); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /src/addons/react-components/spark.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Spark = require('./event-ready/spark-event'); 3 | var EventsClass = require('../Events'); 4 | var Legend = require('./legend'); 5 | var cssPrefix = ['Moz','Webkit','ms','O']; 6 | 7 | module.exports = React.createClass({ 8 | displayName: 'YakoSpark', 9 | 10 | _eventData: {}, 11 | 12 | eventsHandler: '', 13 | 14 | setScale: function (scale) { 15 | this._scale = scale; 16 | }, 17 | 18 | // base 19 | componentWillMount: function () { 20 | var self = this; 21 | this.eventsHandler = Events = new EventsClass(); 22 | var userEvents = self.props.events; 23 | Events._emit = self.triggers; 24 | Events.on = userEvents.on || {}; 25 | Events.hydrate(); 26 | }, 27 | 28 | triggers: function (e) { 29 | var self = this; 30 | this.eventsHandler._associateTriggers(e, function (props) { 31 | self._eventData = props; 32 | }); 33 | }, 34 | 35 | render: function () { 36 | var self = this; 37 | var Events = this.eventsHandler; 38 | var props = Events._toRegister; 39 | var attr = self.props.attr || {}; 40 | var content = []; 41 | var style = { 42 | // height: chart.height || 100, 43 | width: attr.width || 200, 44 | position: 'relative' 45 | }; 46 | props.style = style; 47 | // default tool tip settings 48 | var toolTipSettings = { 49 | shouldShow: true, 50 | content: 'test', 51 | className: '', 52 | offsetBottom: 20, 53 | position: {x: 0, y: 0} 54 | }; 55 | 56 | var userDefinedToolTip = self.props.toolTip || {}; 57 | var Legend = self.props.legend || 0; 58 | var ToolTipReactElement = userDefinedToolTip.reactElement || 0; 59 | 60 | if (ToolTipReactElement) { 61 | var position = Events.getToolTipPosition(self._eventData) || {}; 62 | 63 | var toolTipStyle = { 64 | position: 'absolute', 65 | transform: 'translate(' + (position.hasOwnProperty('left') ? position.left : 0) + 'px,' + position.top + 'px)', 66 | visibility: userDefinedToolTip.shouldShow ? 'visible' : 'hidden', 67 | top: 0 68 | }; 69 | 70 | for (var i = 0; i < cssPrefix.length; i++) { 71 | toolTipStyle[cssPrefix[i] + 'Transform'] = toolTipStyle.transform; 72 | } 73 | 74 | if (position.hasOwnProperty('left')) { 75 | toolTipStyle.left = 0; 76 | } else { 77 | toolTipStyle.right = position.right; 78 | } 79 | 80 | content.push( 81 | 83 | ); 84 | } 85 | 86 | content.push(); 89 | 90 | if (Legend){ 91 | content.push(); 92 | } 93 | 94 | var factory = React.createFactory("div"); 95 | return factory(props, 96 | content 97 | ); 98 | } 99 | }); 100 | -------------------------------------------------------------------------------- /src/addons/react-components/utils/GraphPureRenderMixin.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | shouldComponentUpdate: function (nextProps) { 3 | if (this.props.data == nextProps.data) { 4 | return false; 5 | } 6 | return true; 7 | } 8 | } -------------------------------------------------------------------------------- /src/chart.js: -------------------------------------------------------------------------------- 1 | // This is a draft 2 | var Base = require('./classes/default'); 3 | 4 | module.exports = Base.extend({ 5 | _startCycle: function () { 6 | var points = this.attributes.data; 7 | var self = this; 8 | var chart = this.attributes.opts.chart; 9 | var append = self._append; 10 | var svg = self.make('svg',{ 11 | width: chart.width, 12 | height: chart.height, 13 | viewBox: '0 0 ' + chart.width + ' ' + chart.height, 14 | }); 15 | 16 | var path = self._lifeCycleManager(data, chart, function (preliminraryScale) { 17 | var paths = []; 18 | for (var chartType in points) { 19 | paths.push(self._dispatchProps(preliminraryScale, chartType, points[chartType], chart)); 20 | } 21 | return paths; 22 | }); 23 | 24 | return append(self.element, append(svg, path)); 25 | }, 26 | _describeProps: function (data, newScale) { 27 | // Preliminary Scale should be really tiny. 28 | var scaleCopy = JSON.parse(JSON.stringify(preliminraryScale)); 29 | var scale = self._defineBaseScaleProperties(scaleCopy, data, chart); 30 | }, 31 | _dispatchProps: function (preliminraryScale, chartType, data, chartProps) { 32 | var self = this; 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /src/classes/arc.es6: -------------------------------------------------------------------------------- 1 | import Default from './default'; 2 | import arc from '../svg/arc'; 3 | 4 | export default class Arc extends Default { 5 | 6 | // Parent generator that manages the svg 7 | _startCycle (){ 8 | var self = this; 9 | var chart = self.attributes.opts.chart; 10 | var data = self.attributes.data; 11 | 12 | return self._lifeCycleManager(data, chart, function (scale) { 13 | return self._describePath(scale.outerRadius, scale.relativeDataSet, scale); 14 | }); 15 | } 16 | 17 | // Extends _defineBaseScaleProperties in lib/base/common.js 18 | _defineBaseScaleProperties (data, chart) { 19 | var self = this; 20 | var total = self._sumOfData(data); 21 | var scale = { 22 | total: total, 23 | // Converts nums to relative => total sum equals 1 24 | relativeDataSet: self._dataSetRelativeToTotal(data, total), 25 | // Find the max width & height 26 | outerRadius: chart.outerRadius || (chart.height < chart.width ? chart.height : chart.width) / 2 27 | }; 28 | 29 | self._extend(scale, chart); 30 | return scale; 31 | } 32 | 33 | _polarToCartesian () { 34 | return arc.polarToCartesian.apply(this, arguments); 35 | } 36 | 37 | _describeArc () { 38 | return arc.describeArc.apply(this, arguments); 39 | } 40 | 41 | _describePie () { 42 | return arc.describePie.apply(this, arguments); 43 | } 44 | 45 | /** 46 | * [_describePath super class] 47 | * @return {[type]} [empty string] 48 | */ 49 | _describePath () { 50 | return ''; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/classes/common.es6: -------------------------------------------------------------------------------- 1 | import '../utils/adjustDecimal'; 2 | import randomColor from '../utils/randomColor'; 3 | import Errors from '../utils/error'; 4 | import api from '../components/api'; 5 | import composer from '../svg/composer'; 6 | 7 | var isArray = function (obj) { 8 | return obj instanceof Array; 9 | }; 10 | 11 | var inverseList = { 12 | 'x': 'x', 13 | 'y': 'y' 14 | }; 15 | /** 16 | * deep extend object or json properties 17 | * @param {object} object to extend 18 | * @param {object} object 19 | * @return {object} global function object 20 | */ 21 | 22 | export default class Common { 23 | 24 | // default 25 | constructor () { 26 | // data properties 27 | this.props = {}; 28 | return this; 29 | } 30 | 31 | _sumOfData () { 32 | return api.sumOfData.apply(this, arguments); 33 | } 34 | 35 | // accepts a N * 1 array 36 | // finds total sum then creates a relative measure base on total sum 37 | _dataSetRelativeToTotal () { 38 | return api.dataSetRelativeToTotal.apply(this, arguments); 39 | } 40 | 41 | // random color generator 42 | _randomColor () { 43 | return randomColor.apply(this, arguments); 44 | } 45 | 46 | // appends the elements 47 | // accepts multiple child 48 | _append () { 49 | return composer.append.apply(this, arguments); 50 | } 51 | 52 | // alternate to one level deep 53 | make () { 54 | return composer.make.apply(this, arguments); 55 | } 56 | 57 | // Deep copies an object 58 | // TODO:: improve this 59 | _deepCopy (objToCopy) { 60 | return JSON.parse(JSON.stringify(objToCopy)); 61 | } 62 | 63 | /** 64 | * A super class calls right before return the svg content to the user 65 | */ 66 | postRender (svgContent) { 67 | return svgContent; 68 | } 69 | 70 | /** 71 | * [_isArray check if variable is an array] 72 | * @param any type 73 | * @return {Boolean} true if its an array 74 | */ 75 | _isArray () { 76 | return isArray.apply(this, arguments); 77 | } 78 | 79 | // Default ratio 80 | _getRatio (scale) { 81 | scale.heightRatio = scale.height - (scale.paddingTop + scale.paddingBottom) / scale.max; 82 | } 83 | 84 | // Gets invert chart props defined by user 85 | _getInvertProps (scale) { 86 | // Acceptable inverse flags to inverse the data set 87 | var inverse = {}; 88 | if (scale.invert) { 89 | for (var x in scale.invert) { 90 | if (inverseList[scale.invert[x]]) { 91 | inverse[inverseList[scale.invert[x]]] = true; 92 | } 93 | } 94 | } 95 | scale.hasInverse = inverse; 96 | } 97 | 98 | /** 99 | * [_defineBaseScaleProperties defines the common scale properties] 100 | * @param {[obj]} data [raw data set from user] 101 | * @param {[obj]} chart [chart properties passed by the user] 102 | * @return {[obj]} [return an obj that describes the scale base on the data & chart properties] 103 | */ 104 | _defineBaseScaleProperties (data, chart) { 105 | var self = this; 106 | var opts = this.attributes.opts; 107 | // var chart = opts.chart; 108 | var xAxis = chart.xAxis || opts.xAxis; 109 | var yAxis = chart.yAxis || opts.yAxis; 110 | var scale = self._scale(data, chart); 111 | self._extend(scale, chart); 112 | scale._data = data; 113 | self._getInvertProps(scale); 114 | 115 | if ((chart.type != 'bubble-point') && (yAxis || xAxis)) { 116 | if (self._getExternalProps) { 117 | self._getExternalProps(scale, yAxis, xAxis); 118 | } 119 | if (!self.describeYAxis) { 120 | Errors.label(); 121 | } 122 | } 123 | self._getRatio(scale); 124 | return scale; 125 | } 126 | 127 | /** 128 | * base on the feedback and mange the render of the life cycle 129 | * it passes a immutable obj to preRender and audits the user feedback 130 | */ 131 | // TODO:: Rename lifeCycleManager, incorrect term usage 132 | _lifeCycleManager (data, chart, describe) { 133 | var self = this; 134 | var scale = this._defineBaseScaleProperties(data, chart); 135 | scale.componentName = this.componentName; 136 | self.props.scale = scale; 137 | // check if there is any external steps needed to be done 138 | if (this._call) { 139 | this._call(scale); 140 | } 141 | // make the obj's shallow properties immutable 142 | // we can know if we want to skip the entire process to speed up the computation 143 | var properties = (this.preRender ? this.preRender(Object.freeze(this._deepCopy(scale))) : 0); 144 | 145 | // properties we will except 146 | // - append 147 | // - prepend 148 | var paths = properties.prepend ? properties.prepend : []; 149 | paths = paths.concat(describe(scale)); 150 | paths = paths.concat(properties.append ? properties.append : []); 151 | return paths; 152 | // return summary 153 | } 154 | 155 | // only supports 1 level deep 156 | _makePairs () { 157 | return composer.makePairs.apply(this, arguments); 158 | } 159 | 160 | // deep extend 161 | _extend (attr, json) { 162 | var self = this; 163 | if (!json || !attr) return; 164 | 165 | var k = Object.keys(json), len = k.length; 166 | while(len--) { 167 | if (typeof json[k[len]] !== 'object' || isArray(json[k[len]])) { 168 | attr[k[len]] = json[k[len]]; 169 | } else { //it has child objects, copy them too. 170 | if (!attr[k[len]]) { 171 | attr[k[len]] = {}; 172 | } 173 | self._extend(attr[k[len]], json[k[len]]); 174 | } 175 | } 176 | return this; 177 | } 178 | 179 | isFn (object) { 180 | return !!(object && object.constructor && object.call && object.apply); 181 | } 182 | 183 | _makeToken () { 184 | return Math.random().toString(36).substr(2); 185 | } 186 | 187 | //sig fig rounding 188 | _sigFigs () { 189 | return api.sigFigs.apply(this, arguments); 190 | } 191 | 192 | _getSplits () { 193 | return api.getSplits.apply(this, arguments); 194 | } 195 | 196 | // find min max between multiple rows of data sets 197 | // also handles the scale needed to work with multi axis 198 | _scale () { 199 | return api.scale.apply(this, arguments); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/classes/default.es6: -------------------------------------------------------------------------------- 1 | import Common from './common'; 2 | 3 | export default class Base extends Common { 4 | 5 | // Initialize 6 | constructor (node) { 7 | super(); 8 | // adding width 100% will allow us to have responsive graphs (in re-sizing) 9 | if (typeof node === 'string') { 10 | if (node[0] === '#') { 11 | this.element = this.make('div',{ 12 | id: node.replace(/^#/,''), 13 | width: '100%' 14 | }); 15 | } else { 16 | this.element = this.make('div', { 17 | "class": node.replace(/^\./,''), 18 | width: '100%' 19 | }); 20 | } 21 | } else if (typeof node == 'object') { 22 | this.element = ''; 23 | var mixin = node.mixin; 24 | Object.assign(this, mixin); 25 | } else { 26 | this.element = ''; 27 | } 28 | this.token = this._makeToken(); 29 | this.attributes = {}; 30 | return this; 31 | } 32 | 33 | // Include missing values 34 | _prepare () { 35 | var defaults = { 36 | type: 'chart', 37 | width: '100', 38 | height: '100', 39 | paddingLeft: 0, 40 | paddingRight: 0, 41 | paddingTop: 0, 42 | paddingBottom: 0, 43 | innerPadding: 0, 44 | innerPaddingLeft: 0, 45 | innerPaddingRight: 0, 46 | innerPaddingTop: 0, 47 | innerPaddingBottom: 0, 48 | invert: [], 49 | // spark graph configs 50 | line: true, 51 | fill: true, 52 | scattered: false 53 | }; 54 | this._extend(defaults, this.attributes.opts.chart); 55 | this._extend(this.attributes.opts, defaults); 56 | this.attributes.opts.chart = defaults; 57 | return this; 58 | } 59 | 60 | // Public function for user to set & define the graph attributes 61 | attr (opts) { 62 | var self = this; 63 | opts = opts || 0; 64 | // if a user does not include opts.chart 65 | if (typeof opts.chart === 'undefined') { 66 | opts = { 67 | chart: opts, 68 | data: opts.data || opts.points 69 | }; 70 | delete opts.chart.data; 71 | delete opts.chart.points; 72 | } 73 | 74 | self.props.data = self.attributes.data = (opts.data || opts.points) || []; 75 | self.props.opts = self.attributes.opts = opts; 76 | 77 | return self.postRender(self.finalize(self._prepare() 78 | ._startCycle())); 79 | } 80 | 81 | // Add chart layout property into scale 82 | _addChartLayoutProps (scale) { 83 | var { height, width, paddingTop, paddingLeft, paddingRight, paddingBottom, innerPaddingLeft, innerPaddingRight, innerPaddingTop, innerPaddingBottom } = scale; 84 | 85 | scale.layout = { 86 | x: paddingLeft, 87 | y: paddingTop, 88 | height: height - paddingTop - paddingBottom, 89 | padding: { 90 | left: innerPaddingLeft, 91 | right: innerPaddingRight, 92 | top: innerPaddingTop, 93 | bottom: innerPaddingBottom 94 | }, 95 | width: width - paddingLeft - paddingRight, 96 | yToPixel: scale.heightRatio || null, 97 | xToPixel: (scale.widthRatio || scale.tickSize) || null 98 | }; 99 | 100 | return null; 101 | } 102 | 103 | // Wraps content with svg element 104 | finalize (content) { 105 | var self = this; 106 | var appendTo = self._append; 107 | var append = ''; 108 | var prepend = ''; 109 | var scale = self.props.scale; 110 | var opts = self.props.opts; 111 | 112 | var svg = self.make('svg',{ 113 | width: scale.width, 114 | height: scale.height, 115 | viewBox: '0 0 ' + scale.width + ' ' + scale.height 116 | }); 117 | 118 | self._addChartLayoutProps(scale); 119 | 120 | if (opts.prepend || opts.append) { 121 | var immutableScale = Object.freeze(self._deepCopy(scale)); 122 | append = self._getUserContent(opts.append, content, immutableScale) || ''; 123 | prepend = self._getUserContent(opts.prepend, content, immutableScale) || ''; 124 | } 125 | return appendTo(self.element, appendTo(svg, prepend + content + append)); 126 | } 127 | 128 | // If there is additional content from user, it will retrieve it 129 | _getUserContent (fn, content, immutableScale) { 130 | if (!fn) return ''; 131 | 132 | var additionalContent = fn(content, immutableScale) || ''; 133 | 134 | if (typeof additionalContent == 'object') { 135 | additionalContent = additionalContent.stringify(); 136 | } 137 | return additionalContent; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/components/api.js: -------------------------------------------------------------------------------- 1 | var asc = function (a,b) { return a - b; }; 2 | 3 | var api = module.exports = { 4 | 5 | sigFigs: function (n, sig) { 6 | var mult = Math.pow(10, 7 | sig - Math.floor(Math.log(n) / Math.LN10) - 1); 8 | return Math.round(n * mult) / mult; 9 | }, 10 | 11 | // Returns sum of data set 12 | sumOfData: function (data) { 13 | return data.reduce(function (a, b) { 14 | return a + b; 15 | }); 16 | }, 17 | 18 | // accepts a N * 1 array 19 | // finds total sum then creates a relative measure base on total sum 20 | dataSetRelativeToTotal: function (data, total) { 21 | return data.map(function (num) { 22 | return num / total; 23 | }); 24 | }, 25 | 26 | // calculates the number of yAxis sections base on the maxium value 27 | getSplits: function (value) { 28 | var set = {}; 29 | value = Math.ceil(value, 0); // make sure its a whole number 30 | if (value === 0) return { max : 2, splits: 2}; 31 | 32 | var supportedBorders = [3,2,5]; 33 | var digitLen = value.toString().length; 34 | var ceil = splits = 0; 35 | 36 | // now search for the best for number of borders 37 | var checkIfSatisfy = function (v) { 38 | for (var i = 0; i < 3; i++) { 39 | if (v % supportedBorders[i] === 0) 40 | return supportedBorders[i]; 41 | } 42 | return 0; 43 | }; 44 | 45 | var auditSplits = function (v) { 46 | var leftInt = parseInt(v.toString()[0]); 47 | if (leftInt == 1) return 2; 48 | return checkIfSatisfy(leftInt); 49 | }; 50 | 51 | if (digitLen > 2) { 52 | ceil = Math.ceil10(value, digitLen - 1); 53 | splits = auditSplits(ceil); 54 | if (!splits) { 55 | ceil += Math.pow(10, digitLen - 1); 56 | splits = auditSplits(ceil); 57 | } 58 | } else if (digitLen == 2) { 59 | // double digit 60 | ceil = value.toString(); 61 | if (ceil[1] <= 5 && (ceil[0] == 1 || ceil[0] == 2 || ceil[0] == 5 || ceil[0] == 7) && ceil[1] != 0) { 62 | ceil = parseInt(ceil[0] + "5"); 63 | } else { 64 | ceil = Math.ceil10(value, 1); 65 | ceil = ceil == 70 ? 75 : ceil; 66 | } 67 | splits = checkIfSatisfy(ceil); 68 | } else { 69 | // single digit 70 | ceil = value; 71 | splits = checkIfSatisfy(ceil); 72 | if (ceil == 5 || ceil == 3 || ceil == 2) { 73 | splits = 1; 74 | } 75 | if (!splits) { 76 | ceil += 1; 77 | splits = auditSplits(ceil); 78 | } 79 | } 80 | 81 | return { 82 | max: ceil, 83 | splits: splits 84 | }; 85 | }, 86 | 87 | getScaleForMulti: function(data, rows, len) { 88 | // across multi set 89 | // each set of data needs ot have thier own individual min / max 90 | var ySecs = {}; 91 | var min = []; 92 | var max = []; 93 | 94 | for (var i = 0; i < rows; i++) { 95 | temp = data[i].slice(0).sort(asc); 96 | min[i] = temp[0]; 97 | ans = api.getSplits(temp[len - 1]); 98 | max[i] = ans.max; 99 | ySecs[i] = ans.splits; 100 | // delete temp; 101 | } 102 | 103 | return { 104 | min: min, 105 | max: max, 106 | ySecs: ySecs 107 | }; 108 | }, 109 | 110 | // data reduced base by column to find a new combined min / max 111 | getStackedScale: function (data, rows, len, yAxis, min, max) { 112 | var maxSet = []; 113 | var ySecs = 0; 114 | 115 | for (var i = 0; i < len; i++) { 116 | var rowTotal = 0; 117 | for (var j = 0; j < rows; j++) { 118 | rowTotal += data[j][i]; 119 | } 120 | maxSet.push(rowTotal); 121 | max = max < rowTotal ? rowTotal : max; 122 | min = min > rowTotal ? rowTotal : min; 123 | } 124 | 125 | if (yAxis) { 126 | ans = api.getSplits(max); 127 | max = ans.max; 128 | ySecs = ans.splits; 129 | } 130 | 131 | return { 132 | min : min, 133 | max: max, 134 | ySecs: ySecs, 135 | maxSet: maxSet 136 | }; 137 | }, 138 | 139 | // for bubble and need to find min / max across the x, y , z axis 140 | getBubbleScatterScale: function (data, rows, len, yAxis) { 141 | var ySecs = 0; 142 | var min = []; 143 | var max = []; 144 | 145 | for (var x = 0; x < 3; x++) { 146 | min[x] = Number.MAX_VALUE; 147 | max[x] = 0; 148 | } 149 | 150 | for (var i = 0; i < len; i++) { 151 | for (var j = 0; j < rows; j++) { 152 | for (var c = 0; c < 3; c++) { 153 | max[c] = max[c] < data[j][i][c] ? data[j][i][c] : max[c]; 154 | min[c] = min[c] > data[j][i][c] ? data[j][i][c] : min[c]; 155 | } 156 | } 157 | } 158 | 159 | if (yAxis) { 160 | ans = api.getSplits(max[1]); 161 | max[1] = ans.max; 162 | ySecs = ans.splits; 163 | } 164 | 165 | return { 166 | min : min, 167 | max : max, 168 | ySecs: ySecs 169 | }; 170 | }, 171 | 172 | // find min / max across the entire data set 173 | getSimpleScale: function (data, rows, len, yAxis, min, max) { 174 | var ySecs = 0; 175 | for (var i = 0; i < rows; i++) { 176 | temp = data[i].slice(0).sort(asc); 177 | min = min > temp[0] ? temp[0] : min; 178 | max = max < temp[len - 1] ? temp[len - 1] : max; 179 | // delete temp; 180 | } 181 | 182 | if (yAxis) { 183 | ans = api.getSplits(max); 184 | max = ans.max; 185 | ySecs = ans.splits; 186 | } 187 | 188 | return { 189 | min: min, 190 | max: max, 191 | ySecs: ySecs 192 | }; 193 | }, 194 | 195 | // find min max between multiple rows of data sets 196 | // also handles the scale needed to work with multi axis 197 | scale: function (data, opts) { 198 | 199 | opts = opts || 0; 200 | data = typeof data[0] === 'object' ? data : [data]; 201 | var max = 0; 202 | var yAxis = opts.yAxis || (opts.chart ? opts.chart.yAxis : 0); 203 | var min = Number.MAX_VALUE; 204 | var maxSet = []; 205 | var temp; 206 | var ans; 207 | var self = this; 208 | var ySecs = 0; 209 | var getSplits = api.getSplits; 210 | var color = []; 211 | 212 | // change up the structure if the data set is an object 213 | if (data[0].data || (data[0].data == 0)) { 214 | temp = []; 215 | for (var x = 0; x < data.length; x++) { 216 | temp.push(data[x].data); 217 | color.push(data[x].strokeColor); 218 | } 219 | 220 | if (this.componentName == 'bubble.point' || this.componentName == 'bubble.scatter') { 221 | data = [temp]; 222 | } else { 223 | data = temp; 224 | } 225 | } 226 | 227 | var rows = data.length; 228 | var len = data[0].length; 229 | 230 | if (yAxis && yAxis.multi) { 231 | 232 | var result = api.getScaleForMulti(data, rows, len); 233 | min = result.min; 234 | max = result.max; 235 | ySecs = result.ySecs; 236 | 237 | } else if (opts.stack) { 238 | 239 | var result = api.getStackedScale(data, rows, len, yAxis, min, max); 240 | min = result.min; 241 | max = result.max; 242 | ySecs = result.ySecs; 243 | maxSet= result.maxSet; 244 | 245 | } else if (self.componentName == 'bubble.scatter') { 246 | var result = api.getBubbleScatterScale(data, rows, len, yAxis); 247 | min = result.min; 248 | max = result.max; 249 | ySecs = result.ySecs; 250 | 251 | } else { 252 | var result = api.getSimpleScale(data, rows, len, yAxis, min, max); 253 | min = result.min; 254 | max = result.max; 255 | ySecs = result.ySecs; 256 | 257 | } 258 | 259 | return { 260 | min: min, 261 | max: max, 262 | maxSet: maxSet, 263 | len: len, 264 | rows: rows, 265 | ySecs: ySecs, 266 | color: color 267 | }; 268 | } 269 | }; 270 | -------------------------------------------------------------------------------- /src/components/bar.es6: -------------------------------------------------------------------------------- 1 | import Default from '../classes/default'; 2 | 3 | export default class Bar extends Default { 4 | 5 | get componentName() { 6 | return 'bar'; 7 | } 8 | 9 | _startCycle () { 10 | var data = this.attributes.data; 11 | var self = this; 12 | var chart = this.attributes.opts.chart; 13 | chart.type = 'bar'; 14 | 15 | return self._lifeCycleManager(data, chart, function (newScale) { 16 | return self._describeBar(data, newScale); 17 | }); 18 | } 19 | 20 | // Describes the svg that builds out the bar 21 | _describeBar (data, scale) { 22 | if (!data.length) return ''; 23 | // TODO:: need to account paddings for labels 24 | // Wrap in array for consistency 25 | data = typeof data[0] === 'object' ? data : [data]; 26 | var height = scale.height - scale.paddingTop - scale.paddingBottom; 27 | var paddingY = 5; 28 | var width = scale.width - scale.paddingLeft - scale.paddingRight; 29 | var len = data[0].data.length; 30 | var rows = data.length; 31 | var tickSize = width / len; 32 | var paths = []; 33 | 34 | for (var i = 0; i < len; i++) { 35 | // Stack chart 36 | if (scale.stack) { 37 | // The top padding has been taken care off, now account for the bottom padding 38 | var relativeMax = height * scale.maxSet[i] / scale.max; 39 | var yAxis = height - relativeMax + scale.paddingTop; 40 | var total = 0; 41 | for (var j = 0; j < rows; j++) { 42 | paths.push(this.make('rect',{ 43 | x: tickSize * i + (tickSize / 4) + scale.paddingLeft, 44 | y: yAxis, 45 | width: tickSize / rows, 46 | height: (data[j].data[i] / scale.maxSet[i] * relativeMax), 47 | fill: data[j].fill || this._randomColor() 48 | })); 49 | yAxis += (data[j].data[i] / scale.maxSet[i] * relativeMax); 50 | } 51 | } else { 52 | // Side by side 53 | var x = tickSize * i + (tickSize / 4) + scale.paddingLeft; 54 | for (var j = 0; j < rows; j++) { 55 | x += tickSize / (rows+1)* j; 56 | var relativeMax = height * data[j].data[i] / scale.max; 57 | paths.push(this.make('rect',{ 58 | x: x, 59 | y: height - relativeMax + scale.paddingTop, 60 | width: tickSize / (rows+1), 61 | height: relativeMax, 62 | fill: data[j].fill || this._randomColor() 63 | })); 64 | } 65 | } 66 | } 67 | return paths; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/components/bubble.api.es6: -------------------------------------------------------------------------------- 1 | // TODO:: Consolidate code 2 | import composer from '../svg/composer'; 3 | import randomColor from '../utils/randomColor'; 4 | import error from '../utils/error'; 5 | 6 | export default { 7 | 8 | // TODO:: Should refer to a function in path to build this 9 | // Describes the xAxis for bubble point graph 10 | describeXAxisForBubbleLine (height, width, chart) { 11 | // Note:: chart.xAxis is the old format 12 | var config = chart.axis || chart.xAxis; 13 | var centerY = height / 2; 14 | 15 | // Self Note:: PaddingLeft / PaddingRight adjustments are taken out 16 | return composer.make('path', { 17 | "stroke-linecap": "round", 18 | "stroke-linejoin": "round", 19 | 'stroke-width': config.strokeWidth || 2, 20 | stroke: config.strokeColor || 'transparent', 21 | d: 'M' + chart.paddingLeft + ' ' + centerY + ' H' + width 22 | }); 23 | }, 24 | 25 | describeBubbleByObject (data, scale) { 26 | var { height, width, heightRatio, widthRatio, len, max, innerPaddingLeft, paddingLeft, innerPaddingTop, paddingTop, minRange } = scale; 27 | var self = this; 28 | var defaultFill = scale.fill || 0; 29 | var defaultStrokeColor = scale.strokeColor || 0; 30 | var defaultStrokeWidth = scale.strokeWidth || 0; 31 | var paths = []; 32 | var refs; 33 | var minRadius = scale.minRadius || 0; 34 | var inverse = scale.hasInverse; 35 | 36 | for (var i = 0; i < len; i++) { 37 | var props = data[i]; 38 | var point = props.data; 39 | 40 | if (scale.hasEvents) { 41 | // c = column for reference 42 | refs = { 43 | c: i 44 | }; 45 | } 46 | 47 | var r = (scale.maxRadius - minRadius) * (point[2]/max[2]); 48 | r = r ? r + minRadius : 0; 49 | 50 | var xAxis = point[0] - minRange.x; 51 | var yAxis = point[1] - minRange.y; 52 | xAxis = xAxis < 0 ? 0 : xAxis; 53 | yAxis = yAxis < 0 ? 0 : yAxis; 54 | paths.push(composer.make('circle', { 55 | cx: (inverse.x ? width - (xAxis * widthRatio) - innerPaddingLeft - paddingLeft : (xAxis * widthRatio) + innerPaddingLeft + paddingLeft), 56 | cy: (inverse.y ? paddingTop + innerPaddingTop + (yAxis * heightRatio) : height - (yAxis * heightRatio) - innerPaddingTop - paddingTop), 57 | r: r, 58 | fill: props.fill || (defaultFill || randomColor()) 59 | }, refs)); 60 | } 61 | return paths; 62 | }, 63 | 64 | describeBubbleLineByObject: function (data, height, width, scale) { 65 | if (!data) return ''; 66 | var { paddingLeft, innerPaddingLeft, autoFit, strokeColors, strokeWidths, fill, tickSize, startTick, minRadius, maxRadius } = scale; 67 | var dataPoints = data.length; 68 | var paths = []; 69 | var defaultStrokeColor = strokeColors || 0; 70 | var defaultStrokeWidth = strokeWidths || 0; 71 | var defaultFill = scale.fill || 0; 72 | var centerY = height / 2; 73 | var refs, cx; 74 | var minRadius = minRadius || 0; 75 | 76 | for (var i = 0; i < data.length; i++) { 77 | var point = data[i]; 78 | 79 | if (scale.hasEvents) { 80 | // c = columns 81 | refs = { 82 | c: i 83 | }; 84 | } 85 | 86 | if (autoFit == false) { 87 | cx = (i * tickSize) + paddingLeft + innerPaddingLeft; 88 | } else { 89 | cx = ((point.date.getTime() - startTick) * tickSize) + paddingLeft + innerPaddingLeft; 90 | } 91 | 92 | var r = ( maxRadius - minRadius ) * point.data / scale.max; 93 | r = r ? r + minRadius : 0; 94 | 95 | paths.push(composer.make('circle', { 96 | cx: cx, 97 | cy: centerY, 98 | r: r, 99 | fill: point.fill || defaultFill, 100 | stroke: point.strokeColor || (defaultStrokeColor || 'transparent'), 101 | 'stroke-width' : point.strokeWidth || (defaultStrokeWidth || 0) 102 | }, refs)); 103 | } 104 | return paths; 105 | }, 106 | 107 | // Extends default ratio w/ auto scaling for Bubble Scatter 108 | getRatioByObject: function (scale) { 109 | var { _data, height, width, len, innerPaddingLeft, innerPaddingTop, innerPaddingRight, innerPaddingBottom, minRadius } = scale; 110 | var data = _data; 111 | // bubble as a scattered graph 112 | 113 | scale.max[2] = (scale.maxRange && scale.maxRange.z ? scale.maxRange.z : scale.max[2]); 114 | scale.max[1] = (scale.maxRange && scale.maxRange.y ? scale.maxRange.y : scale.max[1]); 115 | scale.max[0] = (scale.maxRange && scale.maxRange.x ? scale.maxRange.x : scale.max[0]); 116 | 117 | scale.minRange = scale.minRange || {}; 118 | scale.minRange.z = scale.minRange.z || 0; 119 | scale.minRange.y = scale.minRange.y || 0; 120 | scale.minRange.x = scale.minRange.x || 0; 121 | 122 | var maxRadius = scale.maxRadius = parseInt(scale.maxRadius) || Math.sqrt(width * height / len) / 2; 123 | scale.minRadius = minRadius || 0; 124 | scale.innerPaddingLeft = innerPaddingLeft < maxRadius ? maxRadius : innerPaddingLeft; 125 | scale.innerPaddingRight = innerPaddingRight < maxRadius ? maxRadius : innerPaddingRight; 126 | scale.innerPaddingTop = innerPaddingTop < maxRadius ? maxRadius : innerPaddingTop; 127 | scale.innerPaddingBottom = innerPaddingBottom < maxRadius ? maxRadius : innerPaddingBottom; 128 | scale.widthRatio = (width - scale.innerPaddingLeft - scale.innerPaddingRight) / (scale.max[0] - scale.minRange.x); 129 | scale.heightRatio = (height - scale.innerPaddingTop - scale.innerPaddingBottom) / (scale.max[1] - scale.minRange.y); 130 | }, 131 | 132 | // Extends default ratio w/ auto scaling for Bubble point 133 | getRatioByTimeSeries: function (scale) { 134 | var { autoFit, _data, height, width, len, paddingTop, paddingLeft, paddingRight, paddingBottom, axis } = scale; 135 | var data = _data; 136 | scale.axis = axis || {}; 137 | var maxRadius = scale.maxRadius = parseInt(scale.maxRadius) || maxRadius; 138 | var minRadius = scale.minRadius = scale.minRadius || 0; 139 | var startTick, endTick; 140 | 141 | // Check if the start date is defined, if not defined using first element in array 142 | if (autoFit == false) { 143 | startTick = 0; 144 | endTick = len - 1; 145 | } else { 146 | // Date time not provided, it will error out (#40) 147 | startTick = (scale.startDate || data[0].date).getTime(); 148 | endTick = (scale.endDate || data[len - 1].date).getTime(); 149 | } 150 | 151 | scale.startTick = startTick; 152 | scale.endTick = endTick; 153 | var tickLen = endTick - startTick; 154 | tickLen = (tickLen == 0 ? 1000 : tickLen); 155 | 156 | var potentialPxTickRatio = width / tickLen; 157 | 158 | var firstElementRadius = ( maxRadius - minRadius ) * data[0].data / scale.max; 159 | var lastElementRadius = ( maxRadius - minRadius ) * data[len - 1].data / scale.max; 160 | 161 | firstElementRadius = firstElementRadius ? firstElementRadius + minRadius : 0; 162 | lastElementRadius = lastElementRadius ? lastElementRadius + minRadius : 0; 163 | 164 | var startTickLeftRadius = ((startTick - startTick) * potentialPxTickRatio) - firstElementRadius; 165 | var endTickRightRadius = ((endTick - endTick) * potentialPxTickRatio) + lastElementRadius; 166 | scale.paddingLeft = startTickLeftRadius < 0 ? Math.abs(startTickLeftRadius) : 0; 167 | scale.paddingRight = endTickRightRadius > 0 ? endTickRightRadius : 0; 168 | scale.tickSize = (width - scale.paddingLeft - scale.paddingRight) / (tickLen); 169 | } 170 | }; 171 | -------------------------------------------------------------------------------- /src/components/bubble.point.es6: -------------------------------------------------------------------------------- 1 | // Supports time series & object base 2 | import Default from '../classes/default'; 3 | import api from './bubble.api'; 4 | 5 | export default class BubblePoint extends Default { 6 | 7 | get componentName() { 8 | return 'bubble.point'; 9 | } 10 | 11 | // Start of a life cyle 12 | _startCycle () { 13 | var self = this; 14 | var chart = self.attributes.opts.chart; 15 | var data = self.attributes.data; 16 | var paths = ''; 17 | 18 | // Sort the data by date 19 | if (chart.autoFit != false) { 20 | var ascByDate = function (a,b) { return a.date - b.date;}; 21 | data.sort(ascByDate); 22 | } 23 | 24 | return self._lifeCycleManager(data, chart, function (newScale) { 25 | paths = self._describeBubble(data, chart.height, chart.width, newScale); 26 | paths.unshift(self._describeXAxis(chart.height, chart.width, newScale)); 27 | return paths; 28 | }); 29 | } 30 | 31 | // Extends default ratio w/ auto scaling 32 | _getRatio (...args) { 33 | return api.getRatioByTimeSeries(...args); 34 | } 35 | 36 | // Describes the xAxis for bubble point graph 37 | _describeXAxis (...args) { 38 | return api.describeXAxisForBubbleLine(...args); 39 | } 40 | 41 | // Describes bubble point graph 42 | _describeBubble (...args){ 43 | return api.describeBubbleLineByObject(...args); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/components/bubble.scatter.es6: -------------------------------------------------------------------------------- 1 | import Default from '../classes/default'; 2 | import api from './bubble.api'; 3 | 4 | class BubbleScatter extends Default { 5 | 6 | get componentName() { 7 | return 'bubble.scatter'; 8 | } 9 | 10 | _startCycle () { 11 | var self = this; 12 | var chart = self.attributes.opts.chart; 13 | var data = self.attributes.data; 14 | 15 | return self._lifeCycleManager(data, chart, function (newScale) { 16 | return self._describeBubbleChart(data, newScale); 17 | }); 18 | } 19 | 20 | // Describes bubble scattered graph 21 | // Extends default ratio w/ auto scaling 22 | _getRatio (...args) { 23 | return api.getRatioByObject(...args); 24 | } 25 | 26 | _describeBubbleChart (...args) { 27 | return api.describeBubbleByObject(...args); 28 | } 29 | } 30 | 31 | export default BubbleScatter; 32 | -------------------------------------------------------------------------------- /src/components/donut.es6: -------------------------------------------------------------------------------- 1 | import Arc from '../classes/arc'; 2 | 3 | export default class Donut extends Arc { 4 | 5 | get componentName() { 6 | return 'donut'; 7 | } 8 | 9 | /** 10 | * [_describePath genereates the paths for each pie segment] 11 | * @param {[int]} radius [circumfrance] 12 | * @param {[array]} data [data set] 13 | * @param {[json]} chart [user specified chart options] 14 | * @return {[string]} [the html string for the pie] 15 | */ 16 | _describePath (radius, data, chart) { 17 | if (!data) return ''; 18 | var paths = []; 19 | var outerRadius = chart.outerRadius || radius; 20 | var innerRadius = chart.innerRadius || (outerRadius / 2); 21 | var startAngle = 0; 22 | var fills = chart.fills || 0; 23 | var strokes = chart.strokeColors || 0; 24 | var centerY = chart.height / 2; 25 | var centerX = chart.width / 2; 26 | var self = this; 27 | 28 | if (chart.total == 0) { 29 | return self.make('path', { 30 | "stroke-linecap": "round", 31 | "stroke-linejoin": "round", 32 | stroke: strokes[i] || (chart.strokeColor||self._randomColor()), 33 | fill: 'transparent', 34 | d: self._describeDonutRing(centerX, centerY, innerRadius, outerRadius) 35 | }); 36 | } 37 | 38 | for (var i = 0; i < data.length; i++) { 39 | var endAngle = startAngle + 360 * data[i]; 40 | paths.push(self.make('path', { 41 | "stroke-linecap": "round", 42 | "stroke-linejoin": "round", 43 | stroke: strokes[i] || (chart.strokeColor||self._randomColor()), 44 | fill: fills[i] || self._randomColor(), 45 | d: self._describeDonut(centerX, centerY, outerRadius, innerRadius, startAngle, endAngle) 46 | })); 47 | startAngle = endAngle; 48 | } 49 | 50 | return paths; 51 | } 52 | 53 | /** 54 | * [_describeDonutRing describes donut ring path] 55 | * @param {Number} x [x cordinates] 56 | * @param {Number} y [y cordinates] 57 | * @param {Number} R [outer radius] 58 | * @param {Number} r [inner radius] 59 | */ 60 | _describeDonutRing (x, y, r, R) { 61 | var y1 = y+R; 62 | var y2 = y+r; 63 | var path = 'M'+x+' '+y1+ 'A'+R+' '+R+' 0 1 1 '+(x+0.001)+' '+y1; // Outer circle 64 | path += 'M'+x+' '+y2+ 'A'+r+' '+r+' 0 1 0 '+(x-0.001)+' '+y2; // Inner Circle 65 | return path; 66 | } 67 | 68 | /** 69 | * [_describeDonut describes donut path] 70 | * @param {Number} x [x cordinates] 71 | * @param {Number} y [y cordinates] 72 | * @param {Number} outerRadius [description] 73 | * @param {Number} innerRadius [description] 74 | * @param {Number} startAngle [description] 75 | * @param {Number} endAngle [description] 76 | * @return {String} [return path attribute 'd' for donut shape] 77 | */ 78 | _describeDonut (x, y, outerRadius, innerRadius, startAngle, endAngle) { 79 | // A temporary fix for working with a stroke that is 360 80 | if (startAngle == 0 && endAngle == 360) { 81 | startAngle = 1; 82 | }; 83 | 84 | var outerArc = { 85 | start: this._polarToCartesian(x, y, outerRadius, endAngle), 86 | end : this._polarToCartesian(x, y, outerRadius, startAngle) 87 | }; 88 | var innerArc = { 89 | start: this._polarToCartesian(x, y, innerRadius, endAngle), 90 | end : this._polarToCartesian(x, y, innerRadius, startAngle) 91 | }; 92 | var arcSweep = endAngle - startAngle <= 180 ? "0": "1"; 93 | 94 | return [ 95 | 'M', outerArc.start.x, outerArc.start.y, 96 | 'A', outerRadius, outerRadius, 0, arcSweep, 0, outerArc.end.x, outerArc.end.y, 97 | 'L', innerArc.end.x, innerArc.end.y, 98 | 'A', innerRadius, innerRadius, 0, arcSweep, 1, innerArc.start.x, innerArc.start.y, 99 | 'L', outerArc.start.x, outerArc.start.y, 100 | 'Z' 101 | ].join(" "); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/components/line.api.js: -------------------------------------------------------------------------------- 1 | // Describes scattered graph 2 | var api = module.exports = { 3 | describeScatter: function(data, numArr, paddingLeft, paddingTop, scale, ref) { 4 | var height = scale.height; 5 | var heightRatio = scale.heightRatio; 6 | var self = this; 7 | var tickSize = scale.tickSize; 8 | var scattered = data.scattered || 0; 9 | var strokeWidth = scattered.strokeWidth || 2; 10 | var strokeColor = scattered.strokeColor || self._randomColor(); 11 | var radius = scattered.radius || 2; 12 | var fill = scattered.fill || 'white'; 13 | var paths = []; 14 | ref = ref || 0; 15 | 16 | for (var i = 0; i < numArr.length; i++) { 17 | paths.push(self.make('circle', { 18 | cx: i === 0 ? (i + scale.innerPadding + paddingLeft) : ((tickSize * i) + paddingLeft), 19 | cy: (height - (numArr[i] * heightRatio) - paddingTop - scale.innerPaddingTop), 20 | r: radius, 21 | stroke: strokeColor, 22 | 'stroke-width': strokeWidth, 23 | fill: fill 24 | }, { 25 | _ref : ref 26 | })); 27 | } 28 | return paths; 29 | } 30 | } -------------------------------------------------------------------------------- /src/components/line.es6: -------------------------------------------------------------------------------- 1 | import Default from '../classes/default'; 2 | import Errors from '../utils/error'; 3 | import * as _ from '../utils/utility'; 4 | import svgPath from '../svg/path'; 5 | import api from './line.api'; 6 | 7 | import Label from '../addons/Label'; 8 | 9 | class Line extends Default { 10 | constructor (...args) { 11 | if (!_.isEmpty(args)) { 12 | var mixinObj = Object.assign(Label, args[0].mixin); 13 | args[0].mixin = mixinObj; 14 | super(...args); 15 | } else { 16 | super({ 17 | mixin: Label 18 | }); 19 | } 20 | } 21 | 22 | get componentName () { 23 | return 'line'; 24 | } 25 | 26 | /** 27 | * The parent generator that manages the svg generation 28 | * @return {object} global function object 29 | */ 30 | _startCycle () { 31 | var data = this.attributes.data; 32 | var opts = this.attributes.opts; 33 | var chart = opts.chart; 34 | var xAxis = chart.xAxis || opts.xAxis; 35 | var yAxis = chart.yAxis || opts.yAxis; 36 | var append = this._append; 37 | var svg; 38 | var paths = []; 39 | 40 | if (!this._isArray(data)) { 41 | data = [data]; 42 | } 43 | 44 | if (xAxis) { 45 | chart.xAxis = xAxis; 46 | } 47 | 48 | if (yAxis) { 49 | chart.yAxis = yAxis; 50 | } 51 | 52 | return this._lifeCycleManager(data, chart, function (scale) { 53 | for (var x = 0; x < scale.rows; x++) { 54 | if (yAxis && yAxis.multi) { 55 | scale.heightRatio = scale.pHeight / scale.max[x]; 56 | } 57 | var g = this.make('g'); 58 | // pass in a ref for event look up, here `ref` is x 59 | paths.push( 60 | append(g, this._describePath(data[x], scale.paddingLeft, scale.paddingTop, scale, x)) 61 | ); 62 | } 63 | return paths; 64 | }.bind(this)); 65 | } 66 | 67 | // Extends default getRatio in lib/base/common.js 68 | _getRatio (scale) { 69 | var self = this; 70 | var data = self.attributes.data; 71 | 72 | // Check if need inner padding 73 | if (scale.paddingLeft !== 0 && scale.paddingRight !== 0) { 74 | scale.innerPadding = 5; 75 | } 76 | if (!scale.xAxis && !scale.yAxis) { 77 | 78 | for (var i = 0; i < scale.len; i++) { 79 | // Find adjustments for inner left / right padding 80 | var o = data[i]; 81 | var padding = 0; 82 | 83 | if (typeof o == 'object') { 84 | var strokeWidth = o.strokeWidth || 2; 85 | scale.innerPaddingBottom = scale.innerPaddingTop < strokeWidth ? strokeWidth : scale.innerPaddingTop; 86 | } 87 | if (typeof o == 'object' && o.scattered && scale.scattered) { 88 | var p = o.scattered; 89 | padding = (p.strokeWidth ? p.strokeWidth : 2) + (p.radius ? p.radius : 2); 90 | scale.innerPadding = scale.innerPadding < padding + 5 ? padding + 5 : scale.innerPadding; 91 | scale.innerPaddingBottom = scale.innerPadding > scale.innerPaddingBottom ? scale.innerPadding : scale.innerPaddingBottom; 92 | scale.innerPaddingTop = scale.innerPaddingBottom; 93 | } 94 | } 95 | } 96 | 97 | scale.pHeight = scale.height - scale.paddingTop - scale.paddingBottom - scale.innerPaddingTop - scale.innerPaddingBottom; 98 | scale.pWidth = scale.width - scale.paddingLeft - scale.paddingRight - scale.innerPadding; 99 | scale.heightRatio = scale.max ? scale.pHeight / scale.max : scale.pHeight; 100 | scale.tickSize = self._sigFigs((scale.pWidth / (scale.len - 1)), 8); 101 | } 102 | 103 | // Describes scattered graph 104 | _describeScatteredGraph () { 105 | return api.describeScatter.apply(this, arguments); 106 | } 107 | 108 | // Svg path builder 109 | _describePath (data, paddingLeft, paddingTop, scale, ref) { 110 | ref = ref || 0; 111 | var self = this; 112 | var pathToken = svgPath.describeAttributeD(data.data, paddingLeft, paddingTop, scale, ref); 113 | var pathNode = self.make('path',{ 114 | d: pathToken, 115 | stroke: data.strokeColor || self._randomColor(), 116 | 'stroke-width': data.strokeWidth || '3', 117 | 'stroke-linejoin': 'round', 118 | 'stroke-linecap': 'round', 119 | fill: 'none' 120 | },{ 121 | _ref: ref 122 | }); 123 | var paths = []; 124 | 125 | if (data.fill) { 126 | paths.push(self.make('path', { 127 | d: pathToken + svgPath.describeCloseAttributeD(data.data, paddingLeft, paddingTop, scale, ref), 128 | stroke: 'none', 129 | 'stroke-width': '2', 130 | 'stroke-linejoin': 'round', 131 | 'stroke-linecap': 'round', 132 | fill: data.fill, 133 | },{ 134 | _ref: ref 135 | })); 136 | } 137 | 138 | paths.push(pathNode); 139 | 140 | if (data.scattered) { 141 | paths.push(self._describeScatteredGraph(data, data.data, paddingLeft, paddingTop, scale, ref)); 142 | } 143 | 144 | return paths; 145 | } 146 | } 147 | 148 | export default Line; 149 | -------------------------------------------------------------------------------- /src/components/pie.es6: -------------------------------------------------------------------------------- 1 | import Arc from '../classes/arc'; 2 | export default class Pie extends Arc { 3 | 4 | get componentName() { 5 | return 'pie'; 6 | } 7 | 8 | /** 9 | * [_describePath genereates the paths for each pie segment] 10 | * @param {[int]} radius [circumfrance] 11 | * @param {[array]} data [data set] 12 | * @param {[json]} chart [user specified chart options] 13 | * @return {[string]} [the html string for the pie] 14 | */ 15 | _describePath (radius, data, chart) { 16 | if (!data) return ''; 17 | var paths = []; 18 | var startAngle = 0; 19 | var fills = chart.fills || 0; 20 | var strokes = chart.strokeColors || 0; 21 | var centerX = chart.width / 2; 22 | var centerY = chart.height / 2; 23 | var self = this; 24 | 25 | if (chart.total == 0) { 26 | return self.make('path', { 27 | "stroke-linecap": "round", 28 | "stroke-linejoin": "round", 29 | stroke: strokes[i] || (chart.strokeColor||self._randomColor()), 30 | fill: 'transparent', 31 | d: self._describeEmptyPie(centerX, centerY, radius) 32 | }); 33 | } 34 | 35 | for (var i = 0; i < data.length; i++) { 36 | var endAngle = startAngle + 360 * data[i]; 37 | paths.push(self.make('path',{ 38 | "stroke-linecap": "round", 39 | "stroke-linejoin": "round", 40 | stroke: strokes[i] || (chart.strokeColor || self._randomColor()), 41 | d: self._describePie(centerX, centerY, radius, startAngle, endAngle), 42 | fill: fills[i] || self._randomColor() 43 | })); 44 | startAngle = endAngle; 45 | } 46 | return paths; 47 | } 48 | 49 | /** 50 | * [_describeEmptyPie describes a full pie using paths] 51 | * @param {Number} x [x cordinates] 52 | * @param {Number} y [y cordinates] 53 | * @param {Number} R [outer radius] 54 | */ 55 | _describeEmptyPie (x, y, R) { 56 | var y1 = y+R; 57 | var y2 = y+r; 58 | var path = 'M'+x+' '+y1+ 'A'+R+' '+R+' 0 1 1 '+(x+0.001)+' '+y1; // Outer circle 59 | return path; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/components/spark.es6: -------------------------------------------------------------------------------- 1 | import Default from '../classes/default'; 2 | import Errors from '../utils/error'; 3 | import svgPath from '../svg/path'; 4 | import api from './line.api'; 5 | 6 | class Spark extends Default { 7 | 8 | get componentName () { 9 | return 'spark'; 10 | } 11 | 12 | /** 13 | * The parent generator that manages the svg generation 14 | * @return {object} global function object 15 | */ 16 | _startCycle () { 17 | var data = this.attributes.data; 18 | var opts = this.attributes.opts; 19 | var chart = opts.chart; 20 | var xAxis = chart.xAxis || opts.xAxis; 21 | var yAxis = chart.yAxis || opts.yAxis; 22 | var append = this._append; 23 | var svg; 24 | var paths = []; 25 | 26 | if (!this._isArray(data)) { 27 | data = [data]; 28 | } 29 | 30 | if (xAxis) { 31 | chart.xAxis = xAxis; 32 | } 33 | 34 | if (yAxis) { 35 | chart.yAxis = yAxis; 36 | } 37 | 38 | 39 | return this._lifeCycleManager(data, chart, function (scale) { 40 | for (var x = 0; x < scale.rows; x++) { 41 | if (yAxis && yAxis.multi) { 42 | scale.heightRatio = scale.pHeight / scale.max[x]; 43 | } 44 | var g = this.make('g'); 45 | // pass in a ref for event look up, here `ref` is x 46 | paths.push( 47 | append(g, this._describePath(data[x], scale.paddingLeft, scale.paddingTop, scale, x)) 48 | ); 49 | } 50 | return paths; 51 | }.bind(this)); 52 | } 53 | 54 | // Extends default getRatio in lib/base/common.js 55 | _getRatio (scale) { 56 | var self = this; 57 | var data = self.attributes.data; 58 | 59 | // Check if need inner padding 60 | if (scale.paddingLeft !== 0 && scale.paddingRight !== 0) { 61 | scale.innerPadding = 5; 62 | } 63 | if (!scale.xAxis && !scale.yAxis) { 64 | 65 | for (var i = 0; i < scale.len; i++) { 66 | // Find adjustments for inner left / right padding 67 | var o = data[i]; 68 | var padding = 0; 69 | 70 | if (typeof o == 'object') { 71 | var strokeWidth = o.strokeWidth || 2; 72 | scale.innerPaddingBottom = scale.innerPaddingTop < strokeWidth ? strokeWidth : scale.innerPaddingTop; 73 | } 74 | if (typeof o == 'object' && o.scattered && scale.scattered) { 75 | var p = o.scattered; 76 | padding = (p.strokeWidth ? p.strokeWidth : 2) + (p.radius ? p.radius : 2); 77 | scale.innerPadding = scale.innerPadding < padding + 5 ? padding + 5 : scale.innerPadding; 78 | scale.innerPaddingBottom = scale.innerPadding > scale.innerPaddingBottom ? scale.innerPadding : scale.innerPaddingBottom; 79 | scale.innerPaddingTop = scale.innerPaddingBottom; 80 | } 81 | } 82 | } 83 | 84 | scale.pHeight = scale.height - scale.paddingTop - scale.paddingBottom - scale.innerPaddingTop - scale.innerPaddingBottom; 85 | scale.pWidth = scale.width - scale.paddingLeft - scale.paddingRight - scale.innerPadding; 86 | scale.heightRatio = scale.max ? scale.pHeight / scale.max : scale.pHeight; 87 | scale.tickSize = self._sigFigs((scale.pWidth / (scale.len - 1)),8); 88 | } 89 | 90 | // Describes scattered graph 91 | _describeScatteredGraph () { 92 | return api.describeScatter.apply(this, arguments); 93 | } 94 | 95 | // Svg path builder 96 | _describePath (data, paddingLeft, paddingTop, scale, ref) { 97 | ref = ref || 0; 98 | var self = this; 99 | var pathToken = svgPath.describeAttributeD(data.data, paddingLeft, paddingTop, scale, ref); 100 | var pathNode = self.make('path',{ 101 | d: pathToken, 102 | stroke: data.strokeColor || self._randomColor(), 103 | 'stroke-width': data.strokeWidth || '3', 104 | 'stroke-linejoin': 'round', 105 | 'stroke-linecap': 'round', 106 | fill: 'none' 107 | },{ 108 | _ref: ref 109 | }); 110 | var paths = []; 111 | 112 | if (data.fill && scale.fill) { 113 | paths.push(self.make('path', { 114 | d: pathToken + svgPath.describeCloseAttributeD(data.data, paddingLeft, paddingTop, scale, ref), 115 | stroke: 'none', 116 | 'stroke-width': '2', 117 | 'stroke-linejoin': 'round', 118 | 'stroke-linecap': 'round', 119 | fill: data.fill, 120 | },{ 121 | _ref: ref 122 | })); 123 | } 124 | 125 | if (scale.line) { 126 | paths.push(pathNode); 127 | } 128 | 129 | if (scale.scattered) { 130 | paths.push(self._describeScatteredGraph(data, data.data, paddingLeft, paddingTop, scale, ref)); 131 | } 132 | 133 | return paths; 134 | } 135 | } 136 | 137 | export default Spark; 138 | -------------------------------------------------------------------------------- /src/index.es6: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 3 | MIT LICENSE 4 | Alfred Kam (@alfredkam) 5 | */ 6 | 7 | import './utils/polyfill'; 8 | import sparkLine from './components/spark'; 9 | import pie from './components/pie'; 10 | import donut from './components/donut'; 11 | import bar from './components/bar'; 12 | import svg from './svg/svg'; 13 | // time series / object base 14 | import bubblePoint from './components/bubble.point'; 15 | import bubbleScatter from './components/bubble.scatter'; 16 | import lineTimeSeries from './components/line.timeSeries'; 17 | import line from './components/line'; 18 | 19 | var initialize = (component, obj) => { 20 | return new component(obj || {}); 21 | }; 22 | 23 | export default { 24 | 25 | name: 'yakojs', 26 | 27 | VERSION: '0.5.5', 28 | 29 | spark (opts) { 30 | return initialize(sparkLine, opts); 31 | }, 32 | 33 | line (opts) { 34 | return initialize(line, opts); 35 | }, 36 | 37 | pie (opts) { 38 | return initialize(pie, opts); 39 | }, 40 | 41 | donut (opts) { 42 | return initialize(donut, opts); 43 | }, 44 | 45 | bar (opts) { 46 | return initialize(bar, opts); 47 | }, 48 | 49 | bubble: { 50 | point (opts) { 51 | return initialize(bubblePoint, opts); 52 | }, 53 | scatter (opts) { 54 | return initialize(bubbleScatter, opts); 55 | } 56 | }, 57 | 58 | timeSeries: { 59 | line (opts) { 60 | return initialize(lineTimeSeries, opts); 61 | } 62 | }, 63 | 64 | svg: svg 65 | }; 66 | -------------------------------------------------------------------------------- /src/svg/arc.js: -------------------------------------------------------------------------------- 1 | var arc = module.exports = { 2 | 3 | // snippet from http://stackoverflow.com/questions/5736398/how-to-calculate-the-svg-path-for-an-arc-of-a-circle 4 | // calculates the polar to cartesian coordinates 5 | polarToCartesian: function (centerX, centerY, radius, angleInDegrees) { 6 | var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0; 7 | 8 | return { 9 | x: centerX + (radius * Math.cos(angleInRadians)), 10 | y: centerY + (radius * Math.sin(angleInRadians)) 11 | }; 12 | }, 13 | 14 | // describes an arc 15 | describeArc: function (centerX, centerY, radius, startAngle, endAngle){ 16 | if (startAngle == 0 && endAngle == 360) { 17 | // Alt solution http://stackoverflow.com/questions/5737975/circle-drawing-with-svgs-arc-path/10477334#10477334 18 | // return [ 19 | // "M", radius * 2, radius, 20 | // "a", radius, radius, 0, 1, 0, radius*2, 0, 21 | // "a", radius, radius, 0, 1, 0, -radius * 2, 0 22 | // ].join(" "); 23 | startAngle = 1; 24 | } 25 | var start = arc.polarToCartesian(centerX, centerY, radius, endAngle); 26 | var end = arc.polarToCartesian(centerX, centerY, radius, startAngle); 27 | var arcSweep = endAngle - startAngle <= 180 ? "0" : "1"; 28 | 29 | return [ 30 | "M", start.x, start.y, 31 | "A", radius, radius, 0, arcSweep, 0, end.x, end.y 32 | ].join(" "); 33 | }, 34 | 35 | describePie: function (centerX, centerY, radius, startAngle, endAngle) { 36 | return arc.describeArc(centerX, centerY, radius, startAngle, endAngle) + ' L' + centerX + ' ' + centerY; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /src/svg/composer.es6: -------------------------------------------------------------------------------- 1 | var isArray = function (obj) { 2 | return obj instanceof Array; 3 | }; 4 | 5 | var composer = { 6 | 7 | makePairs (prefix, json) { 8 | if (!prefix) return ''; 9 | 10 | if (arguments.length < 2) { 11 | json = prefix; 12 | prefix = ''; 13 | } else { 14 | prefix += '-'; 15 | } 16 | 17 | if (!json) return ''; 18 | 19 | var keys = Object.keys(json), len = keys.length; 20 | var str = ''; 21 | while (len--) { 22 | str += ' ' + prefix + keys[len] + '="' + json[keys[len]] + '"'; 23 | } 24 | return str; 25 | }, 26 | 27 | append (parent, childs) { 28 | if (parent === '') return childs; 29 | if (!isArray(childs)) { 30 | childs = [childs]; 31 | } 32 | return parent.replace(/(.*)(<\/.*>$)/g, function (match, p1, p2) { 33 | return p1 + childs.join("") + p2; 34 | }); 35 | }, 36 | 37 | // alternate to one level deep 38 | make (tagName, attribute, dataAttribute, content) { 39 | var el = '<' + tagName; 40 | 41 | if (tagName === 'svg') { 42 | el += ' version="1.1" xmlns="http://www.w3.org/2000/svg"'; 43 | } 44 | el += composer.makePairs(attribute); 45 | el += composer.makePairs('data', dataAttribute); 46 | return el += '>' + (content || content === 0 ? content : '') + ''; 47 | } 48 | }; 49 | 50 | export default composer; 51 | -------------------------------------------------------------------------------- /src/svg/draw.es6: -------------------------------------------------------------------------------- 1 | import composer from './composer'; 2 | import extend from '../utils/extend'; 3 | 4 | export default class Draw { 5 | 6 | constructor () { 7 | var self = this; 8 | return this; 9 | } 10 | 11 | getNode (node = null) { 12 | var parent = null; 13 | var self = this; 14 | if (!node) { 15 | node = self; 16 | } 17 | return { node, parent }; 18 | } 19 | 20 | create (svgElement) { 21 | var self = this; 22 | self.element = svgElement; 23 | return self; 24 | } 25 | 26 | append (svgElement) { 27 | var self = this; 28 | var { node } = self.getNode(); 29 | node.children = node.children || []; 30 | 31 | if (Array.isArray(svgElement)) { 32 | node.children = node.children.concat(svgElement); 33 | } else if (typeof svgElement == 'string') { 34 | node.children.push(svgElement); 35 | } else { 36 | var svg = new Draw(); 37 | svgElement = svg.create(svgElement); 38 | node.children.push(svgElement); 39 | } 40 | return self; 41 | } 42 | 43 | attr (attrName, property) { 44 | var self = this; 45 | var { node } = self.getNode(); 46 | node.attrs = node.attrs || {}; 47 | 48 | if (typeof attrName == 'object') { 49 | extend(node.attrs, attrName); 50 | } else { 51 | node.attrs[attrName] = property; 52 | } 53 | return self; 54 | } 55 | 56 | forEach (fn) { 57 | var self = this; 58 | var { node } = self.getNode(); 59 | var children = node.children || []; 60 | children.forEach(fn); 61 | return self; 62 | } 63 | 64 | stringify () { 65 | var self = this; 66 | var { node } = self.getNode(); 67 | var childContent = (node.children || []).map(function (svgObj) { 68 | if (typeof svgObj == 'string') { 69 | return svgObj; 70 | } 71 | return svgObj.stringify(); 72 | }); 73 | 74 | return composer.make(node.element, node.attrs, {}, childContent.join("")); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/svg/line.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfredkam/yakojs/c15d29a2bb4d6c3f6ff9a731cb75f37889444826/src/svg/line.js -------------------------------------------------------------------------------- /src/svg/path.js: -------------------------------------------------------------------------------- 1 | // TODO:: shrink the argument 2 | 3 | var api = require('../components/api'); 4 | 5 | var path = module.exports = { 6 | /** 7 | * scale describe the min max 8 | * @param attr: { 9 | * data : an N * M array, 10 | * height: chart height, 11 | * width: chart width 12 | * } 13 | * @return obj min / max 14 | */ 15 | // getLinearScale? 16 | getScale: function (attr) { 17 | var data = attr.data || 0; 18 | var scale = api.scale(data); 19 | scale.paddingY = attr.paddingY || 5; 20 | scale.tickSize = api.sigFigs((attr.width / (scale.len - 1)),8); 21 | scale.heightRatio = (attr.height - (scale.paddingY * 2)) / scale.max; 22 | scale.height = attr.height; 23 | scale.width = attr.width; 24 | scale.innerPadding = attr.innerPadding || 0; 25 | scale.innerPaddingTop = attr.innerPaddingTop || 0; 26 | scale.innerPaddingBottom = attr.innerPaddingBottom || 0; 27 | return scale; 28 | }, 29 | 30 | // Describes an open path 31 | describeAttributeD: function (numArr, paddingLeft, paddingTop, scale, ref) { 32 | var height = scale.height; 33 | var heightRatio = scale.heightRatio; 34 | var tickSize = scale.tickSize; 35 | var hasInverse = scale.hasInverse || {}; 36 | var pathToken = ''; 37 | 38 | if (hasInverse.y) { 39 | if (typeof scale.max == 'object') { 40 | max = scale.max[ref]; 41 | } else { 42 | max = scale.max; 43 | } 44 | } 45 | 46 | // Path generator 47 | for (var i = 0; i < numArr.length; i++) { 48 | var pointY = (hasInverse.y ? height - ((max - numArr[i]) * heightRatio) : (height - (numArr[i] * heightRatio))) - paddingTop - scale.innerPaddingTop; 49 | if (i === 0) { 50 | // X Y 51 | pathToken += 'M ' + (paddingLeft + scale.innerPadding) + ' ' + pointY; 52 | } else { 53 | pathToken += ' L '+ ((tickSize * i) + paddingLeft) + ' ' + pointY; 54 | } 55 | } 56 | 57 | // Eliminates the error calls when attributiting this to the svg path 58 | if (pathToken === '') { 59 | pathToken = 'M 0 0'; 60 | } 61 | 62 | return pathToken; 63 | }, 64 | 65 | // Describes the path to close the open path 66 | describeCloseAttributeD: function (numArr, paddingLeft, paddingTop, scale, ref) { 67 | var height = scale.height; 68 | var heightRatio = scale.heightRatio; 69 | return [ 70 | 'V',(height - paddingTop), 71 | 'H', paddingLeft, 72 | 'L', paddingLeft + scale.innerPadding, 73 | (height - (numArr[0] * heightRatio) - paddingTop - scale.innerPaddingTop) 74 | ].join(" "); 75 | }, 76 | 77 | /** 78 | * getOpenPath describes the open path with the given set 79 | * @param {[obj]} scale contains min, max, interval, heightRatio, height, width 80 | * @param {[array]} numberArray an array of numbers 81 | * @return {[string]} string that descibes attributeD 82 | */ 83 | getOpenPath: function (scale, numberArray) { 84 | return path.describeAttributeD(numberArray, 0, scale.paddingY, scale); 85 | }, 86 | 87 | /** 88 | * getClosedPath describes the closed path with the given set 89 | * @param {[obj]} scale contains min, max, interval, heightRatio, height, width 90 | * @param {[array]} numberArray an array of numbers 91 | * @return {[string]} string that descibes attributeD 92 | */ 93 | getClosedPath: function(scale, numberArray) { 94 | return path.describeAttributeD(numberArray, 0, scale.paddingY, scale) + 95 | path.describeCloseAttributeD(numberArray, 0, scale.paddingY, scale); 96 | }, 97 | 98 | beginPath: function () { 99 | var self = this; 100 | var d = ""; 101 | self.moveTo = function (x, y) { 102 | d += "M" + x + " " + y; 103 | return self; 104 | }; 105 | 106 | self.lineTo = function (x, y) { 107 | d += "L" + x + " " + y; 108 | return self; 109 | }; 110 | 111 | self.endPath = function () { 112 | return d; 113 | }; 114 | return self; 115 | } 116 | }; 117 | -------------------------------------------------------------------------------- /src/svg/rect.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfredkam/yakojs/c15d29a2bb4d6c3f6ff9a731cb75f37889444826/src/svg/rect.js -------------------------------------------------------------------------------- /src/svg/svg.es6: -------------------------------------------------------------------------------- 1 | import path from './path'; 2 | import arc from './arc'; 3 | import rect from './rect'; 4 | import composer from './composer'; 5 | import Draw from './draw'; 6 | 7 | module.exports = { 8 | 9 | path: path, 10 | 11 | arc: arc, 12 | 13 | rect: rect, 14 | 15 | composer: composer, 16 | 17 | create (svgElement) { 18 | var instance = new Draw(); 19 | return instance.create(svgElement); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/utils/adjustDecimal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Code from MDN 3 | * Decimal adjustment of a number. 4 | * 5 | * @param {String} type The type of adjustment. 6 | * @param {Number} value The number. 7 | * @param {Integer} exp The exponent (the 10 logarithm of the adjustment base). 8 | * @returns {Number} The adjusted value. 9 | */ 10 | function decimalAdjust(type, value, exp) { 11 | // If the exp is undefined or zero... 12 | if (typeof exp === 'undefined' || +exp === 0) { 13 | return Math[type](value); 14 | } 15 | value = +value; 16 | exp = +exp; 17 | // If the value is not a number or the exp is not an integer... 18 | if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) { 19 | return NaN; 20 | } 21 | // Shift 22 | value = value.toString().split('e'); 23 | value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp))); 24 | // Shift back 25 | value = value.toString().split('e'); 26 | return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)); 27 | } 28 | // Decimal round 29 | if (!Math.round10) { 30 | Math.round10 = function(value, exp) { 31 | return decimalAdjust('round', value, exp); 32 | }; 33 | } 34 | // Decimal floor 35 | if (!Math.floor10) { 36 | Math.floor10 = function(value, exp) { 37 | return decimalAdjust('floor', value, exp); 38 | }; 39 | } 40 | // Decimal ceil 41 | if (!Math.ceil10) { 42 | Math.ceil10 = function(value, exp) { 43 | return decimalAdjust('ceil', value, exp); 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/utils/error.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | var warn = function (msg) { 3 | console.warn(msg); 4 | }; 5 | 6 | /* istanbul ignore next */ 7 | module.exports = { 8 | 9 | label: function () { 10 | warn("You're attempting to use labels without the `Label` addons. Check documentation https://github.com/alfredkam/yakojs/blob/master/doc.md"); 11 | }, 12 | 13 | eventFeedback: function (componentName) { 14 | warn("No event feedback associated with chart component " + componentName); 15 | }, 16 | 17 | insufficientRange: function (componentName) { 18 | warn("A 'maxRange: [x, y, z]' must be passed in for " + componentName); 19 | }, 20 | 21 | missingDate: function (componentName) { 22 | warn("Missing date object in data for " + componentName); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/utils/extend.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Extend property 3 | */ 4 | var isArray = function (obj) { 5 | return obj instanceof Array; 6 | }; 7 | var extend = module.exports = function (props) { 8 | var self = this; 9 | if (arguments.length > 1) { 10 | self = arguments[0]; 11 | props = arguments[1]; 12 | } 13 | var keys = Object.keys(props); 14 | for (var i = 0;i < keys.length; i++) { 15 | self[keys[i]] = props[keys[i]]; 16 | } 17 | return self; 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /src/utils/polyfill.es6: -------------------------------------------------------------------------------- 1 | if (!Object.assign) { 2 | Object.defineProperty(Object, 'assign', { 3 | enumerable: false, 4 | configurable: true, 5 | writable: true, 6 | value: function(target) { 7 | 'use strict'; 8 | if (target === undefined || target === null) { 9 | throw new TypeError('Cannot convert first argument to object'); 10 | } 11 | 12 | var to = Object(target); 13 | for (var i = 1; i < arguments.length; i++) { 14 | var nextSource = arguments[i]; 15 | if (nextSource === undefined || nextSource === null) { 16 | continue; 17 | } 18 | nextSource = Object(nextSource); 19 | 20 | var keysArray = Object.keys(Object(nextSource)); 21 | for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { 22 | var nextKey = keysArray[nextIndex]; 23 | var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); 24 | if (desc !== undefined && desc.enumerable) { 25 | to[nextKey] = nextSource[nextKey]; 26 | } 27 | } 28 | } 29 | return to; 30 | } 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/randomColor.es6: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return '#'+Math.floor(Math.random()*16777215).toString(16); 3 | }; -------------------------------------------------------------------------------- /src/utils/sort.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ascByData: function (a,b) { return a.date - b.date;} 3 | } -------------------------------------------------------------------------------- /src/utils/utility.es6: -------------------------------------------------------------------------------- 1 | /* stripped down lodash */ 2 | var arrayProto = Array.prototype, 3 | errorProto = Error.prototype, 4 | objectProto = Object.prototype, 5 | stringProto = String.prototype; 6 | var getLength = baseProperty('length'); 7 | var stringTag = '[object String]'; 8 | var objToString = objectProto.toString; 9 | var splice = arrayProto.splice; 10 | var MAX_SAFE_INTEGER = 9007199254740991; 11 | 12 | var support = {}; 13 | 14 | (function(x) { 15 | var Ctor = function() { this.x = x; }, 16 | object = { '0': x, 'length': x }, 17 | props = []; 18 | 19 | Ctor.prototype = { 'valueOf': x, 'y': x }; 20 | for (var key in new Ctor) { props.push(key); } 21 | 22 | /** 23 | * Detect if `name` or `message` properties of `Error.prototype` are 24 | * enumerable by default (IE < 9, Safari < 5.1). 25 | * 26 | * @memberOf _.support 27 | * @type boolean 28 | */ 29 | support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || 30 | propertyIsEnumerable.call(errorProto, 'name'); 31 | 32 | /** 33 | * Detect if `prototype` properties are enumerable by default. 34 | * 35 | * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1 36 | * (if the prototype or a property on the prototype has been set) 37 | * incorrectly set the `[[Enumerable]]` value of a function's `prototype` 38 | * property to `true`. 39 | * 40 | * @memberOf _.support 41 | * @type boolean 42 | */ 43 | support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype'); 44 | 45 | /** 46 | * Detect if properties shadowing those on `Object.prototype` are non-enumerable. 47 | * 48 | * In IE < 9 an object's own properties, shadowing non-enumerable ones, 49 | * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug). 50 | * 51 | * @memberOf _.support 52 | * @type boolean 53 | */ 54 | support.nonEnumShadows = !/valueOf/.test(props); 55 | 56 | /** 57 | * Detect if own properties are iterated after inherited properties (IE < 9). 58 | * 59 | * @memberOf _.support 60 | * @type boolean 61 | */ 62 | support.ownLast = props[0] != 'x'; 63 | 64 | /** 65 | * Detect if `Array#shift` and `Array#splice` augment array-like objects 66 | * correctly. 67 | * 68 | * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array 69 | * `shift()` and `splice()` functions that fail to remove the last element, 70 | * `value[0]`, of array-like objects even though the "length" property is 71 | * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8, 72 | * while `splice()` is buggy regardless of mode in IE < 9. 73 | * 74 | * @memberOf _.support 75 | * @type boolean 76 | */ 77 | support.spliceObjects = (splice.call(object, 0, 1), !object[0]); 78 | 79 | /** 80 | * Detect lack of support for accessing string characters by index. 81 | * 82 | * IE < 8 can't access characters by index. IE 8 can only access characters 83 | * by index on string literals, not string objects. 84 | * 85 | * @memberOf _.support 86 | * @type boolean 87 | */ 88 | support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx'; 89 | }(1, 0)); 90 | 91 | export function toObject(value) { 92 | if (support.unindexedChars && isString(value)) { 93 | var index = -1, 94 | length = value.length, 95 | result = Object(value); 96 | 97 | while (++index < length) { 98 | result[index] = value.charAt(index); 99 | } 100 | return result; 101 | } 102 | return isObject(value) ? value : Object(value); 103 | } 104 | export function baseProperty(key) { 105 | return function(object) { 106 | return object == null ? undefined : toObject(object)[key]; 107 | }; 108 | } 109 | 110 | export function isString(value) { 111 | return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag); 112 | } 113 | 114 | export function isObjectLike(value) { 115 | return !!value && typeof value == 'object'; 116 | } 117 | 118 | export function isArray (arr) { 119 | return arr instanceof Array; 120 | } 121 | 122 | export function isArguments(value) { 123 | return isObjectLike(value) && isArrayLike(value) && 124 | hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); 125 | } 126 | 127 | export function isArrayLike(value) { 128 | return value != null && isLength(getLength(value)); 129 | } 130 | 131 | export function isLength(value) { 132 | return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; 133 | } 134 | 135 | export function isEmpty(value) { 136 | if (value == null) { 137 | return true; 138 | } 139 | if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) || 140 | (isObjectLike(value) && isFunction(value.splice)))) { 141 | return !value.length; 142 | } 143 | return !keys(value).length; 144 | } 145 | 146 | export function isObject(value) { 147 | // Avoid a V8 JIT bug in Chrome 19-20. 148 | // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. 149 | var type = typeof value; 150 | return !!value && (type == 'object' || type == 'function'); 151 | } 152 | -------------------------------------------------------------------------------- /test/addons/label-test.js: -------------------------------------------------------------------------------- 1 | describe('addons/Label', function () { 2 | var chai = require('chai'); 3 | var expect = chai.expect; 4 | var Label = require('../../src/addons/Label'); 5 | var _ = require('lodash'); 6 | 7 | var label; 8 | var scale; 9 | var log; 10 | 11 | before(function () { 12 | // mocks 13 | var isArray = function (obj) { 14 | return obj instanceof Array; 15 | }; 16 | Label._append = function (parent, childs) { 17 | if (parent === '') return childs; 18 | 19 | if (!isArray(childs)) { 20 | childs = [childs]; 21 | } 22 | return parent.replace(/(.*)(<\/.*>$)/g, function (match, p1, p2) { 23 | return p1 + childs.join("") + p2; 24 | }); 25 | }; 26 | Label.make = function (tagName, attribute, dataAttribute, content) { 27 | var el = '<' + tagName; 28 | 29 | if (tagName === 'svg') { 30 | el += ' version="1.1" xmlns="http://www.w3.org/2000/svg"'; 31 | } 32 | el += this._makePairs(attribute); 33 | el += this._makePairs('data', dataAttribute); 34 | return el += '>' + (content || content === 0 ? content : '') + ''; 35 | }; 36 | Label._makePairs = function (prefix, json) { 37 | if (arguments.length < 2) { 38 | json = prefix; 39 | prefix = ''; 40 | } else { 41 | prefix += '-'; 42 | } 43 | 44 | if (!json) return ''; 45 | 46 | var keys = Object.keys(json), len = keys.length; 47 | var str = ''; 48 | while (len--) { 49 | str += ' ' + prefix + keys[len] + '="' + json[keys[len]] + '"'; 50 | } 51 | return str; 52 | }; 53 | 54 | label = Label; 55 | scale = { 56 | min: { '0': 30, '1': 24 }, 57 | max: { '0': 300, '1': 600 }, 58 | maxSet: [], 59 | len: 10, 60 | rows: 2, 61 | ySecs: { 0: 3, 1: 3 }, 62 | color: [ 'red', 'blue' ], 63 | scattered: false, 64 | line: true, 65 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 66 | height: 500, 67 | width: 1200, 68 | pHeight: 460, 69 | paddingY: 20, 70 | paddingX: 30 71 | }; 72 | log = console.warn; 73 | }); 74 | 75 | after(function () { 76 | console.warn = log; 77 | }); 78 | 79 | it('_getExternalProps check with type - will it complain', function () { 80 | var copy = _.cloneDeep(scale); 81 | copy.type = 'bubble-scattered'; 82 | var msg; 83 | console.warn = function (m) { 84 | msg = m; 85 | }; 86 | var result = label._getExternalProps(copy,{},{}); 87 | expect(msg).to.not.be.empty; 88 | }); 89 | 90 | it('describeYAxis (scale, multi : true ) should return a yaxis border', function () { 91 | var result = label.describeYAxis(scale, {multi: true}); 92 | expect(result) 93 | .to.be.a('array') 94 | .to.have.length(2) 95 | .to.satisfy(function (arr) { 96 | return /text/.test(arr[0]) && /text/.test(arr[1]); 97 | }); 98 | }); 99 | 100 | it('describeYAxis (scale, true) should return a yaxis label set', function () { 101 | scale.ySecs = 3; 102 | scale.max = 300; 103 | var result = label.describeYAxis(scale, true); 104 | expect(result) 105 | .to.be.a('array') 106 | .to.have.length(1) 107 | .to.satisfy(function (arr) { 108 | return /text/.test(arr[0]); 109 | }); 110 | }); 111 | 112 | it('describeXAxis (scale, opts) should return a xaxis label set', function () { 113 | var scale = { min: { '0': 68, '1': 30 }, 114 | max: { '0': 600, '1': 500 }, 115 | maxSet: [], 116 | len: 10, 117 | rows: 2, 118 | ySecs: { '0': 3, '1': 5 }, 119 | color: [ 'red', 'blue' ], 120 | scattered: false, 121 | line: true, 122 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 123 | height: 500, 124 | width: 1200, 125 | pHeight: 460, 126 | paddingY: 20, 127 | paddingX: 30, 128 | heightRatio: NaN, 129 | tickSize: 125.55556 }; 130 | 131 | var opts = { dateTimeLabelFormat: 'MM/DD hh ap', 132 | minUTC: 1378512000000, 133 | interval: '4h', 134 | format: 'dateTime' }; 135 | 136 | var result = label.describeXAxis(scale, opts); 137 | expect(result) 138 | .to.be.a('array') 139 | .to.have.length(1) 140 | .to.satisfy(function (arr) { 141 | return /text/.test(arr[0]); 142 | }); 143 | }); 144 | 145 | it('_utcMultiplier should return utc', function () { 146 | expect(label._utcMultiplier('1s')) 147 | .to.equal(1000); 148 | expect(label._utcMultiplier('1m')) 149 | .to.equal(6e4); 150 | expect(label._utcMultiplier('1h')) 151 | .to.equal(3600000); 152 | expect(label._utcMultiplier('1D')) 153 | .to.equal(86400000); 154 | expect(label._utcMultiplier('1M')) 155 | .to.equal(2592000000); 156 | expect(label._utcMultiplier('1Y')) 157 | .to.equal(31104000000); 158 | }); 159 | 160 | it('_formatTimeStamp should return the desired time format', function () { 161 | var base = 1378512000000; 162 | expect(label._formatTimeStamp('YY/MM', base + 3600000)) 163 | .to.equal('13/9'); 164 | expect(label._formatTimeStamp('YYYY', base + 3600000)) 165 | .to.equal('2013'); 166 | expect(label._formatTimeStamp('mm.ss', base + 3600000)) 167 | .to.equal('00.00'); 168 | }); 169 | }); 170 | -------------------------------------------------------------------------------- /test/classes/arc-test.js: -------------------------------------------------------------------------------- 1 | describe('/src/classes/arc', function () { 2 | var chai = require('chai'); 3 | var expect = chai.expect; 4 | var Arc = require('../../src/classes/arc'); 5 | var arc; 6 | var scale; 7 | 8 | before(function () { 9 | arc = new Arc(); 10 | scale = { 11 | width: 300, 12 | height: 100, 13 | paddingLeft: 100, 14 | paddingRight: 100, 15 | paddingTop: 100, 16 | paddingBottom: 100 17 | }; 18 | }); 19 | 20 | it('_describePath is the super class', function () { 21 | expect(arc._describePath) 22 | .to.satisfy(function (object) { 23 | return !!(object && object.constructor && object.call && object.apply); 24 | }); 25 | }); 26 | 27 | it('should return a string with attr wrapped w/ defined properties', function () { 28 | arc.props.scale = scale; 29 | 30 | var result = arc.attr({ 31 | chart : { 32 | width: 300, 33 | height: 100, 34 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 35 | showPointer: false, 36 | }, 37 | data: [ 100, 45, 500, 195, 371, 121, 274, 275] 38 | }); 39 | 40 | var expected = ''; 41 | 42 | expect(result) 43 | .to.be.a('string') 44 | .to.equal(expected); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/classes/default-test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | var Base = require('../../src/classes/default'); 4 | 5 | describe('src/classes/default', function () { 6 | var base; 7 | after(function () { 8 | base = ''; 9 | }); 10 | 11 | it('should create a element with div class wrapped', function () { 12 | base = new Base('.graph'); 13 | expect(base.element) 14 | .to.be.equal('
'); 15 | }); 16 | 17 | it('should create a element with div id wrapped', function () { 18 | base = new Base('#graph'); 19 | expect(base.element) 20 | .to.be.equal('
'); 21 | }); 22 | 23 | it('should return empty', function () { 24 | base = new Base(); 25 | expect(base.element) 26 | .to.be.equal(''); 27 | }); 28 | 29 | // depreciated test 30 | // it('should return this', function () { 31 | // var obj = { 32 | // style: { 33 | // width: '100%' 34 | // }, 35 | // innerHTML: '' 36 | // }; 37 | // base = new Base(obj); 38 | // expect(base.element) 39 | // .to.deep.equal(obj); 40 | // }); 41 | }); -------------------------------------------------------------------------------- /test/components/bar-test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | var barGraph = require('../../src/components/bar'); 4 | 5 | describe('src/bar', function () { 6 | var bar; 7 | before(function () { 8 | bar = new barGraph(); 9 | }); 10 | 11 | after(function () { 12 | bar = ''; 13 | }); 14 | 15 | it('_describeBar should describe a stack (group by) ', function () { 16 | var data = [ 17 | { data: 18 | [ 23, 19 | 162, 20 | 304, 21 | 135, 22 | 350, 23 | 56, 24 | 387, 25 | 356, 26 | 283, 27 | 14, 28 | 112, 29 | 59, 30 | 505, 31 | 489, 32 | 78, 33 | 194, 34 | 340, 35 | 118, 36 | 305, 37 | 74, 38 | 71, 39 | 316, 40 | 206, 41 | 208, 42 | 143, 43 | 355, 44 | 97, 45 | 238, 46 | 308, 47 | 264 ], 48 | strokeColor: '#5d2b8f', 49 | fill: '#11b694', 50 | scattered: 51 | { strokeColor: '#5d2b8f', 52 | fill: 'white', 53 | strokeWidth: 2, 54 | radius: 3 } }, 55 | { data: 56 | [ 477, 57 | 251, 58 | 149, 59 | 223, 60 | 36, 61 | 262, 62 | 319, 63 | 397, 64 | 353, 65 | 246, 66 | 284, 67 | 152, 68 | 199, 69 | 13, 70 | 475, 71 | 288, 72 | 269, 73 | 13, 74 | 495, 75 | 50, 76 | 506, 77 | 148, 78 | 374, 79 | 40, 80 | 269, 81 | 50, 82 | 334, 83 | 54, 84 | 367, 85 | 227 ], 86 | strokeColor: '#85d8ef', 87 | fill: '#7f6677', 88 | scattered: 89 | { strokeColor: '#85d8ef', 90 | fill: 'white', 91 | strokeWidth: 2, 92 | radius: 3 } } ]; 93 | 94 | var scale = { 95 | min: 124, 96 | max: 800, 97 | maxSet: 98 | [ 500, 99 | 413, 100 | 453, 101 | 358, 102 | 386, 103 | 318, 104 | 706, 105 | 753, 106 | 636, 107 | 260, 108 | 396, 109 | 211, 110 | 704, 111 | 502, 112 | 553, 113 | 482, 114 | 609, 115 | 131, 116 | 800, 117 | 124, 118 | 577, 119 | 464, 120 | 580, 121 | 248, 122 | 412, 123 | 405, 124 | 431, 125 | 292, 126 | 675, 127 | 491 ], 128 | len: 30, 129 | rows: 2, 130 | ySecs: 0, 131 | color: [ '#5d2b8f', '#85d8ef' ], 132 | stack: true, 133 | showPointer: false, 134 | scattered: false, 135 | fill: [], 136 | line: true, 137 | paddingBottom: 0, 138 | paddingTop: 0, 139 | paddingRight: 0, 140 | paddingLeft: 0, 141 | height: 100, 142 | width: 300, 143 | type: 'bar', 144 | _data: data, 145 | heightRatio: 100 }; 146 | 147 | var result = bar._describeBar(data, scale); 148 | expect(result) 149 | .to.be.a('array') 150 | .to.have.length.of.at.least(60) 151 | .to.satisfy(function (result) { 152 | return (//i).test(result.join("")) && !(/NaN/).test(result.join("")); 153 | }); 154 | }); 155 | 156 | it('_describeBar should describe a bar chart (side by side) ', function () { 157 | var data = [ { data: [ 164, 138, 359, 183, 437, 84, 79, 173, 382, 260 ], 158 | strokeColor: 'red', 159 | strokeWidth: 2, 160 | scattered: { strokeColor: 'red', fill: 'white', strokeWidth: 2, radius: 5 } }, 161 | { data: [ 123, 376, 422, 212, 40, 255, 365, 446, 72, 98 ], 162 | strokeColor: 'blue', 163 | strokeWidth: 2, 164 | scattered: { strokeColor: 'blue', fill: 'white', strokeWidth: 2, radius: 5 } } ]; 165 | 166 | var scale = { min: 40, 167 | max: 500, 168 | maxSet: [], 169 | len: 10, 170 | rows: 2, 171 | ySecs: 5, 172 | color: [ 'red', 'blue' ], 173 | showPointer: false, 174 | yAxis: true, 175 | xAxis: 176 | { format: 'dateTime', 177 | interval: '1D', 178 | minUTC: 1378512000000, 179 | dateTimeLabelFormat: 'MM/DD hh ap' }, 180 | scattered: false, 181 | fill: [], 182 | line: true, 183 | paddingBottom: 20, 184 | paddingTop: 20, 185 | paddingRight: 30, 186 | paddingLeft: 30, 187 | height: 100, 188 | width: 600, 189 | type: 'bar', 190 | _data: data, 191 | pHeight: 60, 192 | pWidth: 540, 193 | tickSize: 54, 194 | heightRatio: 99.92 }; 195 | 196 | var result = bar._describeBar(data, scale); 197 | expect(result) 198 | .to.be.a('array') 199 | .to.have.length.of.at.least(20) 200 | .to.satisfy(function (result) { 201 | return (//i).test(result.join("")) && !(/NaN/).test(result.join("")); 202 | }); 203 | 204 | }); 205 | }); -------------------------------------------------------------------------------- /test/components/donut-test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | var donutChart = require('../../src/components/donut'); 4 | 5 | describe('src/donut', function () { 6 | var donut; 7 | before(function () { 8 | donut = new donutChart(); 9 | }); 10 | 11 | after(function () { 12 | donut = ''; 13 | }); 14 | 15 | it('_describePath should return an array of paths & without NaN', function () { 16 | var radius = 50; 17 | var data = [ 18 | 0.04800768122899664, 19 | 0.028924627940470474, 20 | 0.0022803648583773406, 21 | 0.04200672107537206, 22 | 0.05892942870859338, 23 | 0.040206433029284686, 24 | 0.053168506961113776, 25 | 0.04788766202592415, 26 | 0.024963994239078253, 27 | 0.05736917906865099, 28 | 0.04656745079212674, 29 | 0.0142822851656265, 30 | 0.054368698991838695, 31 | 0.0037205952952472396, 32 | 0.03960633701392223, 33 | 0.03084493518963034, 34 | 0.043686989918386945, 35 | 0.007921267402784446, 36 | 0.02556409025444071, 37 | 0.0200432069131061, 38 | 0.011521843494959194, 39 | 0.008281325012001921, 40 | 0.04956793086893903, 41 | 0.04668746999519923, 42 | 0.01668266922707633, 43 | 0.0542486797887662, 44 | 0.015602496399423908, 45 | 0.04404704752760442, 46 | 0.043686989918386945, 47 | 0.019323091694671148 48 | ]; 49 | var chart = { 50 | relativeDataSet: [ 51 | 0.04800768122899664, 52 | 0.028924627940470474, 53 | 0.0022803648583773406, 54 | 0.04200672107537206, 55 | 0.05892942870859338, 56 | 0.040206433029284686, 57 | 0.053168506961113776, 58 | 0.04788766202592415, 59 | 0.024963994239078253, 60 | 0.05736917906865099, 61 | 0.04656745079212674, 62 | 0.0142822851656265, 63 | 0.054368698991838695, 64 | 0.0037205952952472396, 65 | 0.03960633701392223, 66 | 0.03084493518963034, 67 | 0.043686989918386945, 68 | 0.007921267402784446, 69 | 0.02556409025444071, 70 | 0.0200432069131061, 71 | 0.011521843494959194, 72 | 0.008281325012001921, 73 | 0.04956793086893903, 74 | 0.04668746999519923, 75 | 0.01668266922707633, 76 | 0.0542486797887662, 77 | 0.015602496399423908, 78 | 0.04404704752760442, 79 | 0.043686989918386945, 80 | 0.019323091694671148 ], 81 | outerRadius: 50, 82 | innerRadius: 40, 83 | scattered: false, 84 | fill: true, 85 | line: true, 86 | paddingBottom: 0, 87 | paddingTop: 0, 88 | paddingRight: 0, 89 | paddingLeft: 0, 90 | height: 100, 91 | width: 300, 92 | type: 'chart' }; 93 | 94 | var result = donut._describePath(radius, data, chart); 95 | expect(result) 96 | .to.be.a('array') 97 | .to.satisfy(function (result) { 98 | return (//i).test(result.join("")) && !(/NaN/).test(result.join("")); 99 | }); 100 | }); 101 | 102 | it('_describeDonut should describe a segment of donut in string', function () { 103 | var x = 150; 104 | var y = 50; 105 | var outerRadius = 50; 106 | var innerRadius = 40; 107 | var startAngle = 202.42438790206432; 108 | var endAngle = 213.52856457033124; 109 | 110 | var result = donut._describeDonut(x, y, outerRadius, innerRadius, startAngle, endAngle); 111 | expect(result) 112 | .to.be.a('string') 113 | .to.satisfy(function (result) { 114 | return /M 122.38236765104345 91.68052762907242 A 50 50 0 0 0 130.9268062956573 96.21918737836722 L 134.74144503652582 86.97534990269378 A 40 40 0 0 1 127.90589412083477 83.34442210325793 L 122.38236765104345 91.68052762907242 Z/.test(result); 115 | }); 116 | }); 117 | 118 | }); -------------------------------------------------------------------------------- /test/components/pie-test.js: -------------------------------------------------------------------------------- 1 | describe('src/pie', function () { 2 | var chai = require('chai'); 3 | var expect = chai.expect; 4 | var pieChart = require('../../src/components/pie'); 5 | var pie; 6 | before(function () { 7 | pie = new pieChart(); 8 | }); 9 | 10 | after(function () { 11 | pie = ''; 12 | }); 13 | 14 | it('_describePath should return a set of paths & without NaN', function () { 15 | var radius = 50; 16 | var data = [ 0.003744850830108601, 17 | 0.035700911247035325, 18 | 0.04755960554237923, 19 | 0.029584321557857944, 20 | 0.025090500561727624, 21 | 0.033578829109973785, 22 | 0.05529896392460367, 23 | 0.012233179378354763, 24 | 0.027961552864810887, 25 | 0.05392585195356385, 26 | 0.03744850830108601, 27 | 0.019098739233553864, 28 | 0.03532642616402447, 29 | 0.04543752340531769, 30 | 0.05879415803270503, 31 | 0.022718761702658845, 32 | 0.0019972537760579202, 33 | 0.05043065784546249, 34 | 0.017725627262514042, 35 | 0.029334664835850705, 36 | 0.05592310572962177, 37 | 0.05517413556360005, 38 | 0.03482711272000999, 39 | 0.03208088877793035, 40 | 0.04294095618524529, 41 | 0.030832605167894146, 42 | 0.0022469104980651603, 43 | 0.04493820996130321, 44 | 0.045812008488328546, 45 | 0.012233179378354763 ]; 46 | 47 | var chart = { 48 | relativeDataSet: [ 49 | 0.003744850830108601, 50 | 0.035700911247035325, 51 | 0.04755960554237923, 52 | 0.029584321557857944, 53 | 0.025090500561727624, 54 | 0.033578829109973785, 55 | 0.05529896392460367, 56 | 0.012233179378354763, 57 | 0.027961552864810887, 58 | 0.05392585195356385, 59 | 0.03744850830108601, 60 | 0.019098739233553864, 61 | 0.03532642616402447, 62 | 0.04543752340531769, 63 | 0.05879415803270503, 64 | 0.022718761702658845, 65 | 0.0019972537760579202, 66 | 0.05043065784546249, 67 | 0.017725627262514042, 68 | 0.029334664835850705, 69 | 0.05592310572962177, 70 | 0.05517413556360005, 71 | 0.03482711272000999, 72 | 0.03208088877793035, 73 | 0.04294095618524529, 74 | 0.030832605167894146, 75 | 0.0022469104980651603, 76 | 0.04493820996130321, 77 | 0.045812008488328546, 78 | 0.012233179378354763 ], 79 | outerRadius: 50, 80 | scattered: false, 81 | fill: true, 82 | line: true, 83 | paddingBottom: 0, 84 | paddingTop: 0, 85 | paddingRight: 0, 86 | paddingLeft: 0, 87 | height: 100, 88 | width: 300, 89 | type: 'chart' }; 90 | 91 | var result = pie._describePath(radius, data, chart); 92 | expect(result) 93 | .to.be.a('array') 94 | .to.satisfy(function (result) { 95 | return (//i).test(result.join("")) && !(/NaN/).test(result.join("")); 96 | }); 97 | 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /test/components/spark-test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | var SparkLine = require('../../src/components/spark'); 4 | 5 | describe('src/spark', function () { 6 | var spark; 7 | before(function () { 8 | spark = new SparkLine(); 9 | }); 10 | 11 | after(function () { 12 | spark = ''; 13 | }); 14 | 15 | it('_describePath should return a path string & without NaN', function () { 16 | var path = spark._describePath( 17 | { 18 | data: [ 0, 1, 2, 3, 4], 19 | }, 20 | 5, 21 | 5, 22 | { 23 | height: 300, 24 | width: 100, 25 | line: true, 26 | heightRatio: 290 / 4, 27 | tickSize: spark._sigFigs((100 / 4),8), 28 | innerPadding: 0, 29 | innerPaddingTop: 0, 30 | innerPaddingBottom: 0 31 | } 32 | ); 33 | 34 | expect(path) 35 | .to.be.a('array') 36 | .to.satisfy(function (arr) { 37 | return /M 5 295 L 30 222.5 L 55 150 L 80 77.5 L 105 5/i.test(arr.join('')); 38 | }); 39 | }); 40 | 41 | it('calling attr should render w/ default properties should render 1 series', function () { 42 | var set = { data: 43 | [ 100, 45, 500, 264, 380, 126, 186, 291, 69 ], 44 | strokeColor: '#2b26a0', 45 | fill: '#6bd775' }; 46 | 47 | var svg = spark.attr({ 48 | chart : { 49 | type: 'line', 50 | width: 300, 51 | height: 100, 52 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 53 | }, 54 | title: 'just a test', 55 | data: set 56 | }); 57 | 58 | expect(svg) 59 | .that.is.a('string'). 60 | to.satisfy(function (result) { 61 | return //i.test(result) && !//i.test(result); 62 | }); 63 | }); 64 | 65 | it('calling attr should render w/ default properties should render 2 series & without NaN', function () { 66 | var set = [ { data: 67 | [ 100, 45, 500, 264, 380, 126, 186, 291, 69 ], 68 | strokeColor: '#2b26a0', 69 | fill: '#6bd775' }, 70 | { data: 71 | [ 271, 335, 216, 195, 423, 332, 413, 171, 241 ], 72 | strokeColor: '#1dbc53', 73 | fill: '#befb6f' } ]; 74 | 75 | var svg = spark.attr({ 76 | chart : { 77 | type: 'line', 78 | width: 300, 79 | height: 100, 80 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 81 | }, 82 | title: 'just a test', 83 | data: set 84 | }); 85 | 86 | expect(svg) 87 | .that.is.a('string'). 88 | to.satisfy(function (result) { 89 | return //i.test(result) && !(/NaN/).test(result);; 90 | }); 91 | }); 92 | 93 | it('calling attr should render w/ fill default to true should render a closed path', function () { 94 | var set = [ { data: 95 | [ 100, 45, 500, 264, 380, 126, 186, 291, 69 ], 96 | strokeColor: '#2b26a0', 97 | fill: '#6bd775' }, 98 | { data: 99 | [ 271, 335, 216, 195, 423, 332, 413, 171, 241 ], 100 | strokeColor: '#1dbc53', 101 | fill: '#befb6f' } ]; 102 | 103 | var svg = spark.attr({ 104 | chart : { 105 | type: 'line', 106 | width: 300, 107 | height: 100, 108 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 109 | line: false 110 | }, 111 | title: 'just a test', 112 | data: set 113 | }); 114 | 115 | expect(svg) 116 | .that.is.a('string'). 117 | to.satisfy(function (result) { 118 | return //i.test(result); 119 | }); 120 | }); 121 | 122 | it('calling attr should render w/ line false & fill false and scattered true should render a scattered graph', function () { 123 | var set = [ { data: 124 | [ 100, 45, 500, 264, 380, 126, 186, 291, 69 ], 125 | strokeColor: '#2b26a0', 126 | fill: '#6bd775' }, 127 | { data: 128 | [ 271, 335, 216, 195, 423, 332, 413, 171, 241 ], 129 | strokeColor: '#1dbc53', 130 | fill: '#befb6f' } ]; 131 | 132 | var svg = spark.attr({ 133 | chart : { 134 | type: 'line', 135 | width: 300, 136 | height: 100, 137 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 138 | line: false, 139 | fill: false, 140 | scattered: true 141 | }, 142 | title: 'just a test', 143 | data: set 144 | }); 145 | 146 | expect(svg) 147 | .that.is.a('string'). 148 | to.satisfy(function (result) { 149 | return !//i.test(result) && //i.test(result); 150 | }); 151 | }); 152 | 153 | it('calling attr should render w/ scattered true should render a scattered graph w/ area filled ', function () { 154 | var set = [ { data: 155 | [ 100, 45, 500, 264, 380, 126, 186, 291, 69 ], 156 | strokeColor: '#2b26a0', 157 | fill: '#6bd775' }, 158 | { data: 159 | [ 271, 335, 216, 195, 423, 332, 413, 171, 241 ], 160 | strokeColor: '#1dbc53', 161 | fill: '#befb6f' } ]; 162 | 163 | var svg = spark.attr({ 164 | chart : { 165 | type: 'line', 166 | width: 300, 167 | height: 100, 168 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 169 | scattered: true 170 | }, 171 | title: 'just a test', 172 | data: set 173 | }); 174 | 175 | expect(svg) 176 | .that.is.a('string'). 177 | to.satisfy(function (result) { 178 | return //i.test(result) && //i.test(result); 179 | }); 180 | }); 181 | 182 | it('calling attr should render w/ scattered true should render a scattered graph w/ line & no area filled', function () { 183 | var set = [ { data: 184 | [ 100, 45, 500, 264, 380, 126, 186, 291, 69 ], 185 | strokeColor: '#2b26a0'}, 186 | { data: 187 | [ 271, 335, 216, 195, 423, 332, 413, 171, 241 ], 188 | strokeColor: '#1dbc53'} ]; 189 | 190 | var svg = spark.attr({ 191 | chart : { 192 | type: 'line', 193 | width: 300, 194 | height: 100, 195 | 'font-family': '"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif', 196 | scattered: true 197 | }, 198 | title: 'just a test', 199 | data: set 200 | }); 201 | 202 | expect(svg) 203 | .that.is.a('string'). 204 | to.satisfy(function (result) { 205 | return //i.test(result) && 206 | //i.test(result) && 207 | !/V\s.*H\s.*L\s.*>/i.test(result); // for closing graph 208 | }); 209 | }); 210 | }); 211 | -------------------------------------------------------------------------------- /test/svg/arc-test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | var arc = require('../../src/svg/arc'); 4 | 5 | describe('src/svg/arc', function () { 6 | it('polarToCartesian should return proper polar to cartesian coordinates', function () { 7 | var result = arc.polarToCartesian(50, 50, 25, 50); 8 | expect(result) 9 | .to.deep.equal({ 10 | x: 69.15111107797445, 11 | y: 33.93030975783652 12 | }); 13 | }); 14 | 15 | it('describArc should describe the arc\'s path', function () { 16 | // 1-2nd quadrent 17 | var result = arc.describeArc(25, 25, 10, 50, 60); 18 | var expected = 'M 33.66025403784439 20 A 10 10 0 0 0 32.66044443118978 18.572123903134607'; 19 | 20 | expect(result) 21 | .to.be.a('string') 22 | .to.be.equal(expected); 23 | 24 | // 3-4th quadrent 25 | var result = arc.describeArc(25, 25, 10, 170, 320); 26 | var expected = 'M 18.572123903134603 17.33955556881022 A 10 10 0 0 0 26.736481776669304 34.84807753012208'; 27 | 28 | expect(result) 29 | .to.be.a('string') 30 | .to.be.equal(expected); 31 | }); 32 | 33 | it('describePie should describe a piece of a pie', function () { 34 | var result = arc.describePie(25, 25, 10, 50, 60); 35 | var expected = 'M 33.66025403784439 20 A 10 10 0 0 0 32.66044443118978 18.572123903134607 L25 25'; 36 | 37 | expect(result) 38 | .to.be.a('string') 39 | .to.be.equal(expected); 40 | 41 | }); 42 | }); -------------------------------------------------------------------------------- /test/svg/path-test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | var path = require('../../src/svg/path'); 4 | 5 | describe('src/svg/path', function () { 6 | var attr = {}; 7 | before(function () { 8 | attr = { 9 | data: [ 211, 254, 255, 18, 210, 31, 13, 320, 328, 312, 494, 128, 83, 230, 114, 173, 90, 328, 22, 174, 88, 305, 390, 181, 481, 497, 300, 395, 88, 509 ], 10 | height: 100, 11 | width: 300 12 | }; 13 | }); 14 | it('scale calculation & structure', function () { 15 | var scale = path.getScale(attr); 16 | var result = { min: 13, 17 | max: 509, 18 | maxSet: [], 19 | len: 30, 20 | paddingY: 5, 21 | tickSize: 10.344828, 22 | heightRatio: 0.17681728880157171, 23 | height: 100, 24 | width: 300, 25 | color: [], 26 | rows: 1, 27 | ySecs: 0, 28 | innerPadding: 0, 29 | innerPaddingTop: 0, 30 | innerPaddingBottom: 0 31 | }; 32 | expect(scale).to.deep.equal(result); 33 | }); 34 | 35 | it('openPath calculation should return the proper attribute D', function () { 36 | var expectD = "M 0 57.69155206286837 L 10.344828 50.088408644400786 L 20.689656 49.911591355599214 L 31.034484 91.8172888015717 L 41.379312 57.86836935166994 L 51.72414 89.51866404715128 L 62.068968 92.70137524557957 L 72.41379599999999 38.41846758349705 L 82.758624 37.00392927308448 L 93.103452 39.83300589390962 L 103.44828 7.652259332023576 L 113.79310799999999 72.36738703339881 L 124.137936 80.32416502946955 L 134.482764 54.33202357563851 L 144.82759199999998 74.84282907662083 L 155.17242 64.4106090373281 L 165.517248 79.08644400785855 L 175.862076 37.00392927308448 L 186.206904 91.11001964636542 L 196.551732 64.23379174852653 L 206.89656 79.44007858546169 L 217.241388 41.07072691552063 L 227.58621599999998 26.041257367387033 L 237.93104399999999 62.99607072691552 L 248.275872 9.950884086444006 L 258.6207 7.121807465618858 L 268.965528 41.95481335952849 L 279.310356 25.15717092337917 L 289.65518399999996 79.44007858546169 L 300.00001199999997 5"; 37 | var scale = path.getScale(attr); 38 | expect(expectD).to.equal(path.getOpenPath(scale, attr.data)); 39 | }); 40 | 41 | it('closedPath calculation should return the proper attribute D with the path closed', function () { 42 | var expectD = "M 0 57.69155206286837 L 10.344828 50.088408644400786 L 20.689656 49.911591355599214 L 31.034484 91.8172888015717 L 41.379312 57.86836935166994 L 51.72414 89.51866404715128 L 62.068968 92.70137524557957 L 72.41379599999999 38.41846758349705 L 82.758624 37.00392927308448 L 93.103452 39.83300589390962 L 103.44828 7.652259332023576 L 113.79310799999999 72.36738703339881 L 124.137936 80.32416502946955 L 134.482764 54.33202357563851 L 144.82759199999998 74.84282907662083 L 155.17242 64.4106090373281 L 165.517248 79.08644400785855 L 175.862076 37.00392927308448 L 186.206904 91.11001964636542 L 196.551732 64.23379174852653 L 206.89656 79.44007858546169 L 217.241388 41.07072691552063 L 227.58621599999998 26.041257367387033 L 237.93104399999999 62.99607072691552 L 248.275872 9.950884086444006 L 258.6207 7.121807465618858 L 268.965528 41.95481335952849 L 279.310356 25.15717092337917 L 289.65518399999996 79.44007858546169 L 300.00001199999997 5V 95 H 0 L 0 57.69155206286837"; 43 | var scale = path.getScale(attr); 44 | expect(expectD).to.equal(path.getClosedPath(scale, attr.data)); 45 | }); 46 | }); -------------------------------------------------------------------------------- /test/utils/adjustDecimal-test.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var expect = chai.expect; 3 | require('../../src/utils/adjustDecimal'); 4 | 5 | describe('src/utils/math', function () { 6 | it('should be able to round10', function () { 7 | expect(Math.round10(120.002, 2)) 8 | .to.be.equal(100); 9 | expect(Math.round10(180.002, 2)) 10 | .to.be.equal(200); 11 | }); 12 | 13 | it('should be able to floor10', function () { 14 | expect(Math.floor10(120.002, 2)) 15 | .to.be.equal(100); 16 | expect(Math.floor10(120.684, -2)) 17 | .to.be.equal(120.68); 18 | }); 19 | 20 | it('should be able to ceil10', function () { 21 | expect(Math.ceil10(120.002, 2)) 22 | .to.be.equal(200); 23 | }); 24 | 25 | it('should be able to handle no arguments and return NaN', function () { 26 | expect(Math.round10()) 27 | .to.satisfy(function (re) { 28 | if (isNaN(re)) { 29 | return true; 30 | } 31 | return false; 32 | }); 33 | expect(Math.floor10()) 34 | .to.satisfy(function (re) { 35 | if (isNaN(re)) { 36 | return true; 37 | } 38 | return false; 39 | }); 40 | expect(Math.ceil10()) 41 | .to.satisfy(function (re) { 42 | if (isNaN(re)) { 43 | return true; 44 | } 45 | return false; 46 | }); 47 | }); 48 | 49 | it('should be able to handle and return the origin number', function () { 50 | expect(Math.round10(10)) 51 | .to.be.equal(10); 52 | expect(Math.floor10(10)) 53 | .to.be.equal(10); 54 | expect(Math.ceil10(10)) 55 | .to.be.equal(10); 56 | }); 57 | }); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | require('babel-core/register')({ 2 | ignore: /(node_modules|__tests__)/, 3 | optional : [ 4 | 'es7.asyncFunctions', 5 | 'es7.classProperties', 6 | 'es7.comprehensions', 7 | 'es7.decorators', 8 | 'es7.doExpressions', 9 | 'es7.exponentiationOperator', 10 | 'es7.exportExtensions', 11 | 'es7.objectRestSpread', 12 | 'es7.trailingFunctionCommas' 13 | ], 14 | 15 | blacklist: ['strict'], 16 | 17 | extensions: [".es6", ".es", ".jsx", ".es6.js", ".js" ] 18 | }); 19 | 20 | var webpack = require('webpack'); 21 | var path = require('path'); 22 | 23 | module.exports = { 24 | 25 | context : __dirname, 26 | 27 | entry : { 28 | yako : './build-tools/expose.build' 29 | }, 30 | 31 | devtool: 'source-map', 32 | 33 | output : { 34 | path: path.join(__dirname, 'dist'), 35 | filename: '[name].js' 36 | }, 37 | 38 | plugins: [ 39 | new webpack.HotModuleReplacementPlugin(), 40 | new webpack.NoErrorsPlugin() 41 | ], 42 | 43 | module: { 44 | loaders: [ 45 | { 46 | test: /\.jsx$/, 47 | loaders: ['jsx-loader?harmony'] 48 | }, 49 | 50 | { 51 | test: /\.(es6|js)$/, 52 | loader: 'babel-loader?stage=0&blacklist=useStrict' 53 | }, 54 | 55 | // allow less files to load urls pointing to font assets 56 | // @TODO: figure out why this is necessary and do it better 57 | { 58 | test: /\.(woff|ttf|eot|svg)$/, loader: 'file-loader' 59 | } 60 | ] 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/default 2 | deploy: 3 | steps: 4 | - lukevivier/gh-pages@0.2.1: 5 | token: $GIT_TOKEN 6 | domain: alfredkam.com/yakojs 7 | basedir: demo --------------------------------------------------------------------------------