├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── basic-visualization ├── app.js ├── bower.json ├── index.html └── nyc-temperature.csv ├── capture-visualization ├── index.js ├── output │ └── placeholder.txt ├── package.json ├── public │ ├── app.css │ ├── app.js │ ├── bower.json │ ├── index.html │ └── nyc-temperature.csv ├── toolkit │ └── capture-web-page.js └── web-server.js └── images └── support1.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ashleydavis 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | capture-visualization/output/ 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Data Wrangling with JavaScript 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nodejs-visualization-example 2 | 3 | An example that shows how to use the Nightmare headless browser to capture web-based visualizations under Node.js. 4 | 5 | 6 | 7 | Example code here accompanies [my article on CSS tricks](http://bit.ly/2HW3W0b) which is an extract from my book [Data Wrangling with JavaScript](http://bit.ly/2t2cJu2). 8 | 9 | [You can support my work here](https://www.codecapers.com.au/about#support-my-work) 10 | 11 | ## Examples 12 | 13 | basic-visualization/ 14 | 15 | This sub-directory contains a simple web app swith an example chart using C3. 16 | 17 | capture-visualization/ 18 | 19 | Working Node.js example that uses headless browser Nightmare/Electron to capture a browser-based visualization (chart) to a PNG image file. 20 | 21 | ## Data Wrangling with JavaScript 22 | 23 | These examples are an extract from my book [Data Wrangling with JavaScript](http://bit.ly/2t2cJu2). 24 | -------------------------------------------------------------------------------- /basic-visualization/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // 4 | // Render a chart to a particular element on the page. 5 | // 6 | function renderChart (bindto, data, size) { 7 | var chart = c3.generate({ 8 | bindto: bindto, 9 | size: size, // Set the size of the chart. Not used in this example, but we'll make use of this later. 10 | data: { 11 | json: data, 12 | keys: { 13 | x: "Year", // Specify the CSV file column to use as the X axis. 14 | value: [ 15 | "AvgTemp" 16 | ] 17 | } 18 | }, 19 | transition: { 20 | duration: 0 // Disable animated transitions when we are capturing a static image. 21 | } 22 | }); 23 | }; 24 | 25 | $(function () { 26 | 27 | $.get("nyc-temperature.csv") // Get CSV data file from the web server. 28 | .then(function (response) { 29 | var parseOptions = { 30 | header: true, 31 | dynamicTyping: true 32 | }; 33 | var parsed = Papa.parse(response, parseOptions); // Parse CSV data to JavaScript data. 34 | renderChart("#chart", parsed.data); // Use C3 to render the chart. 35 | }) 36 | .catch(function (err) { 37 | console.error(err); 38 | }); 39 | 40 | }); 41 | 42 | -------------------------------------------------------------------------------- /basic-visualization/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "c3-template-web-page", 3 | "homepage": "https://github.com/JavaScript-Data-Wrangling/Chapter-10", 4 | "authors": [ 5 | "Ashley Davis " 6 | ], 7 | "description": "", 8 | "main": "", 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "jquery": "^3.3.1", 19 | "c3": "^0.4.18", 20 | "papaparse": "^4.3.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /basic-visualization/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NYC average yearly temperature 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /basic-visualization/nyc-temperature.csv: -------------------------------------------------------------------------------- 1 | Year,MinTemp,MaxTemp,AvgTemp 2 | 1917,-25,37.8,10.547245179063365 3 | 1918,-21.1,40,11.825205479452052 4 | 1919,-18.3,37.2,12.125824175824178 5 | 1920,-18.9,34.4,11.286475409836074 6 | 1921,-15.6,35.6,12.751095890410959 7 | 1922,-18.9,34.4,12.001369863013698 8 | 1923,-13.9,37.2,11.665753424657524 9 | 1924,-15,37.2,11.08784153005464 10 | 1925,-18.9,37.2,11.937637362637368 11 | 1926,-15,37.8,10.772865013774107 12 | 1927,-18.3,33.3,11.932465753424664 13 | 1928,-13.9,34.4,11.961157024793385 14 | 1929,-13.3,37.2,12.328082191780814 15 | 1930,-13.9,38.9,12.53301369863014 16 | 1931,-11.7,37.2,13.26561643835617 17 | 1932,-11.7,35.6,12.900961538461535 18 | 1933,-21.1,38.9,12.447099447513821 19 | 1934,-26.1,38.3,11.733150684931507 20 | 1935,-18.3,35,11.739041095890402 21 | 1936,-19.4,41.1,11.940027322404374 22 | 1937,-10,37.8,12.546428571428576 23 | 1938,-14.4,35.6,12.938356164383551 24 | 1939,-14.4,35.6,12.62821917808219 25 | 1940,-13.9,36.7,11.04616438356164 26 | 1941,-12.2,36.7,12.78082191780821 27 | 1942,-20,36.1,12.310714285714294 28 | 1943,-22.2,37.2,12.117671232876717 29 | 1944,-11.1,38.9,12.637534246575356 30 | 1945,-16.7,36.1,12.307808219178066 31 | 1946,-15,34.4,13.040273972602742 32 | 1947,-13.9,35,12.159041095890403 33 | 1948,-17.8,39.4,12.316986301369864 34 | 1949,-8.3,38.9,13.88131868131868 35 | 1950,-14.4,35,12.076373626373627 36 | 1951,-13.3,34.4,12.815479452054786 37 | 1952,-13.3,37.8,13.236202185792356 38 | 1953,-10,38.9,13.947796143250693 39 | 1954,-13.9,37.8,12.737671232876705 40 | 1955,-17.8,37.8,12.68255494505495 41 | 1956,-10,37.2,11.957240437158474 42 | 1957,-17.8,38.3,13.081318681318683 43 | 1958,-16.1,33.9,11.46863013698631 44 | 1959,-13.9,36.1,13.060273972602742 45 | 1960,-13.3,32.8,12.23196721311476 46 | 1961,-18.9,36.1,12.839589041095888 47 | 1962,-15.6,37.2,11.958630136986303 48 | 1963,-18.9,36.7,12.046575342465754 49 | 1964,-12.8,37.2,12.555191256830605 50 | 1965,-12.8,35,12.37712328767122 51 | 1966,-13.3,39.4,12.846849315068491 52 | 1967,-15.6,35.6,11.705753424657528 53 | 1968,-18.3,36.7,12.284836065573764 54 | 1969,-11.7,36.1,12.702191780821918 55 | 1970,-16.1,34.4,12.42027397260274 56 | 1971,-15.6,35.6,12.939726027397267 57 | 1972,-15,34.4,12.172540983606549 58 | 1973,-13.9,36.7,13.430821917808212 59 | 1974,-14.4,35,12.660684931506855 60 | 1975,-9.4,36.7,12.790684931506851 61 | 1976,-18.3,35.6,11.859699453551904 62 | 1977,-18.9,40,12.458356164383554 63 | 1978,-12.2,35,11.707123287671221 64 | 1979,-17.8,35,13.251780821917801 65 | 1980,-18.3,38.9,12.78374316939891 66 | 1981,-16.7,35.6,12.912876712328769 67 | 1982,-17.8,36.7,12.749178082191776 68 | 1983,-15.6,37.2,13.362191780821927 69 | 1984,-13.3,35.6,13.030327868852472 70 | 1985,-18.9,35,13.110821917808213 71 | 1986,-13.3,36.7,12.983287671232878 72 | 1987,-15.6,36.1,12.903287671232881 73 | 1988,-15,37.2,12.674316939890723 74 | 1989,-14.4,35.6,12.234657534246589 75 | 1990,-13.9,35,14.048219178082192 76 | 1991,-12.2,38.9,14.049178082191775 77 | 1992,-11.7,33.9,12.195081967213117 78 | 1993,-13.9,38.9,13.1131506849315 79 | 1994,-18.9,36.7,12.952602739726041 80 | 1995,-14.4,38.9,13.01150684931507 81 | 1996,-15,35.6,12.081420765027325 82 | 1997,-15.6,36.1,12.414246575342458 83 | 1998,-10,33.9,14.0149315068493 84 | 1999,-12.8,38.3,13.639589041095885 85 | 2000,-16.1,33.9,12.131420765027318 86 | 2001,-8.9,39.4,13.498493150684943 87 | 2002,-7.2,36.7,13.599999999999998 88 | 2003,-13.9,34.4,11.953150684931495 89 | 2004,-17.2,32.8,12.515846994535524 90 | 2005,-15,37.2,13.219863013698642 91 | 2006,-9.4,36.1,13.841506849315062 92 | 2007,-13.3,33.3,12.878493150684925 93 | 2008,-12.2,35.6,12.991803278688527 94 | 2009,-14.4,33.3,12.273972602739729 95 | 2010,-10.6,39.4,13.783698630136989 96 | 2011,-14.4,40,13.620684931506842 97 | 2012,-10.6,37.8,14.093579234972683 98 | 2013,-11.7,36.7,13.033150684931497 99 | 2014,-15.5,33.3,12.514657534246563 100 | 2015,-16.6,36.1,13.809726027397259 101 | 2016,-18.2,35.6,14.016803278688531 102 | 2017,-9.9,34.4,14.783536585365862 -------------------------------------------------------------------------------- /capture-visualization/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const webServer = require('./web-server.js'); 4 | const captureWebPage = require('./toolkit/capture-web-page.js'); 5 | 6 | webServer.start() 7 | .then(server => { 8 | const urlToCapture = "http://localhost:3000"; 9 | const outputImagePath = "./output/nyc-temperatures.png"; 10 | return captureWebPage(urlToCapture, "svg", outputImagePath) 11 | .then(() => server.close()); // Stop the web server when we are done. 12 | }) 13 | .then(() => { 14 | console.log("All done :)"); 15 | }) 16 | .catch(err => { 17 | console.error("Something went wrong :("); 18 | console.error(err); 19 | }); 20 | -------------------------------------------------------------------------------- /capture-visualization/output/placeholder.txt: -------------------------------------------------------------------------------- 1 | This file is just to ensure that the output directory exists. -------------------------------------------------------------------------------- /capture-visualization/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "listing-4", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "express": "4.16.2", 14 | "nightmare": "2.10.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /capture-visualization/public/app.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | background: white; 4 | } 5 | 6 | /* 7 | Make the output image look better. 8 | */ 9 | svg { 10 | padding: 5px; 11 | } 12 | -------------------------------------------------------------------------------- /capture-visualization/public/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // 4 | // Render a chart to a particular element on the page. 5 | // 6 | function renderChart (bindto, data, size) { 7 | var chart = c3.generate({ 8 | bindto: bindto, 9 | size: size, 10 | data: { 11 | json: data, 12 | keys: { 13 | x: "Year", 14 | value: [ 15 | "AvgTemp" 16 | ] 17 | } 18 | }, 19 | transition: { 20 | duration: 0 // Disable animated transitions when we are capturing a static image. 21 | } 22 | }); 23 | }; 24 | 25 | $(function () { 26 | 27 | $.get("nyc-temperature.csv") 28 | .then(function (response) { 29 | var parseOptions = { 30 | header: true, 31 | dynamicTyping: true 32 | }; 33 | const parsed = Papa.parse(response, parseOptions); 34 | const chartSize = { 35 | width: 600, 36 | height: 300 37 | }; 38 | renderChart("#chart", parsed.data, chartSize); 39 | }) 40 | .catch(function (err) { 41 | console.error(err); 42 | }); 43 | 44 | }); 45 | 46 | -------------------------------------------------------------------------------- /capture-visualization/public/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "c3-template-web-page", 3 | "homepage": "https://github.com/JavaScript-Data-Wrangling/Chapter-10", 4 | "authors": [ 5 | "Ashley Davis " 6 | ], 7 | "description": "", 8 | "main": "", 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "jquery": "^3.3.1", 19 | "c3": "^0.4.18", 20 | "papaparse": "^4.3.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /capture-visualization/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NYC average yearly temperature 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /capture-visualization/public/nyc-temperature.csv: -------------------------------------------------------------------------------- 1 | Year,MinTemp,MaxTemp,AvgTemp 2 | 1917,-25,37.8,10.547245179063365 3 | 1918,-21.1,40,11.825205479452052 4 | 1919,-18.3,37.2,12.125824175824178 5 | 1920,-18.9,34.4,11.286475409836074 6 | 1921,-15.6,35.6,12.751095890410959 7 | 1922,-18.9,34.4,12.001369863013698 8 | 1923,-13.9,37.2,11.665753424657524 9 | 1924,-15,37.2,11.08784153005464 10 | 1925,-18.9,37.2,11.937637362637368 11 | 1926,-15,37.8,10.772865013774107 12 | 1927,-18.3,33.3,11.932465753424664 13 | 1928,-13.9,34.4,11.961157024793385 14 | 1929,-13.3,37.2,12.328082191780814 15 | 1930,-13.9,38.9,12.53301369863014 16 | 1931,-11.7,37.2,13.26561643835617 17 | 1932,-11.7,35.6,12.900961538461535 18 | 1933,-21.1,38.9,12.447099447513821 19 | 1934,-26.1,38.3,11.733150684931507 20 | 1935,-18.3,35,11.739041095890402 21 | 1936,-19.4,41.1,11.940027322404374 22 | 1937,-10,37.8,12.546428571428576 23 | 1938,-14.4,35.6,12.938356164383551 24 | 1939,-14.4,35.6,12.62821917808219 25 | 1940,-13.9,36.7,11.04616438356164 26 | 1941,-12.2,36.7,12.78082191780821 27 | 1942,-20,36.1,12.310714285714294 28 | 1943,-22.2,37.2,12.117671232876717 29 | 1944,-11.1,38.9,12.637534246575356 30 | 1945,-16.7,36.1,12.307808219178066 31 | 1946,-15,34.4,13.040273972602742 32 | 1947,-13.9,35,12.159041095890403 33 | 1948,-17.8,39.4,12.316986301369864 34 | 1949,-8.3,38.9,13.88131868131868 35 | 1950,-14.4,35,12.076373626373627 36 | 1951,-13.3,34.4,12.815479452054786 37 | 1952,-13.3,37.8,13.236202185792356 38 | 1953,-10,38.9,13.947796143250693 39 | 1954,-13.9,37.8,12.737671232876705 40 | 1955,-17.8,37.8,12.68255494505495 41 | 1956,-10,37.2,11.957240437158474 42 | 1957,-17.8,38.3,13.081318681318683 43 | 1958,-16.1,33.9,11.46863013698631 44 | 1959,-13.9,36.1,13.060273972602742 45 | 1960,-13.3,32.8,12.23196721311476 46 | 1961,-18.9,36.1,12.839589041095888 47 | 1962,-15.6,37.2,11.958630136986303 48 | 1963,-18.9,36.7,12.046575342465754 49 | 1964,-12.8,37.2,12.555191256830605 50 | 1965,-12.8,35,12.37712328767122 51 | 1966,-13.3,39.4,12.846849315068491 52 | 1967,-15.6,35.6,11.705753424657528 53 | 1968,-18.3,36.7,12.284836065573764 54 | 1969,-11.7,36.1,12.702191780821918 55 | 1970,-16.1,34.4,12.42027397260274 56 | 1971,-15.6,35.6,12.939726027397267 57 | 1972,-15,34.4,12.172540983606549 58 | 1973,-13.9,36.7,13.430821917808212 59 | 1974,-14.4,35,12.660684931506855 60 | 1975,-9.4,36.7,12.790684931506851 61 | 1976,-18.3,35.6,11.859699453551904 62 | 1977,-18.9,40,12.458356164383554 63 | 1978,-12.2,35,11.707123287671221 64 | 1979,-17.8,35,13.251780821917801 65 | 1980,-18.3,38.9,12.78374316939891 66 | 1981,-16.7,35.6,12.912876712328769 67 | 1982,-17.8,36.7,12.749178082191776 68 | 1983,-15.6,37.2,13.362191780821927 69 | 1984,-13.3,35.6,13.030327868852472 70 | 1985,-18.9,35,13.110821917808213 71 | 1986,-13.3,36.7,12.983287671232878 72 | 1987,-15.6,36.1,12.903287671232881 73 | 1988,-15,37.2,12.674316939890723 74 | 1989,-14.4,35.6,12.234657534246589 75 | 1990,-13.9,35,14.048219178082192 76 | 1991,-12.2,38.9,14.049178082191775 77 | 1992,-11.7,33.9,12.195081967213117 78 | 1993,-13.9,38.9,13.1131506849315 79 | 1994,-18.9,36.7,12.952602739726041 80 | 1995,-14.4,38.9,13.01150684931507 81 | 1996,-15,35.6,12.081420765027325 82 | 1997,-15.6,36.1,12.414246575342458 83 | 1998,-10,33.9,14.0149315068493 84 | 1999,-12.8,38.3,13.639589041095885 85 | 2000,-16.1,33.9,12.131420765027318 86 | 2001,-8.9,39.4,13.498493150684943 87 | 2002,-7.2,36.7,13.599999999999998 88 | 2003,-13.9,34.4,11.953150684931495 89 | 2004,-17.2,32.8,12.515846994535524 90 | 2005,-15,37.2,13.219863013698642 91 | 2006,-9.4,36.1,13.841506849315062 92 | 2007,-13.3,33.3,12.878493150684925 93 | 2008,-12.2,35.6,12.991803278688527 94 | 2009,-14.4,33.3,12.273972602739729 95 | 2010,-10.6,39.4,13.783698630136989 96 | 2011,-14.4,40,13.620684931506842 97 | 2012,-10.6,37.8,14.093579234972683 98 | 2013,-11.7,36.7,13.033150684931497 99 | 2014,-15.5,33.3,12.514657534246563 100 | 2015,-16.6,36.1,13.809726027397259 101 | 2016,-18.2,35.6,14.016803278688531 102 | 2017,-9.9,34.4,14.783536585365862 -------------------------------------------------------------------------------- /capture-visualization/toolkit/capture-web-page.js: -------------------------------------------------------------------------------- 1 | // 2 | // A reusable toolkit function to capture a web page to an image file using Nightmare. 3 | // 4 | 5 | "use strict"; 6 | 7 | const Nightmare = require('nightmare'); 8 | 9 | // 10 | // Capture the web page specified by URL to the specifed image file. 11 | // 12 | function captureWebPage (urlToCapture, captureElementSelector, outputImagePath) { 13 | console.log("<< " + urlToCapture); 14 | console.log(">> " + outputImagePath); 15 | 16 | const nightmare = new Nightmare(); // Create an Nightmare instance. 17 | return nightmare.goto(urlToCapture) // Point the browser at the requested web page. 18 | .wait(captureElementSelector) // Wait until the specified HTML element appears on the screen. 19 | .evaluate(captureElementSelector => { // Evaluate JavaScript code within the headless browser. This function returns a promise which changes the way our code works. 20 | const body = document.querySelector("body"); // Find the body element of the web page. 21 | const captureElement = document.querySelector(captureElementSelector); // Find the HTML element to be captured in the DOM. 22 | const captureRect = captureElement.getBoundingClientRect(); // Get the area that we want to capture. 23 | return { // Return details computed in the headless browser to Node.js. 24 | documentArea: { // Return the scrollable area of the page, we will expand the size of the browser window to cover the entire documents (thus removing any scrollbars we might otherwise capture). 25 | width: body.scrollWidth, 26 | height: body.scrollHeight 27 | }, 28 | captureArea: { // Return the rect of the area of the page (e.g. the chart) that we want to capture. 29 | x: captureRect.left, 30 | y: captureRect.top, 31 | width: captureRect.right - captureRect.left, 32 | height: captureRect.bottom - captureRect.top 33 | } 34 | }; 35 | }, captureElementSelector) 36 | .then(pageDetails => { // Retrieve details computed in the headless browser. We can now use these value in subsequent Node.js code. 37 | return nightmare.viewport(pageDetails.documentArea.width, pageDetails.documentArea.height) // Set the viewport to cover the area of the chart. 38 | .screenshot(outputImagePath, pageDetails.captureArea) // Capture a screenshot to an image file. 39 | .end(); // End the Nightmare session. Any queued operations are complated and the headless browser is terminated. 40 | }); 41 | }; 42 | 43 | module.exports = captureWebPage; // Export the function so we can use it in other code modules. -------------------------------------------------------------------------------- /capture-visualization/web-server.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const express = require('express'); 4 | const path = require('path'); 5 | 6 | module.exports = { 7 | start: () => { // Export a start function so our main module can start the web server at its leisure. 8 | return new Promise((resolve, reject) => { // Wrap web server startup in a promise. 9 | const app = express(); 10 | 11 | const staticFilesPath = path.join(__dirname, "public"); // Make our 'public' sub-directory accessible via HTTP. 12 | const staticFilesMiddleWare = express.static(staticFilesPath); 13 | app.use('/', staticFilesMiddleWare); 14 | 15 | const server = app.listen(3000, err => { // Start our web server! 16 | if (err) { 17 | reject(err); // Error occurred while starting web server. 18 | } 19 | else { 20 | resolve(server); // Web server started ok. 21 | } 22 | }); 23 | }); 24 | } 25 | } -------------------------------------------------------------------------------- /images/support1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Data-Wrangling-with-JavaScript/nodejs-visualization-example/f70c9a1276a842df22a69392deb1b57d200fed72/images/support1.png --------------------------------------------------------------------------------