Merge branch 'artemisbot-module-charts'
This commit is contained in:
commit
262136393b
421
package-lock.json
generated
421
package-lock.json
generated
@ -1595,6 +1595,12 @@
|
|||||||
"validator": "^9.4.1"
|
"validator": "^9.4.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"acorn": {
|
||||||
|
"version": "5.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
||||||
|
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
@ -1615,6 +1621,46 @@
|
|||||||
"supports-color": "^5.3.0"
|
"supports-color": "^5.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jsdom": {
|
||||||
|
"version": "11.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
|
||||||
|
"integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"abab": "^2.0.0",
|
||||||
|
"acorn": "^5.5.3",
|
||||||
|
"acorn-globals": "^4.1.0",
|
||||||
|
"array-equal": "^1.0.0",
|
||||||
|
"cssom": ">= 0.3.2 < 0.4.0",
|
||||||
|
"cssstyle": "^1.0.0",
|
||||||
|
"data-urls": "^1.0.0",
|
||||||
|
"domexception": "^1.0.1",
|
||||||
|
"escodegen": "^1.9.1",
|
||||||
|
"html-encoding-sniffer": "^1.0.2",
|
||||||
|
"left-pad": "^1.3.0",
|
||||||
|
"nwsapi": "^2.0.7",
|
||||||
|
"parse5": "4.0.0",
|
||||||
|
"pn": "^1.1.0",
|
||||||
|
"request": "^2.87.0",
|
||||||
|
"request-promise-native": "^1.0.5",
|
||||||
|
"sax": "^1.2.4",
|
||||||
|
"symbol-tree": "^3.2.2",
|
||||||
|
"tough-cookie": "^2.3.4",
|
||||||
|
"w3c-hr-time": "^1.0.1",
|
||||||
|
"webidl-conversions": "^4.0.2",
|
||||||
|
"whatwg-encoding": "^1.0.3",
|
||||||
|
"whatwg-mimetype": "^2.1.0",
|
||||||
|
"whatwg-url": "^6.4.1",
|
||||||
|
"ws": "^5.2.0",
|
||||||
|
"xml-name-validator": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parse5": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
@ -1629,6 +1675,26 @@
|
|||||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
|
||||||
"integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==",
|
"integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"whatwg-url": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lodash.sortby": "^4.7.0",
|
||||||
|
"tr46": "^1.0.1",
|
||||||
|
"webidl-conversions": "^4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"async-limiter": "~1.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3369,6 +3435,266 @@
|
|||||||
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
|
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"d3": {
|
||||||
|
"version": "4.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3/-/d3-4.13.0.tgz",
|
||||||
|
"integrity": "sha512-l8c4+0SldjVKLaE2WG++EQlqD7mh/dmQjvi2L2lKPadAVC+TbJC4ci7Uk9bRi+To0+ansgsS0iWfPjD7DBy+FQ==",
|
||||||
|
"requires": {
|
||||||
|
"d3-array": "1.2.1",
|
||||||
|
"d3-axis": "1.0.8",
|
||||||
|
"d3-brush": "1.0.4",
|
||||||
|
"d3-chord": "1.0.4",
|
||||||
|
"d3-collection": "1.0.4",
|
||||||
|
"d3-color": "1.0.3",
|
||||||
|
"d3-dispatch": "1.0.3",
|
||||||
|
"d3-drag": "1.2.1",
|
||||||
|
"d3-dsv": "1.0.8",
|
||||||
|
"d3-ease": "1.0.3",
|
||||||
|
"d3-force": "1.1.0",
|
||||||
|
"d3-format": "1.2.2",
|
||||||
|
"d3-geo": "1.9.1",
|
||||||
|
"d3-hierarchy": "1.1.5",
|
||||||
|
"d3-interpolate": "1.1.6",
|
||||||
|
"d3-path": "1.0.5",
|
||||||
|
"d3-polygon": "1.0.3",
|
||||||
|
"d3-quadtree": "1.0.3",
|
||||||
|
"d3-queue": "3.0.7",
|
||||||
|
"d3-random": "1.1.0",
|
||||||
|
"d3-request": "1.0.6",
|
||||||
|
"d3-scale": "1.0.7",
|
||||||
|
"d3-selection": "1.3.0",
|
||||||
|
"d3-shape": "1.2.0",
|
||||||
|
"d3-time": "1.0.8",
|
||||||
|
"d3-time-format": "2.1.1",
|
||||||
|
"d3-timer": "1.0.7",
|
||||||
|
"d3-transition": "1.1.1",
|
||||||
|
"d3-voronoi": "1.1.2",
|
||||||
|
"d3-zoom": "1.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-array": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw=="
|
||||||
|
},
|
||||||
|
"d3-axis": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz",
|
||||||
|
"integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo="
|
||||||
|
},
|
||||||
|
"d3-brush": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=",
|
||||||
|
"requires": {
|
||||||
|
"d3-dispatch": "1",
|
||||||
|
"d3-drag": "1",
|
||||||
|
"d3-interpolate": "1",
|
||||||
|
"d3-selection": "1",
|
||||||
|
"d3-transition": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-chord": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=",
|
||||||
|
"requires": {
|
||||||
|
"d3-array": "1",
|
||||||
|
"d3-path": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-collection": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI="
|
||||||
|
},
|
||||||
|
"d3-color": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs="
|
||||||
|
},
|
||||||
|
"d3-dispatch": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg="
|
||||||
|
},
|
||||||
|
"d3-drag": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==",
|
||||||
|
"requires": {
|
||||||
|
"d3-dispatch": "1",
|
||||||
|
"d3-selection": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-dsv": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==",
|
||||||
|
"requires": {
|
||||||
|
"commander": "2",
|
||||||
|
"iconv-lite": "0.4",
|
||||||
|
"rw": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-ease": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4="
|
||||||
|
},
|
||||||
|
"d3-force": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==",
|
||||||
|
"requires": {
|
||||||
|
"d3-collection": "1",
|
||||||
|
"d3-dispatch": "1",
|
||||||
|
"d3-quadtree": "1",
|
||||||
|
"d3-timer": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-format": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw=="
|
||||||
|
},
|
||||||
|
"d3-geo": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==",
|
||||||
|
"requires": {
|
||||||
|
"d3-array": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-hexbin": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-hexbin/-/d3-hexbin-0.2.2.tgz",
|
||||||
|
"integrity": "sha1-nFg32s/UcasFM3qeke8Qv8T5iDE="
|
||||||
|
},
|
||||||
|
"d3-hierarchy": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz",
|
||||||
|
"integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY="
|
||||||
|
},
|
||||||
|
"d3-interpolate": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==",
|
||||||
|
"requires": {
|
||||||
|
"d3-color": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-path": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q="
|
||||||
|
},
|
||||||
|
"d3-polygon": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI="
|
||||||
|
},
|
||||||
|
"d3-quadtree": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg="
|
||||||
|
},
|
||||||
|
"d3-queue": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz",
|
||||||
|
"integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg="
|
||||||
|
},
|
||||||
|
"d3-random": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM="
|
||||||
|
},
|
||||||
|
"d3-request": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==",
|
||||||
|
"requires": {
|
||||||
|
"d3-collection": "1",
|
||||||
|
"d3-dispatch": "1",
|
||||||
|
"d3-dsv": "1",
|
||||||
|
"xmlhttprequest": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-scale": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==",
|
||||||
|
"requires": {
|
||||||
|
"d3-array": "^1.2.0",
|
||||||
|
"d3-collection": "1",
|
||||||
|
"d3-color": "1",
|
||||||
|
"d3-format": "1",
|
||||||
|
"d3-interpolate": "1",
|
||||||
|
"d3-time": "1",
|
||||||
|
"d3-time-format": "2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-selection": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA=="
|
||||||
|
},
|
||||||
|
"d3-shape": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=",
|
||||||
|
"requires": {
|
||||||
|
"d3-path": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-time": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ=="
|
||||||
|
},
|
||||||
|
"d3-time-format": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==",
|
||||||
|
"requires": {
|
||||||
|
"d3-time": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-timer": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA=="
|
||||||
|
},
|
||||||
|
"d3-transition": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==",
|
||||||
|
"requires": {
|
||||||
|
"d3-color": "1",
|
||||||
|
"d3-dispatch": "1",
|
||||||
|
"d3-ease": "1",
|
||||||
|
"d3-interpolate": "1",
|
||||||
|
"d3-selection": "^1.1.0",
|
||||||
|
"d3-timer": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"d3-voronoi": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw="
|
||||||
|
},
|
||||||
|
"d3-zoom": {
|
||||||
|
"version": "1.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz",
|
||||||
|
"integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==",
|
||||||
|
"requires": {
|
||||||
|
"d3-dispatch": "1",
|
||||||
|
"d3-drag": "1",
|
||||||
|
"d3-interpolate": "1",
|
||||||
|
"d3-selection": "1",
|
||||||
|
"d3-transition": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dashdash": {
|
"dashdash": {
|
||||||
"version": "1.14.1",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||||
@ -3396,19 +3722,6 @@
|
|||||||
"abab": "^2.0.0",
|
"abab": "^2.0.0",
|
||||||
"whatwg-mimetype": "^2.2.0",
|
"whatwg-mimetype": "^2.2.0",
|
||||||
"whatwg-url": "^7.0.0"
|
"whatwg-url": "^7.0.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"whatwg-url": {
|
|
||||||
"version": "7.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
|
|
||||||
"integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"lodash.sortby": "^4.7.0",
|
|
||||||
"tr46": "^1.0.1",
|
|
||||||
"webidl-conversions": "^4.0.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"datauri": {
|
"datauri": {
|
||||||
@ -6571,7 +6884,6 @@
|
|||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
"safer-buffer": ">= 2.1.2 < 3"
|
||||||
}
|
}
|
||||||
@ -7336,48 +7648,6 @@
|
|||||||
"integrity": "sha1-hCRCjVtWOtjFx/vsB5uaiwnI3Po=",
|
"integrity": "sha1-hCRCjVtWOtjFx/vsB5uaiwnI3Po=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jsdom": {
|
|
||||||
"version": "11.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
|
|
||||||
"integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"abab": "^2.0.0",
|
|
||||||
"acorn": "^5.5.3",
|
|
||||||
"acorn-globals": "^4.1.0",
|
|
||||||
"array-equal": "^1.0.0",
|
|
||||||
"cssom": ">= 0.3.2 < 0.4.0",
|
|
||||||
"cssstyle": "^1.0.0",
|
|
||||||
"data-urls": "^1.0.0",
|
|
||||||
"domexception": "^1.0.1",
|
|
||||||
"escodegen": "^1.9.1",
|
|
||||||
"html-encoding-sniffer": "^1.0.2",
|
|
||||||
"left-pad": "^1.3.0",
|
|
||||||
"nwsapi": "^2.0.7",
|
|
||||||
"parse5": "4.0.0",
|
|
||||||
"pn": "^1.1.0",
|
|
||||||
"request": "^2.87.0",
|
|
||||||
"request-promise-native": "^1.0.5",
|
|
||||||
"sax": "^1.2.4",
|
|
||||||
"symbol-tree": "^3.2.2",
|
|
||||||
"tough-cookie": "^2.3.4",
|
|
||||||
"w3c-hr-time": "^1.0.1",
|
|
||||||
"webidl-conversions": "^4.0.2",
|
|
||||||
"whatwg-encoding": "^1.0.3",
|
|
||||||
"whatwg-mimetype": "^2.1.0",
|
|
||||||
"whatwg-url": "^6.4.1",
|
|
||||||
"ws": "^5.2.0",
|
|
||||||
"xml-name-validator": "^3.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"acorn": {
|
|
||||||
"version": "5.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
|
||||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
|
|
||||||
"dev": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jsesc": {
|
"jsesc": {
|
||||||
"version": "2.5.2",
|
"version": "2.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||||
@ -8735,6 +9005,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nodom": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nodom/-/nodom-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-+W3jlsobV3NNkO15xQXkWoboeq1RPa/SKi8NMHmWF33SCMX4ALcM5dpPLEnUs69Gu+uZoCX9wcWXy866LXvd8w=="
|
||||||
|
},
|
||||||
"nomnom": {
|
"nomnom": {
|
||||||
"version": "1.5.2",
|
"version": "1.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz",
|
||||||
@ -9361,12 +9636,6 @@
|
|||||||
"error-ex": "^1.2.0"
|
"error-ex": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parse5": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"parseurl": {
|
"parseurl": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
||||||
@ -10492,6 +10761,11 @@
|
|||||||
"aproba": "^1.1.1"
|
"aproba": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rw": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||||
|
"integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
|
||||||
|
},
|
||||||
"rxjs": {
|
"rxjs": {
|
||||||
"version": "6.4.0",
|
"version": "6.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
|
||||||
@ -10524,8 +10798,7 @@
|
|||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"sanitize-html": {
|
"sanitize-html": {
|
||||||
"version": "1.20.0",
|
"version": "1.20.0",
|
||||||
@ -12882,9 +13155,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"whatwg-url": {
|
"whatwg-url": {
|
||||||
"version": "6.5.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
|
||||||
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
|
"integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash.sortby": "^4.7.0",
|
"lodash.sortby": "^4.7.0",
|
||||||
@ -13034,15 +13307,6 @@
|
|||||||
"mkdirp": "^0.5.1"
|
"mkdirp": "^0.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ws": {
|
|
||||||
"version": "5.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
|
||||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"async-limiter": "~1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"xhr": {
|
"xhr": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz",
|
||||||
@ -13096,6 +13360,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
|
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
|
||||||
"integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk="
|
"integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk="
|
||||||
},
|
},
|
||||||
|
"xmlhttprequest": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
|
||||||
|
"integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw="
|
||||||
|
},
|
||||||
"xpath": {
|
"xpath": {
|
||||||
"version": "0.0.27",
|
"version": "0.0.27",
|
||||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz",
|
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz",
|
||||||
|
@ -95,6 +95,8 @@
|
|||||||
"crypto-api": "^0.8.3",
|
"crypto-api": "^0.8.3",
|
||||||
"crypto-js": "^3.1.9-1",
|
"crypto-js": "^3.1.9-1",
|
||||||
"ctph.js": "0.0.5",
|
"ctph.js": "0.0.5",
|
||||||
|
"d3": "^4.9.1",
|
||||||
|
"d3-hexbin": "^0.2.2",
|
||||||
"diff": "^4.0.1",
|
"diff": "^4.0.1",
|
||||||
"es6-promisify": "^6.0.1",
|
"es6-promisify": "^6.0.1",
|
||||||
"escodegen": "^1.11.1",
|
"escodegen": "^1.11.1",
|
||||||
@ -123,6 +125,7 @@
|
|||||||
"ngeohash": "^0.6.3",
|
"ngeohash": "^0.6.3",
|
||||||
"node-forge": "^0.8.2",
|
"node-forge": "^0.8.2",
|
||||||
"node-md6": "^0.1.0",
|
"node-md6": "^0.1.0",
|
||||||
|
"nodom": "^2.2.0",
|
||||||
"notepack.io": "^2.2.0",
|
"notepack.io": "^2.2.0",
|
||||||
"nwmatcher": "^1.4.4",
|
"nwmatcher": "^1.4.4",
|
||||||
"otp": "^0.1.3",
|
"otp": "^0.1.3",
|
||||||
|
@ -1027,6 +1027,7 @@ class Utils {
|
|||||||
"Comma": ",",
|
"Comma": ",",
|
||||||
"Semi-colon": ";",
|
"Semi-colon": ";",
|
||||||
"Colon": ":",
|
"Colon": ":",
|
||||||
|
"Tab": "\t",
|
||||||
"Line feed": "\n",
|
"Line feed": "\n",
|
||||||
"CRLF": "\r\n",
|
"CRLF": "\r\n",
|
||||||
"Forward slash": "/",
|
"Forward slash": "/",
|
||||||
|
@ -378,7 +378,11 @@
|
|||||||
"Image Filter",
|
"Image Filter",
|
||||||
"Contain Image",
|
"Contain Image",
|
||||||
"Cover Image",
|
"Cover Image",
|
||||||
"Image Hue/Saturation/Lightness"
|
"Image Hue/Saturation/Lightness",
|
||||||
|
"Hex Density chart",
|
||||||
|
"Scatter chart",
|
||||||
|
"Series chart",
|
||||||
|
"Heatmap chart"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -395,6 +399,7 @@
|
|||||||
"Generate QR Code",
|
"Generate QR Code",
|
||||||
"Parse QR Code",
|
"Parse QR Code",
|
||||||
"Haversine distance",
|
"Haversine distance",
|
||||||
|
"HTML To Text",
|
||||||
"Generate Lorem Ipsum",
|
"Generate Lorem Ipsum",
|
||||||
"Numberwang",
|
"Numberwang",
|
||||||
"XKCD Random Number"
|
"XKCD Random Number"
|
||||||
|
178
src/core/lib/Charts.mjs
Normal file
178
src/core/lib/Charts.mjs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* @author tlwr [toby@toby.codes]
|
||||||
|
* @author Matt C [me@mitt.dev]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constant
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
export const RECORD_DELIMITER_OPTIONS = ["Line feed", "CRLF"];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @constant
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
export const FIELD_DELIMITER_OPTIONS = ["Space", "Comma", "Semi-colon", "Colon", "Tab"];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default from colour
|
||||||
|
*
|
||||||
|
* @constant
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
export const COLOURS = {
|
||||||
|
min: "white",
|
||||||
|
max: "black"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets values from input for a plot.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {string} recordDelimiter
|
||||||
|
* @param {string} fieldDelimiter
|
||||||
|
* @param {boolean} columnHeadingsAreIncluded - whether we should skip the first record
|
||||||
|
* @param {number} length
|
||||||
|
* @returns {Object[]}
|
||||||
|
*/
|
||||||
|
export function getValues(input, recordDelimiter, fieldDelimiter, columnHeadingsAreIncluded, length) {
|
||||||
|
let headings;
|
||||||
|
const values = [];
|
||||||
|
|
||||||
|
input
|
||||||
|
.split(recordDelimiter)
|
||||||
|
.forEach((row, rowIndex) => {
|
||||||
|
const split = row.split(fieldDelimiter);
|
||||||
|
if (split.length !== length) throw new OperationError(`Each row must have length ${length}.`);
|
||||||
|
|
||||||
|
if (columnHeadingsAreIncluded && rowIndex === 0) {
|
||||||
|
headings = split;
|
||||||
|
} else {
|
||||||
|
values.push(split);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { headings, values };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets values from input for a scatter plot.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {string} recordDelimiter
|
||||||
|
* @param {string} fieldDelimiter
|
||||||
|
* @param {boolean} columnHeadingsAreIncluded - whether we should skip the first record
|
||||||
|
* @returns {Object[]}
|
||||||
|
*/
|
||||||
|
export function getScatterValues(input, recordDelimiter, fieldDelimiter, columnHeadingsAreIncluded) {
|
||||||
|
let { headings, values } = getValues(
|
||||||
|
input,
|
||||||
|
recordDelimiter,
|
||||||
|
fieldDelimiter,
|
||||||
|
columnHeadingsAreIncluded,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
if (headings) {
|
||||||
|
headings = {x: headings[0], y: headings[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
values = values.map(row => {
|
||||||
|
const x = parseFloat(row[0], 10),
|
||||||
|
y = parseFloat(row[1], 10);
|
||||||
|
|
||||||
|
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
||||||
|
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
|
||||||
|
|
||||||
|
return [x, y];
|
||||||
|
});
|
||||||
|
|
||||||
|
return { headings, values };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets values from input for a scatter plot with colour from the third column.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {string} recordDelimiter
|
||||||
|
* @param {string} fieldDelimiter
|
||||||
|
* @param {boolean} columnHeadingsAreIncluded - whether we should skip the first record
|
||||||
|
* @returns {Object[]}
|
||||||
|
*/
|
||||||
|
export function getScatterValuesWithColour(input, recordDelimiter, fieldDelimiter, columnHeadingsAreIncluded) {
|
||||||
|
let { headings, values } = getValues(
|
||||||
|
input,
|
||||||
|
recordDelimiter, fieldDelimiter,
|
||||||
|
columnHeadingsAreIncluded,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
|
if (headings) {
|
||||||
|
headings = {x: headings[0], y: headings[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
values = values.map(row => {
|
||||||
|
const x = parseFloat(row[0], 10),
|
||||||
|
y = parseFloat(row[1], 10),
|
||||||
|
colour = row[2];
|
||||||
|
|
||||||
|
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
||||||
|
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
|
||||||
|
|
||||||
|
return [x, y, colour];
|
||||||
|
});
|
||||||
|
|
||||||
|
return { headings, values };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets values from input for a time series plot.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {string} recordDelimiter
|
||||||
|
* @param {string} fieldDelimiter
|
||||||
|
* @param {boolean} columnHeadingsAreIncluded - whether we should skip the first record
|
||||||
|
* @returns {Object[]}
|
||||||
|
*/
|
||||||
|
export function getSeriesValues(input, recordDelimiter, fieldDelimiter, columnHeadingsAreIncluded) {
|
||||||
|
const { values } = getValues(
|
||||||
|
input,
|
||||||
|
recordDelimiter, fieldDelimiter,
|
||||||
|
false,
|
||||||
|
3
|
||||||
|
);
|
||||||
|
|
||||||
|
let xValues = new Set();
|
||||||
|
const series = {};
|
||||||
|
|
||||||
|
values.forEach(row => {
|
||||||
|
const serie = row[0],
|
||||||
|
xVal = row[1],
|
||||||
|
val = parseFloat(row[2], 10);
|
||||||
|
|
||||||
|
if (Number.isNaN(val)) throw new OperationError("Values must be numbers in base 10.");
|
||||||
|
|
||||||
|
xValues.add(xVal);
|
||||||
|
if (typeof series[serie] === "undefined") series[serie] = {};
|
||||||
|
series[serie][xVal] = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
xValues = new Array(...xValues);
|
||||||
|
|
||||||
|
const seriesList = [];
|
||||||
|
for (const seriesName in series) {
|
||||||
|
const serie = series[seriesName];
|
||||||
|
seriesList.push({name: seriesName, data: serie});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { xValues, series: seriesList };
|
||||||
|
}
|
41
src/core/operations/HTMLToText.mjs
Normal file
41
src/core/operations/HTMLToText.mjs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* @author tlwr [toby@toby.codes]
|
||||||
|
* @author Matt C [me@mitt.dev]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML To Text operation
|
||||||
|
*/
|
||||||
|
class HTMLToText extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTMLToText constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "HTML To Text";
|
||||||
|
this.module = "Default";
|
||||||
|
this.description = "Converts an HTML output from an operation to a readable string instead of being rendered in the DOM.";
|
||||||
|
this.infoURL = "";
|
||||||
|
this.inputType = "html";
|
||||||
|
this.outputType = "string";
|
||||||
|
this.args = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {html} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HTMLToText;
|
266
src/core/operations/HeatmapChart.mjs
Normal file
266
src/core/operations/HeatmapChart.mjs
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
/**
|
||||||
|
* @author tlwr [toby@toby.codes]
|
||||||
|
* @author Matt C [me@mitt.dev]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as d3temp from "d3";
|
||||||
|
import * as nodomtemp from "nodom";
|
||||||
|
import { getScatterValues, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts";
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
import Utils from "../Utils";
|
||||||
|
|
||||||
|
const d3 = d3temp.default ? d3temp.default : d3temp;
|
||||||
|
const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heatmap chart operation
|
||||||
|
*/
|
||||||
|
class HeatmapChart extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HeatmapChart constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Heatmap chart";
|
||||||
|
this.module = "Charts";
|
||||||
|
this.description = "A heatmap is a graphical representation of data where the individual values contained in a matrix are represented as colors.";
|
||||||
|
this.infoURL = "https://wikipedia.org/wiki/Heat_map";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Record delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: RECORD_DELIMITER_OPTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Field delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: FIELD_DELIMITER_OPTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Number of vertical bins",
|
||||||
|
type: "number",
|
||||||
|
value: 25,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Number of horizontal bins",
|
||||||
|
type: "number",
|
||||||
|
value: 25,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Use column headers as labels",
|
||||||
|
type: "boolean",
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "X label",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Y label",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Draw bin edges",
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Min colour value",
|
||||||
|
type: "string",
|
||||||
|
value: COLOURS.min,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Max colour value",
|
||||||
|
type: "string",
|
||||||
|
value: COLOURS.max,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heatmap chart operation.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const recordDelimiter = Utils.charRep(args[0]),
|
||||||
|
fieldDelimiter = Utils.charRep(args[1]),
|
||||||
|
vBins = args[2],
|
||||||
|
hBins = args[3],
|
||||||
|
columnHeadingsAreIncluded = args[4],
|
||||||
|
drawEdges = args[7],
|
||||||
|
minColour = args[8],
|
||||||
|
maxColour = args[9],
|
||||||
|
dimension = 500;
|
||||||
|
if (vBins <= 0) throw new OperationError("Number of vertical bins must be greater than 0");
|
||||||
|
if (hBins <= 0) throw new OperationError("Number of horizontal bins must be greater than 0");
|
||||||
|
|
||||||
|
let xLabel = args[5],
|
||||||
|
yLabel = args[6];
|
||||||
|
const { headings, values } = getScatterValues(
|
||||||
|
input,
|
||||||
|
recordDelimiter,
|
||||||
|
fieldDelimiter,
|
||||||
|
columnHeadingsAreIncluded
|
||||||
|
);
|
||||||
|
|
||||||
|
if (headings) {
|
||||||
|
xLabel = headings.x;
|
||||||
|
yLabel = headings.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = new nodom.Document();
|
||||||
|
let svg = document.createElement("svg");
|
||||||
|
svg = d3.select(svg)
|
||||||
|
.attr("width", "100%")
|
||||||
|
.attr("height", "100%")
|
||||||
|
.attr("viewBox", `0 0 ${dimension} ${dimension}`);
|
||||||
|
|
||||||
|
const margin = {
|
||||||
|
top: 10,
|
||||||
|
right: 0,
|
||||||
|
bottom: 40,
|
||||||
|
left: 30,
|
||||||
|
},
|
||||||
|
width = dimension - margin.left - margin.right,
|
||||||
|
height = dimension - margin.top - margin.bottom,
|
||||||
|
binWidth = width / hBins,
|
||||||
|
binHeight = height/ vBins,
|
||||||
|
marginedSpace = svg.append("g")
|
||||||
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||||
|
|
||||||
|
const bins = this.getHeatmapPacking(values, vBins, hBins),
|
||||||
|
maxCount = Math.max(...bins.map(row => {
|
||||||
|
const lengths = row.map(cell => cell.length);
|
||||||
|
return Math.max(...lengths);
|
||||||
|
}));
|
||||||
|
|
||||||
|
const xExtent = d3.extent(values, d => d[0]),
|
||||||
|
yExtent = d3.extent(values, d => d[1]);
|
||||||
|
|
||||||
|
const xAxis = d3.scaleLinear()
|
||||||
|
.domain(xExtent)
|
||||||
|
.range([0, width]);
|
||||||
|
const yAxis = d3.scaleLinear()
|
||||||
|
.domain(yExtent)
|
||||||
|
.range([height, 0]);
|
||||||
|
|
||||||
|
const colour = d3.scaleSequential(d3.interpolateLab(minColour, maxColour))
|
||||||
|
.domain([0, maxCount]);
|
||||||
|
|
||||||
|
marginedSpace.append("clipPath")
|
||||||
|
.attr("id", "clip")
|
||||||
|
.append("rect")
|
||||||
|
.attr("width", width)
|
||||||
|
.attr("height", height);
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "bins")
|
||||||
|
.attr("clip-path", "url(#clip)")
|
||||||
|
.selectAll("g")
|
||||||
|
.data(bins)
|
||||||
|
.enter()
|
||||||
|
.append("g")
|
||||||
|
.selectAll("rect")
|
||||||
|
.data(d => d)
|
||||||
|
.enter()
|
||||||
|
.append("rect")
|
||||||
|
.attr("x", (d) => binWidth * d.x)
|
||||||
|
.attr("y", (d) => (height - binHeight * (d.y + 1)))
|
||||||
|
.attr("width", binWidth)
|
||||||
|
.attr("height", binHeight)
|
||||||
|
.attr("fill", (d) => colour(d.length))
|
||||||
|
.attr("stroke", drawEdges ? "rgba(0, 0, 0, 0.5)" : "none")
|
||||||
|
.attr("stroke-width", drawEdges ? "0.5" : "none")
|
||||||
|
.append("title")
|
||||||
|
.text(d => {
|
||||||
|
const count = d.length,
|
||||||
|
perc = 100.0 * d.length / values.length,
|
||||||
|
tooltip = `Count: ${count}\n
|
||||||
|
Percentage: ${perc.toFixed(2)}%\n
|
||||||
|
`.replace(/\s{2,}/g, "\n");
|
||||||
|
return tooltip;
|
||||||
|
});
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "axis axis--y")
|
||||||
|
.call(d3.axisLeft(yAxis).tickSizeOuter(-width));
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("transform", "rotate(-90)")
|
||||||
|
.attr("y", -margin.left)
|
||||||
|
.attr("x", -(height / 2))
|
||||||
|
.attr("dy", "1em")
|
||||||
|
.style("text-anchor", "middle")
|
||||||
|
.text(yLabel);
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "axis axis--x")
|
||||||
|
.attr("transform", "translate(0," + height + ")")
|
||||||
|
.call(d3.axisBottom(xAxis).tickSizeOuter(-height));
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("x", width / 2)
|
||||||
|
.attr("y", dimension)
|
||||||
|
.style("text-anchor", "middle")
|
||||||
|
.text(xLabel);
|
||||||
|
|
||||||
|
return svg._groups[0][0].outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packs a list of x, y coordinates into a number of bins for use in a heatmap.
|
||||||
|
*
|
||||||
|
* @param {Object[]} points
|
||||||
|
* @param {number} number of vertical bins
|
||||||
|
* @param {number} number of horizontal bins
|
||||||
|
* @returns {Object[]} a list of bins (each bin is an Array) with x y coordinates, filled with the points
|
||||||
|
*/
|
||||||
|
getHeatmapPacking(values, vBins, hBins) {
|
||||||
|
const xBounds = d3.extent(values, d => d[0]),
|
||||||
|
yBounds = d3.extent(values, d => d[1]),
|
||||||
|
bins = [];
|
||||||
|
|
||||||
|
if (xBounds[0] === xBounds[1]) throw "Cannot pack points. There is no difference between the minimum and maximum X coordinate.";
|
||||||
|
if (yBounds[0] === yBounds[1]) throw "Cannot pack points. There is no difference between the minimum and maximum Y coordinate.";
|
||||||
|
|
||||||
|
for (let y = 0; y < vBins; y++) {
|
||||||
|
bins.push([]);
|
||||||
|
for (let x = 0; x < hBins; x++) {
|
||||||
|
const item = [];
|
||||||
|
item.y = y;
|
||||||
|
item.x = x;
|
||||||
|
|
||||||
|
bins[y].push(item);
|
||||||
|
} // x
|
||||||
|
} // y
|
||||||
|
|
||||||
|
const epsilon = 0.000000001; // This is to clamp values that are exactly the maximum;
|
||||||
|
|
||||||
|
values.forEach(v => {
|
||||||
|
const fractionOfY = (v[1] - yBounds[0]) / ((yBounds[1] + epsilon) - yBounds[0]),
|
||||||
|
fractionOfX = (v[0] - xBounds[0]) / ((xBounds[1] + epsilon) - xBounds[0]),
|
||||||
|
y = Math.floor(vBins * fractionOfY),
|
||||||
|
x = Math.floor(hBins * fractionOfX);
|
||||||
|
|
||||||
|
bins[y][x].push({x: v[0], y: v[1]});
|
||||||
|
});
|
||||||
|
|
||||||
|
return bins;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HeatmapChart;
|
296
src/core/operations/HexDensityChart.mjs
Normal file
296
src/core/operations/HexDensityChart.mjs
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
/**
|
||||||
|
* @author tlwr [toby@toby.codes]
|
||||||
|
* @author Matt C [me@mitt.dev]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as d3temp from "d3";
|
||||||
|
import * as d3hexbintemp from "d3-hexbin";
|
||||||
|
import * as nodomtemp from "nodom";
|
||||||
|
import { getScatterValues, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts";
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import Utils from "../Utils";
|
||||||
|
|
||||||
|
const d3 = d3temp.default ? d3temp.default : d3temp;
|
||||||
|
const d3hexbin = d3hexbintemp.default ? d3hexbintemp.default : d3hexbintemp;
|
||||||
|
const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hex Density chart operation
|
||||||
|
*/
|
||||||
|
class HexDensityChart extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HexDensityChart constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Hex Density chart";
|
||||||
|
this.module = "Charts";
|
||||||
|
this.description = "Hex density charts are used in a similar way to scatter charts, however rather than rendering tens of thousands of points, it groups the points into a few hundred hexagons to show the distribution.";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Record delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: RECORD_DELIMITER_OPTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Field delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: FIELD_DELIMITER_OPTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Pack radius",
|
||||||
|
type: "number",
|
||||||
|
value: 25,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Draw radius",
|
||||||
|
type: "number",
|
||||||
|
value: 15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Use column headers as labels",
|
||||||
|
type: "boolean",
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "X label",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Y label",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Draw hexagon edges",
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Min colour value",
|
||||||
|
type: "string",
|
||||||
|
value: COLOURS.min,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Max colour value",
|
||||||
|
type: "string",
|
||||||
|
value: COLOURS.max,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Draw empty hexagons within data boundaries",
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hex Bin chart operation.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const recordDelimiter = Utils.charRep(args[0]),
|
||||||
|
fieldDelimiter = Utils.charRep(args[1]),
|
||||||
|
packRadius = args[2],
|
||||||
|
drawRadius = args[3],
|
||||||
|
columnHeadingsAreIncluded = args[4],
|
||||||
|
drawEdges = args[7],
|
||||||
|
minColour = args[8],
|
||||||
|
maxColour = args[9],
|
||||||
|
drawEmptyHexagons = args[10],
|
||||||
|
dimension = 500;
|
||||||
|
|
||||||
|
let xLabel = args[5],
|
||||||
|
yLabel = args[6];
|
||||||
|
const { headings, values } = getScatterValues(
|
||||||
|
input,
|
||||||
|
recordDelimiter,
|
||||||
|
fieldDelimiter,
|
||||||
|
columnHeadingsAreIncluded
|
||||||
|
);
|
||||||
|
|
||||||
|
if (headings) {
|
||||||
|
xLabel = headings.x;
|
||||||
|
yLabel = headings.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = new nodom.Document();
|
||||||
|
let svg = document.createElement("svg");
|
||||||
|
svg = d3.select(svg)
|
||||||
|
.attr("width", "100%")
|
||||||
|
.attr("height", "100%")
|
||||||
|
.attr("viewBox", `0 0 ${dimension} ${dimension}`);
|
||||||
|
|
||||||
|
const margin = {
|
||||||
|
top: 10,
|
||||||
|
right: 0,
|
||||||
|
bottom: 40,
|
||||||
|
left: 30,
|
||||||
|
},
|
||||||
|
width = dimension - margin.left - margin.right,
|
||||||
|
height = dimension - margin.top - margin.bottom,
|
||||||
|
marginedSpace = svg.append("g")
|
||||||
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||||
|
|
||||||
|
const hexbin = d3hexbin.hexbin()
|
||||||
|
.radius(packRadius)
|
||||||
|
.extent([0, 0], [width, height]);
|
||||||
|
|
||||||
|
const hexPoints = hexbin(values),
|
||||||
|
maxCount = Math.max(...hexPoints.map(b => b.length));
|
||||||
|
|
||||||
|
const xExtent = d3.extent(hexPoints, d => d.x),
|
||||||
|
yExtent = d3.extent(hexPoints, d => d.y);
|
||||||
|
xExtent[0] -= 2 * packRadius;
|
||||||
|
xExtent[1] += 3 * packRadius;
|
||||||
|
yExtent[0] -= 2 * packRadius;
|
||||||
|
yExtent[1] += 2 * packRadius;
|
||||||
|
|
||||||
|
const xAxis = d3.scaleLinear()
|
||||||
|
.domain(xExtent)
|
||||||
|
.range([0, width]);
|
||||||
|
const yAxis = d3.scaleLinear()
|
||||||
|
.domain(yExtent)
|
||||||
|
.range([height, 0]);
|
||||||
|
|
||||||
|
const colour = d3.scaleSequential(d3.interpolateLab(minColour, maxColour))
|
||||||
|
.domain([0, maxCount]);
|
||||||
|
|
||||||
|
marginedSpace.append("clipPath")
|
||||||
|
.attr("id", "clip")
|
||||||
|
.append("rect")
|
||||||
|
.attr("width", width)
|
||||||
|
.attr("height", height);
|
||||||
|
|
||||||
|
if (drawEmptyHexagons) {
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "empty-hexagon")
|
||||||
|
.selectAll("path")
|
||||||
|
.data(this.getEmptyHexagons(hexPoints, packRadius))
|
||||||
|
.enter()
|
||||||
|
.append("path")
|
||||||
|
.attr("d", d => {
|
||||||
|
return `M${xAxis(d.x)},${yAxis(d.y)} ${hexbin.hexagon(drawRadius)}`;
|
||||||
|
})
|
||||||
|
.attr("fill", (d) => colour(0))
|
||||||
|
.attr("stroke", drawEdges ? "black" : "none")
|
||||||
|
.attr("stroke-width", drawEdges ? "0.5" : "none")
|
||||||
|
.append("title")
|
||||||
|
.text(d => {
|
||||||
|
const count = 0,
|
||||||
|
perc = 0,
|
||||||
|
tooltip = `Count: ${count}\n
|
||||||
|
Percentage: ${perc.toFixed(2)}%\n
|
||||||
|
Center: ${d.x.toFixed(2)}, ${d.y.toFixed(2)}\n
|
||||||
|
`.replace(/\s{2,}/g, "\n");
|
||||||
|
return tooltip;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "hexagon")
|
||||||
|
.attr("clip-path", "url(#clip)")
|
||||||
|
.selectAll("path")
|
||||||
|
.data(hexPoints)
|
||||||
|
.enter()
|
||||||
|
.append("path")
|
||||||
|
.attr("d", d => {
|
||||||
|
return `M${xAxis(d.x)},${yAxis(d.y)} ${hexbin.hexagon(drawRadius)}`;
|
||||||
|
})
|
||||||
|
.attr("fill", (d) => colour(d.length))
|
||||||
|
.attr("stroke", drawEdges ? "black" : "none")
|
||||||
|
.attr("stroke-width", drawEdges ? "0.5" : "none")
|
||||||
|
.append("title")
|
||||||
|
.text(d => {
|
||||||
|
const count = d.length,
|
||||||
|
perc = 100.0 * d.length / values.length,
|
||||||
|
CX = d.x,
|
||||||
|
CY = d.y,
|
||||||
|
xMin = Math.min(...d.map(d => d[0])),
|
||||||
|
xMax = Math.max(...d.map(d => d[0])),
|
||||||
|
yMin = Math.min(...d.map(d => d[1])),
|
||||||
|
yMax = Math.max(...d.map(d => d[1])),
|
||||||
|
tooltip = `Count: ${count}\n
|
||||||
|
Percentage: ${perc.toFixed(2)}%\n
|
||||||
|
Center: ${CX.toFixed(2)}, ${CY.toFixed(2)}\n
|
||||||
|
Min X: ${xMin.toFixed(2)}\n
|
||||||
|
Max X: ${xMax.toFixed(2)}\n
|
||||||
|
Min Y: ${yMin.toFixed(2)}\n
|
||||||
|
Max Y: ${yMax.toFixed(2)}
|
||||||
|
`.replace(/\s{2,}/g, "\n");
|
||||||
|
return tooltip;
|
||||||
|
});
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "axis axis--y")
|
||||||
|
.call(d3.axisLeft(yAxis).tickSizeOuter(-width));
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("transform", "rotate(-90)")
|
||||||
|
.attr("y", -margin.left)
|
||||||
|
.attr("x", -(height / 2))
|
||||||
|
.attr("dy", "1em")
|
||||||
|
.style("text-anchor", "middle")
|
||||||
|
.text(yLabel);
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "axis axis--x")
|
||||||
|
.attr("transform", "translate(0," + height + ")")
|
||||||
|
.call(d3.axisBottom(xAxis).tickSizeOuter(-height));
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("x", width / 2)
|
||||||
|
.attr("y", dimension)
|
||||||
|
.style("text-anchor", "middle")
|
||||||
|
.text(xLabel);
|
||||||
|
|
||||||
|
return svg._groups[0][0].outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hex Bin chart operation.
|
||||||
|
*
|
||||||
|
* @param {Object[]} - centres
|
||||||
|
* @param {number} - radius
|
||||||
|
* @returns {Object[]}
|
||||||
|
*/
|
||||||
|
getEmptyHexagons(centres, radius) {
|
||||||
|
const emptyCentres = [],
|
||||||
|
boundingRect = [d3.extent(centres, d => d.x), d3.extent(centres, d => d.y)],
|
||||||
|
hexagonCenterToEdge = Math.cos(2 * Math.PI / 12) * radius,
|
||||||
|
hexagonEdgeLength = Math.sin(2 * Math.PI / 12) * radius;
|
||||||
|
let indent = false;
|
||||||
|
|
||||||
|
for (let y = boundingRect[1][0]; y <= boundingRect[1][1] + radius; y += hexagonEdgeLength + radius) {
|
||||||
|
for (let x = boundingRect[0][0]; x <= boundingRect[0][1] + radius; x += 2 * hexagonCenterToEdge) {
|
||||||
|
let cx = x;
|
||||||
|
const cy = y;
|
||||||
|
|
||||||
|
if (indent && x >= boundingRect[0][1]) break;
|
||||||
|
if (indent) cx += hexagonCenterToEdge;
|
||||||
|
|
||||||
|
emptyCentres.push({x: cx, y: cy});
|
||||||
|
}
|
||||||
|
indent = !indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyCentres;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HexDensityChart;
|
@ -21,7 +21,7 @@ class JavaScriptParser extends Operation {
|
|||||||
this.name = "JavaScript Parser";
|
this.name = "JavaScript Parser";
|
||||||
this.module = "Code";
|
this.module = "Code";
|
||||||
this.description = "Returns an Abstract Syntax Tree for valid JavaScript code.";
|
this.description = "Returns an Abstract Syntax Tree for valid JavaScript code.";
|
||||||
this.infoURL = "https://en.wikipedia.org/wiki/Abstract_syntax_tree";
|
this.infoURL = "https://wikipedia.org/wiki/Abstract_syntax_tree";
|
||||||
this.inputType = "string";
|
this.inputType = "string";
|
||||||
this.outputType = "string";
|
this.outputType = "string";
|
||||||
this.args = [
|
this.args = [
|
||||||
|
@ -21,7 +21,7 @@ class PEMToHex extends Operation {
|
|||||||
this.name = "PEM to Hex";
|
this.name = "PEM to Hex";
|
||||||
this.module = "PublicKey";
|
this.module = "PublicKey";
|
||||||
this.description = "Converts PEM (Privacy Enhanced Mail) format to a hexadecimal DER (Distinguished Encoding Rules) string.";
|
this.description = "Converts PEM (Privacy Enhanced Mail) format to a hexadecimal DER (Distinguished Encoding Rules) string.";
|
||||||
this.infoURL = "https://en.wikipedia.org/wiki/X.690#DER_encoding";
|
this.infoURL = "https://wikipedia.org/wiki/X.690#DER_encoding";
|
||||||
this.inputType = "string";
|
this.inputType = "string";
|
||||||
this.outputType = "string";
|
this.outputType = "string";
|
||||||
this.args = [];
|
this.args = [];
|
||||||
|
199
src/core/operations/ScatterChart.mjs
Normal file
199
src/core/operations/ScatterChart.mjs
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/**
|
||||||
|
* @author tlwr [toby@toby.codes]
|
||||||
|
* @author Matt C [me@mitt.dev]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as d3temp from "d3";
|
||||||
|
import * as nodomtemp from "nodom";
|
||||||
|
import { getScatterValues, getScatterValuesWithColour, RECORD_DELIMITER_OPTIONS, COLOURS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts";
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import Utils from "../Utils";
|
||||||
|
|
||||||
|
const d3 = d3temp.default ? d3temp.default : d3temp;
|
||||||
|
const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scatter chart operation
|
||||||
|
*/
|
||||||
|
class ScatterChart extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ScatterChart constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Scatter chart";
|
||||||
|
this.module = "Charts";
|
||||||
|
this.description = "Plots two-variable data as single points on a graph.";
|
||||||
|
this.infoURL = "https://wikipedia.org/wiki/Scatter_plot";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Record delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: RECORD_DELIMITER_OPTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Field delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: FIELD_DELIMITER_OPTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Use column headers as labels",
|
||||||
|
type: "boolean",
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "X label",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Y label",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Colour",
|
||||||
|
type: "string",
|
||||||
|
value: COLOURS.max,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Point radius",
|
||||||
|
type: "number",
|
||||||
|
value: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Use colour from third column",
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scatter chart operation.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const recordDelimiter = Utils.charRep(args[0]),
|
||||||
|
fieldDelimiter = Utils.charRep(args[1]),
|
||||||
|
columnHeadingsAreIncluded = args[2],
|
||||||
|
fillColour = args[5],
|
||||||
|
radius = args[6],
|
||||||
|
colourInInput = args[7],
|
||||||
|
dimension = 500;
|
||||||
|
|
||||||
|
let xLabel = args[3],
|
||||||
|
yLabel = args[4];
|
||||||
|
|
||||||
|
const dataFunction = colourInInput ? getScatterValuesWithColour : getScatterValues;
|
||||||
|
const { headings, values } = dataFunction(
|
||||||
|
input,
|
||||||
|
recordDelimiter,
|
||||||
|
fieldDelimiter,
|
||||||
|
columnHeadingsAreIncluded
|
||||||
|
);
|
||||||
|
|
||||||
|
if (headings) {
|
||||||
|
xLabel = headings.x;
|
||||||
|
yLabel = headings.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = new nodom.Document();
|
||||||
|
let svg = document.createElement("svg");
|
||||||
|
svg = d3.select(svg)
|
||||||
|
.attr("width", "100%")
|
||||||
|
.attr("height", "100%")
|
||||||
|
.attr("viewBox", `0 0 ${dimension} ${dimension}`);
|
||||||
|
|
||||||
|
const margin = {
|
||||||
|
top: 10,
|
||||||
|
right: 0,
|
||||||
|
bottom: 40,
|
||||||
|
left: 30,
|
||||||
|
},
|
||||||
|
width = dimension - margin.left - margin.right,
|
||||||
|
height = dimension - margin.top - margin.bottom,
|
||||||
|
marginedSpace = svg.append("g")
|
||||||
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||||
|
|
||||||
|
const xExtent = d3.extent(values, d => d[0]),
|
||||||
|
xDelta = xExtent[1] - xExtent[0],
|
||||||
|
yExtent = d3.extent(values, d => d[1]),
|
||||||
|
yDelta = yExtent[1] - yExtent[0],
|
||||||
|
xAxis = d3.scaleLinear()
|
||||||
|
.domain([xExtent[0] - (0.1 * xDelta), xExtent[1] + (0.1 * xDelta)])
|
||||||
|
.range([0, width]),
|
||||||
|
yAxis = d3.scaleLinear()
|
||||||
|
.domain([yExtent[0] - (0.1 * yDelta), yExtent[1] + (0.1 * yDelta)])
|
||||||
|
.range([height, 0]);
|
||||||
|
|
||||||
|
marginedSpace.append("clipPath")
|
||||||
|
.attr("id", "clip")
|
||||||
|
.append("rect")
|
||||||
|
.attr("width", width)
|
||||||
|
.attr("height", height);
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "points")
|
||||||
|
.attr("clip-path", "url(#clip)")
|
||||||
|
.selectAll("circle")
|
||||||
|
.data(values)
|
||||||
|
.enter()
|
||||||
|
.append("circle")
|
||||||
|
.attr("cx", (d) => xAxis(d[0]))
|
||||||
|
.attr("cy", (d) => yAxis(d[1]))
|
||||||
|
.attr("r", d => radius)
|
||||||
|
.attr("fill", d => {
|
||||||
|
return colourInInput ? d[2] : fillColour;
|
||||||
|
})
|
||||||
|
.attr("stroke", "rgba(0, 0, 0, 0.5)")
|
||||||
|
.attr("stroke-width", "0.5")
|
||||||
|
.append("title")
|
||||||
|
.text(d => {
|
||||||
|
const x = d[0],
|
||||||
|
y = d[1],
|
||||||
|
tooltip = `X: ${x}\n
|
||||||
|
Y: ${y}\n
|
||||||
|
`.replace(/\s{2,}/g, "\n");
|
||||||
|
return tooltip;
|
||||||
|
});
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "axis axis--y")
|
||||||
|
.call(d3.axisLeft(yAxis).tickSizeOuter(-width));
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("transform", "rotate(-90)")
|
||||||
|
.attr("y", -margin.left)
|
||||||
|
.attr("x", -(height / 2))
|
||||||
|
.attr("dy", "1em")
|
||||||
|
.style("text-anchor", "middle")
|
||||||
|
.text(yLabel);
|
||||||
|
|
||||||
|
marginedSpace.append("g")
|
||||||
|
.attr("class", "axis axis--x")
|
||||||
|
.attr("transform", "translate(0," + height + ")")
|
||||||
|
.call(d3.axisBottom(xAxis).tickSizeOuter(-height));
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("x", width / 2)
|
||||||
|
.attr("y", dimension)
|
||||||
|
.style("text-anchor", "middle")
|
||||||
|
.text(xLabel);
|
||||||
|
|
||||||
|
return svg._groups[0][0].outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ScatterChart;
|
227
src/core/operations/SeriesChart.mjs
Normal file
227
src/core/operations/SeriesChart.mjs
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/**
|
||||||
|
* @author tlwr [toby@toby.codes]
|
||||||
|
* @author Matt C [me@mitt.dev]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as d3temp from "d3";
|
||||||
|
import * as nodomtemp from "nodom";
|
||||||
|
import { getSeriesValues, RECORD_DELIMITER_OPTIONS, FIELD_DELIMITER_OPTIONS } from "../lib/Charts";
|
||||||
|
|
||||||
|
import Operation from "../Operation";
|
||||||
|
import Utils from "../Utils";
|
||||||
|
|
||||||
|
const d3 = d3temp.default ? d3temp.default : d3temp;
|
||||||
|
const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Series chart operation
|
||||||
|
*/
|
||||||
|
class SeriesChart extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SeriesChart constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Series chart";
|
||||||
|
this.module = "Charts";
|
||||||
|
this.description = "A time series graph is a line graph of repeated measurements taken over regular time intervals.";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Record delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: RECORD_DELIMITER_OPTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Field delimiter",
|
||||||
|
type: "option",
|
||||||
|
value: FIELD_DELIMITER_OPTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "X label",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Point radius",
|
||||||
|
type: "number",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Series colours",
|
||||||
|
type: "string",
|
||||||
|
value: "mediumseagreen, dodgerblue, tomato",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Series chart operation.
|
||||||
|
*
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const recordDelimiter = Utils.charRep(args[0]),
|
||||||
|
fieldDelimiter = Utils.charRep(args[1]),
|
||||||
|
xLabel = args[2],
|
||||||
|
pipRadius = args[3],
|
||||||
|
seriesColours = args[4].split(","),
|
||||||
|
svgWidth = 500,
|
||||||
|
interSeriesPadding = 20,
|
||||||
|
xAxisHeight = 50,
|
||||||
|
seriesLabelWidth = 50,
|
||||||
|
seriesHeight = 100,
|
||||||
|
seriesWidth = svgWidth - seriesLabelWidth - interSeriesPadding;
|
||||||
|
|
||||||
|
const { xValues, series } = getSeriesValues(input, recordDelimiter, fieldDelimiter),
|
||||||
|
allSeriesHeight = Object.keys(series).length * (interSeriesPadding + seriesHeight),
|
||||||
|
svgHeight = allSeriesHeight + xAxisHeight + interSeriesPadding;
|
||||||
|
|
||||||
|
const document = new nodom.Document();
|
||||||
|
let svg = document.createElement("svg");
|
||||||
|
svg = d3.select(svg)
|
||||||
|
.attr("width", "100%")
|
||||||
|
.attr("height", "100%")
|
||||||
|
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||||
|
|
||||||
|
const xAxis = d3.scalePoint()
|
||||||
|
.domain(xValues)
|
||||||
|
.range([0, seriesWidth]);
|
||||||
|
|
||||||
|
svg.append("g")
|
||||||
|
.attr("class", "axis axis--x")
|
||||||
|
.attr("transform", `translate(${seriesLabelWidth}, ${xAxisHeight})`)
|
||||||
|
.call(
|
||||||
|
d3.axisTop(xAxis).tickValues(xValues.filter((x, i) => {
|
||||||
|
return [0, Math.round(xValues.length / 2), xValues.length -1].indexOf(i) >= 0;
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
svg.append("text")
|
||||||
|
.attr("x", svgWidth / 2)
|
||||||
|
.attr("y", xAxisHeight / 2)
|
||||||
|
.style("text-anchor", "middle")
|
||||||
|
.text(xLabel);
|
||||||
|
|
||||||
|
const tooltipText = {},
|
||||||
|
tooltipAreaWidth = seriesWidth / xValues.length;
|
||||||
|
|
||||||
|
xValues.forEach(x => {
|
||||||
|
const tooltip = [];
|
||||||
|
|
||||||
|
series.forEach(serie => {
|
||||||
|
const y = serie.data[x];
|
||||||
|
if (typeof y === "undefined") return;
|
||||||
|
|
||||||
|
tooltip.push(`${serie.name}: ${y}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
tooltipText[x] = tooltip.join("\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartArea = svg.append("g")
|
||||||
|
.attr("transform", `translate(${seriesLabelWidth}, ${xAxisHeight})`);
|
||||||
|
|
||||||
|
chartArea
|
||||||
|
.append("g")
|
||||||
|
.selectAll("rect")
|
||||||
|
.data(xValues)
|
||||||
|
.enter()
|
||||||
|
.append("rect")
|
||||||
|
.attr("x", x => {
|
||||||
|
return xAxis(x) - (tooltipAreaWidth / 2);
|
||||||
|
})
|
||||||
|
.attr("y", 0)
|
||||||
|
.attr("width", tooltipAreaWidth)
|
||||||
|
.attr("height", allSeriesHeight)
|
||||||
|
.attr("stroke", "none")
|
||||||
|
.attr("fill", "transparent")
|
||||||
|
.append("title")
|
||||||
|
.text(x => {
|
||||||
|
return `${x}\n
|
||||||
|
--\n
|
||||||
|
${tooltipText[x]}\n
|
||||||
|
`.replace(/\s{2,}/g, "\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
const yAxesArea = svg.append("g")
|
||||||
|
.attr("transform", `translate(0, ${xAxisHeight})`);
|
||||||
|
|
||||||
|
series.forEach((serie, seriesIndex) => {
|
||||||
|
const yExtent = d3.extent(Object.values(serie.data)),
|
||||||
|
yAxis = d3.scaleLinear()
|
||||||
|
.domain(yExtent)
|
||||||
|
.range([seriesHeight, 0]);
|
||||||
|
|
||||||
|
const seriesGroup = chartArea
|
||||||
|
.append("g")
|
||||||
|
.attr("transform", `translate(0, ${seriesHeight * seriesIndex + interSeriesPadding * (seriesIndex + 1)})`);
|
||||||
|
|
||||||
|
let path = "";
|
||||||
|
xValues.forEach((x, xIndex) => {
|
||||||
|
let nextX = xValues[xIndex + 1],
|
||||||
|
y = serie.data[x],
|
||||||
|
nextY= serie.data[nextX];
|
||||||
|
|
||||||
|
if (typeof y === "undefined" || typeof nextY === "undefined") return;
|
||||||
|
|
||||||
|
x = xAxis(x); nextX = xAxis(nextX);
|
||||||
|
y = yAxis(y); nextY = yAxis(nextY);
|
||||||
|
|
||||||
|
path += `M ${x} ${y} L ${nextX} ${nextY} z `;
|
||||||
|
});
|
||||||
|
|
||||||
|
seriesGroup
|
||||||
|
.append("path")
|
||||||
|
.attr("d", path)
|
||||||
|
.attr("fill", "none")
|
||||||
|
.attr("stroke", seriesColours[seriesIndex % seriesColours.length])
|
||||||
|
.attr("stroke-width", "1");
|
||||||
|
|
||||||
|
xValues.forEach(x => {
|
||||||
|
const y = serie.data[x];
|
||||||
|
if (typeof y === "undefined") return;
|
||||||
|
|
||||||
|
seriesGroup
|
||||||
|
.append("circle")
|
||||||
|
.attr("cx", xAxis(x))
|
||||||
|
.attr("cy", yAxis(y))
|
||||||
|
.attr("r", pipRadius)
|
||||||
|
.attr("fill", seriesColours[seriesIndex % seriesColours.length])
|
||||||
|
.append("title")
|
||||||
|
.text(d => {
|
||||||
|
return `${x}\n
|
||||||
|
--\n
|
||||||
|
${tooltipText[x]}\n
|
||||||
|
`.replace(/\s{2,}/g, "\n");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
yAxesArea
|
||||||
|
.append("g")
|
||||||
|
.attr("transform", `translate(${seriesLabelWidth - interSeriesPadding}, ${seriesHeight * seriesIndex + interSeriesPadding * (seriesIndex + 1)})`)
|
||||||
|
.attr("class", "axis axis--y")
|
||||||
|
.call(d3.axisLeft(yAxis).ticks(5));
|
||||||
|
|
||||||
|
yAxesArea
|
||||||
|
.append("g")
|
||||||
|
.attr("transform", `translate(0, ${seriesHeight / 2 + seriesHeight * seriesIndex + interSeriesPadding * (seriesIndex + 1)})`)
|
||||||
|
.append("text")
|
||||||
|
.style("text-anchor", "middle")
|
||||||
|
.attr("transform", "rotate(-90)")
|
||||||
|
.text(serie.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
return svg._groups[0][0].outerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SeriesChart;
|
@ -32,6 +32,7 @@ import "./tests/BitwiseOp";
|
|||||||
import "./tests/ByteRepr";
|
import "./tests/ByteRepr";
|
||||||
import "./tests/CartesianProduct";
|
import "./tests/CartesianProduct";
|
||||||
import "./tests/CharEnc";
|
import "./tests/CharEnc";
|
||||||
|
import "./tests/Charts";
|
||||||
import "./tests/Checksum";
|
import "./tests/Checksum";
|
||||||
import "./tests/Ciphers";
|
import "./tests/Ciphers";
|
||||||
import "./tests/Code";
|
import "./tests/Code";
|
||||||
|
55
tests/operations/tests/Charts.mjs
Normal file
55
tests/operations/tests/Charts.mjs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Chart tests.
|
||||||
|
*
|
||||||
|
* @author Matt C [me@mitt.dev]
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import TestRegister from "../TestRegister";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "Scatter chart",
|
||||||
|
input: "100 100\n200 200\n300 300\n400 400\n500 500",
|
||||||
|
expectedMatch: /^<svg width/,
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Scatter chart",
|
||||||
|
"args": ["Line feed", "Space", false, "time", "stress", "black", 5, false]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Hex density chart",
|
||||||
|
input: "100 100\n200 200\n300 300\n400 400\n500 500",
|
||||||
|
expectedMatch: /^<svg width/,
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Hex Density chart",
|
||||||
|
"args": ["Line feed", "Space", 25, 15, true, "", "", true, "white", "black", true]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Series chart",
|
||||||
|
input: "100 100 100\n200 200 200\n300 300 300\n400 400 400\n500 500 500",
|
||||||
|
expectedMatch: /^<svg width/,
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Series chart",
|
||||||
|
"args": ["Line feed", "Space", "", 1, "mediumseagreen, dodgerblue, tomato"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Heatmap chart",
|
||||||
|
input: "100 100\n200 200\n300 300\n400 400\n500 500",
|
||||||
|
expectedMatch: /^<svg width/,
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Heatmap chart",
|
||||||
|
"args": ["Line feed", "Space", 25, 25, true, "", "", false, "white", "black"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
@ -138,7 +138,10 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
fs: "empty"
|
fs: "empty",
|
||||||
|
"child_process": "empty",
|
||||||
|
net: "empty",
|
||||||
|
tls: "empty"
|
||||||
},
|
},
|
||||||
performance: {
|
performance: {
|
||||||
hints: false
|
hints: false
|
||||||
|
Loading…
Reference in New Issue
Block a user