├── test.bar.png ├── test.line.png ├── .gitignore ├── package.json ├── LICENSE ├── examples ├── test.line.js └── test.bar.js ├── README.md └── index.js /test.bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenGrider/node-chartjs/master/test.bar.png -------------------------------------------------------------------------------- /test.line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StephenGrider/node-chartjs/master/test.line.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules 29 | coverage 30 | .nyc_output 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-chartjs", 3 | "version": "0.0.7", 4 | "description": "Chart.js on the server in Node.js", 5 | "engines": { 6 | "node": ">=8.0.0" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/jacktuck/node-chartjs.git" 11 | }, 12 | "keywords": [ 13 | "chart.js", 14 | "charts", 15 | "line", 16 | "bar", 17 | "pie", 18 | "server-side", 19 | "node.js" 20 | ], 21 | "main": "index.js", 22 | "scripts": { 23 | "test": "echo \"Error: no test specified\" && exit 1" 24 | }, 25 | "author": "Jack Tuck", 26 | "license": "MIT", 27 | "dependencies": { 28 | "canvas": "2.0.0-alpha.2", 29 | "jsdom": "^11.6.2" 30 | }, 31 | "devDependencies": { 32 | "chart.js": "2.4.0", 33 | "opn": "^5.2.0" 34 | }, 35 | "peerDependencies": { 36 | "chart.js": "2.4.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jack Tuck 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/test.line.js: -------------------------------------------------------------------------------- 1 | const ChartJs = require('..') 2 | const cjs = new ChartJs(200, 40) 3 | 4 | const opn = require('opn') 5 | const util = require('util') 6 | 7 | const lineConfig = { 8 | type: 'line', 9 | data: { 10 | labels: [1, 2, 3, 4, 5, 6, 7], 11 | datasets: [{ 12 | backgroundColor: 'transparent', 13 | borderColor: 'red', 14 | label: 'Tasks', 15 | data: [4,1,55,77,99,11,99], 16 | fill: false, 17 | pointRadius: 0, 18 | cubicInterpolationMode: 'monotone', 19 | borderCapStyle: 'round' 20 | }] 21 | }, 22 | options: { 23 | plugins: [{ 24 | beforeUpdate: function (chartInstance) { 25 | const ctx = chartInstance.chart.ctx 26 | 27 | const width = chartInstance.chart.width 28 | const height = chartInstance.chart.height 29 | 30 | const dataset = chartInstance.data.datasets[0] 31 | 32 | const gradient = ctx.createLinearGradient(0, height, width, 0) 33 | gradient.addColorStop(0, '#FF7978') 34 | gradient.addColorStop(1, '#FFA278') 35 | dataset.borderColor = gradient 36 | 37 | console.log('dataset', dataset) 38 | } 39 | }], 40 | layout: { 41 | padding: 10, 42 | lineHeight: 1 43 | }, 44 | legend: { 45 | display: false 46 | }, 47 | linearGradientLine: true, 48 | scales: { 49 | yAxes: [{ 50 | display: false, 51 | ticks: { 52 | display: false 53 | } 54 | }], 55 | xAxes: [{ 56 | display: false 57 | }] 58 | } 59 | } 60 | } 61 | 62 | cjs.makeChart(lineConfig) 63 | .then(res => { 64 | cjs.drawChart() 65 | 66 | cjs.toFile('test.line.png') 67 | .then(_ => { 68 | opn('test.line.png') 69 | }) 70 | }) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-chartjs 2 | 3 | Chart.js on the server in Node.js 8.x.x or later 4 | 5 | Based on previous work by https://github.com/vmpowerio/chartjs-node 6 | 7 | *With a few improvements we think:* 8 | 9 | - Uses the newer ~9.x.x~ 11.x.x version of JSDOM 10 | - Does not pollute node's global namespace 11 | 12 | > Note that we strongly advise against trying to "execute scripts" by mashing together the jsdom and Node global environments (e.g. by doing global.window = dom.window), and then executing scripts or test code inside the Node global environment. Instead, you should treat jsdom like you would a browser, and run all scripts and tests that need access to a DOM inside the jsdom environment, using window.eval or runScripts: "dangerously". This might require, for example, creating a browserify bundle to execute as a 30 | ` 31 | 32 | const { window } = new JSDOM(html, { 33 | features: { 34 | FetchExternalResources: ['script'], 35 | ProcessExternalResources: ['script'], 36 | SkipExternalResources: false 37 | }, 38 | runScripts: 'dangerously' 39 | }) 40 | 41 | this.window = window 42 | this.window.CanvasRenderingContext2D = Canvas.Context2d 43 | this.canvas = this.window.document.getElementById('myChart') 44 | this.ctx = this.canvas.getContext('2d') 45 | } 46 | 47 | async makeChart (chartConfig) { 48 | this._chart && this._chart.destroy() 49 | 50 | chartConfig.options = chartConfig.options || {} 51 | chartConfig.options.responsive = false 52 | chartConfig.options.width = 400 53 | chartConfig.options.height = 400 54 | chartConfig.options.animation = false 55 | 56 | this.chartConfig = chartConfig 57 | 58 | return this 59 | } 60 | 61 | drawChart () { 62 | this.emit('beforeDraw', this.window.Chart) 63 | 64 | if (this.chartConfig.options.plugins) { 65 | this.window.Chart.pluginService.register(this.chartConfig.options.plugins) 66 | } 67 | 68 | if (this.chartConfig.options.charts) { 69 | for (const chart of this.chartConfig.options.charts) { 70 | this.window.Chart.defaults[chart.type] = chart.defaults || {} 71 | if (chart.baseType) { 72 | this.window.Chart.controllers[chart.type] = this.window.Chart.controllers[chart.baseType].extend(chart.controller) 73 | } else { 74 | this.window.Chart.controllers[chart.type] = this.window.Chart.DatasetController.extend(chart.controller) 75 | } 76 | } 77 | } 78 | 79 | this._chart = this.window.Chart(this.ctx, this.chartConfig) 80 | 81 | return this 82 | } 83 | 84 | toBlob (mime) { 85 | const toBlobRearg = (mime, cb) => this.canvas.toBlob((blob, err) => cb(err, blob), mime) 86 | 87 | return promisify(toBlobRearg)(mime) 88 | .catch(console.error) 89 | } 90 | 91 | toBuffer (mime = 'image/png') { 92 | return this.toBlob(mime) 93 | .then(blob => new Promise((resolve, reject) => { 94 | const reader = new this.window.FileReader() 95 | 96 | reader.onload = function (){ 97 | const buffer = new Buffer(reader.result) 98 | resolve(buffer) 99 | } 100 | 101 | reader.readAsArrayBuffer(blob) 102 | })) 103 | .catch(console.error) 104 | } 105 | 106 | toFile (path, mime = 'image/png') { 107 | const writeFile = promisify(fs.writeFile) 108 | 109 | return this.toBuffer(mime) 110 | .then(blob => writeFile(path, blob, 'binary')) 111 | .catch(console.error) 112 | } 113 | } 114 | 115 | 116 | module.exports = ChartJs 117 | --------------------------------------------------------------------------------