diff --git a/README.md b/README.md
index d2459a2..3d5c66b 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,77 @@
# CAT
+
+## Overview
This Charting Application Tester (CAT) lets users make and adjust web graphics on the fly.
+
+### Controls
+allow the choice and configuration of charts, as well as the data file with which to initialize the charts
+
+#### 1. Choose a Charting Library
+controls which charting application library/version and charting library/version will be loaded
+
+##### Render Chart
+button that generates the selected chart
+
+1. Destroys the currently displayed chart if one has been rendered.
+2. Loads the selected data file.
+3. Initializes the selected charting application library.
+
+##### Library:
+dropdown with a list of charting application libraries
+
+1. Removes the previous library's .js, .css, and/or stylesheet from the DOM.
+2. Updates the status section.
+3. Loads the master branch of the selected library.
+ 1. Loads the library's package.json file to know where the main .js file lives.
+ 2. Optionally loads the settings-schema.json file to populate the settings text/form if the library has a settings schema file.
+ 3. Loads the main .js file.
+ 4. Optionally loads the main .css file if the library has a .css file.
+4. Loads the branches and releases of the library.
+5. Updates the settings text/form.
+
+##### Version:
+dropdown with a list of branches and releases for the selected charting application library
+
+1. Removes the previous library's .js, .css, and/or stylesheet from the DOM.
+2. Loads the selected version of the selected library.
+ 1. Loads the library's package.json file to know where the main .js file lives.
+ 2. Optionally loads the settings-schema.json file to populate the settings text/form if the library has a settings schema file.
+ 3. Loads the main .js file.
+ 4. Optionally loads the main .css file if the library has a .css file.
+3. Updates the settings text/form.
+
+##### Init:
+input that allows the specification of the namespace of the selected charting application library
+
+##### .
+optional input that allows the specification of a method of the selected charting application library that generates the chart
+
+##### Webcharts Version:
+dropdown with a list of branches and releases of the charting library, which is a dependency of the charting application library; Webcharts is currently the only charting library supported
+
+1. Removes the previous library's .js, .css, and/or stylesheet from the DOM.
+2. Loads the selected library.
+ 1. Loads the library's package.json file to know where the main .js file lives.
+ 2. Loads the main .js file.
+ 3. Loads the main .css file if the library has a .css file.
+
+##### Schema:
+input that accepts the name of the settings schema of the selected charting application library
+
+#### 2. Choose a Dataset
+dropdown that allows the selection of data file with which to initialize the selected charting application
+
+##### :magnifying glass:
+button that toggles the display from the chart to the loaded dataset
+
+#### 3. Customize the Chart
+allows editing of the chart settings, either with a text input or with a settings form generated with the charting application library's settings schema
+
+##### Settings:-text
+a simple text input that allows the specification of a settings object in JSON or JavaScript
+
+##### Settings:-form
+a list of inputs generated with the settings schema of the loaded charting application library
+
+#### 4. Environment
+a list of the loaded .css, .js, and stylesheets
diff --git a/build/cat.js b/build/cat.js
index f31cdb9..ea14fc9 100644
--- a/build/cat.js
+++ b/build/cat.js
@@ -1,1799 +1,1048 @@
-(function(global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined'
- ? (module.exports = factory())
- : typeof define === 'function' && define.amd ? define(factory) : (global.cat = factory());
-})(this, function() {
- 'use strict';
-
- /**
- * @this {Promise}
- */
- function finallyConstructor(callback) {
- var constructor = this.constructor;
- return this.then(
- function(value) {
- return constructor.resolve(callback()).then(function() {
- return value;
- });
- },
- function(reason) {
- return constructor.resolve(callback()).then(function() {
- return constructor.reject(reason);
- });
- }
- );
- }
-
- // Store setTimeout reference so promise-polyfill will be unaffected by
- // other code modifying setTimeout (like sinon.useFakeTimers())
- var setTimeoutFunc = setTimeout;
-
- function noop() {}
-
- // Polyfill for Function.prototype.bind
- function bind(fn, thisArg) {
- return function() {
- fn.apply(thisArg, arguments);
- };
- }
-
- /**
- * @constructor
- * @param {Function} fn
- */
- function Promise$1(fn) {
- if (!(this instanceof Promise$1))
- throw new TypeError('Promises must be constructed via new');
- if (typeof fn !== 'function') throw new TypeError('not a function');
- /** @type {!number} */
- this._state = 0;
- /** @type {!boolean} */
- this._handled = false;
- /** @type {Promise|undefined} */
- this._value = undefined;
- /** @type {!Array} */
- this._deferreds = [];
-
- doResolve(fn, this);
- }
-
- function handle(self, deferred) {
- while (self._state === 3) {
- self = self._value;
- }
- if (self._state === 0) {
- self._deferreds.push(deferred);
- return;
- }
- self._handled = true;
- Promise$1._immediateFn(function() {
- var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
- if (cb === null) {
- (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
- return;
- }
- var ret;
- try {
- ret = cb(self._value);
- } catch (e) {
- reject(deferred.promise, e);
- return;
- }
- resolve(deferred.promise, ret);
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.cat = factory());
+}(this, (function () { 'use strict';
+
+ /**
+ * @this {Promise}
+ */
+ function finallyConstructor(callback) {
+ var constructor = this.constructor;
+ return this.then(
+ function(value) {
+ return constructor.resolve(callback()).then(function() {
+ return value;
});
- }
-
- function resolve(self, newValue) {
- try {
- // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
- if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.');
- if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
- var then = newValue.then;
- if (newValue instanceof Promise$1) {
- self._state = 3;
- self._value = newValue;
- finale(self);
- return;
- } else if (typeof then === 'function') {
- doResolve(bind(then, newValue), self);
- return;
- }
- }
- self._state = 1;
- self._value = newValue;
- finale(self);
- } catch (e) {
- reject(self, e);
- }
- }
-
- function reject(self, newValue) {
- self._state = 2;
- self._value = newValue;
- finale(self);
- }
-
- function finale(self) {
- if (self._state === 2 && self._deferreds.length === 0) {
- Promise$1._immediateFn(function() {
- if (!self._handled) {
- Promise$1._unhandledRejectionFn(self._value);
- }
- });
- }
-
- for (var i = 0, len = self._deferreds.length; i < len; i++) {
- handle(self, self._deferreds[i]);
- }
- self._deferreds = null;
- }
-
- /**
- * @constructor
- */
- function Handler(onFulfilled, onRejected, promise) {
- this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
- this.onRejected = typeof onRejected === 'function' ? onRejected : null;
- this.promise = promise;
- }
-
- /**
- * Take a potentially misbehaving resolver function and make sure
- * onFulfilled and onRejected are only called once.
- *
- * Makes no guarantees about asynchrony.
- */
- function doResolve(fn, self) {
- var done = false;
- try {
- fn(
- function(value) {
- if (done) return;
- done = true;
- resolve(self, value);
- },
- function(reason) {
- if (done) return;
- done = true;
- reject(self, reason);
- }
- );
- } catch (ex) {
- if (done) return;
- done = true;
- reject(self, ex);
- }
- }
-
- Promise$1.prototype['catch'] = function(onRejected) {
- return this.then(null, onRejected);
- };
-
- Promise$1.prototype.then = function(onFulfilled, onRejected) {
- // @ts-ignore
- var prom = new this.constructor(noop);
-
- handle(this, new Handler(onFulfilled, onRejected, prom));
- return prom;
- };
-
- Promise$1.prototype['finally'] = finallyConstructor;
-
- Promise$1.all = function(arr) {
- return new Promise$1(function(resolve, reject) {
- if (!arr || typeof arr.length === 'undefined')
- throw new TypeError('Promise.all accepts an array');
- var args = Array.prototype.slice.call(arr);
- if (args.length === 0) return resolve([]);
- var remaining = args.length;
-
- function res(i, val) {
- try {
- if (val && (typeof val === 'object' || typeof val === 'function')) {
- var then = val.then;
- if (typeof then === 'function') {
- then.call(
- val,
- function(val) {
- res(i, val);
- },
- reject
- );
- return;
- }
- }
- args[i] = val;
- if (--remaining === 0) {
- resolve(args);
- }
- } catch (ex) {
- reject(ex);
- }
- }
-
- for (var i = 0; i < args.length; i++) {
- res(i, args[i]);
- }
+ },
+ function(reason) {
+ return constructor.resolve(callback()).then(function() {
+ return constructor.reject(reason);
});
- };
+ }
+ );
+ }
- Promise$1.resolve = function(value) {
- if (value && typeof value === 'object' && value.constructor === Promise$1) {
- return value;
- }
+ // Store setTimeout reference so promise-polyfill will be unaffected by
+ // other code modifying setTimeout (like sinon.useFakeTimers())
+ var setTimeoutFunc = setTimeout;
- return new Promise$1(function(resolve) {
- resolve(value);
- });
- };
+ function noop() {}
- Promise$1.reject = function(value) {
- return new Promise$1(function(resolve, reject) {
- reject(value);
- });
- };
-
- Promise$1.race = function(values) {
- return new Promise$1(function(resolve, reject) {
- for (var i = 0, len = values.length; i < len; i++) {
- values[i].then(resolve, reject);
- }
- });
- };
-
- // Use polyfill for setImmediate for performance gains
- Promise$1._immediateFn =
- (typeof setImmediate === 'function' &&
- function(fn) {
- setImmediate(fn);
- }) ||
- function(fn) {
- setTimeoutFunc(fn, 0);
- };
-
- Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) {
- if (typeof console !== 'undefined' && console) {
- console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
- }
+ // Polyfill for Function.prototype.bind
+ function bind(fn, thisArg) {
+ return function() {
+ fn.apply(thisArg, arguments);
};
-
- /** @suppress {undefinedVars} */
- var globalNS = (function() {
- // the only reliable means to get the global object is
- // `Function('return this')()`
- // However, this causes CSP violations in Chrome apps.
- if (typeof self !== 'undefined') {
- return self;
- }
- if (typeof window !== 'undefined') {
- return window;
- }
- if (typeof global !== 'undefined') {
- return global;
- }
- throw new Error('unable to locate global object');
- })();
-
- if (!('Promise' in globalNS)) {
- globalNS['Promise'] = Promise$1;
- } else if (!globalNS.Promise.prototype['finally']) {
- globalNS.Promise.prototype['finally'] = finallyConstructor;
- }
-
- if (typeof Object.assign != 'function') {
- Object.defineProperty(Object, 'assign', {
- value: function assign(target, varArgs) {
- if (target == null) {
- // TypeError if undefined or null
- throw new TypeError('Cannot convert undefined or null to object');
- }
-
- var to = Object(target);
-
- for (var index = 1; index < arguments.length; index++) {
- var nextSource = arguments[index];
-
- if (nextSource != null) {
- // Skip over if undefined or null
- for (var nextKey in nextSource) {
- // Avoid bugs when hasOwnProperty is shadowed
- if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
- to[nextKey] = nextSource[nextKey];
- }
- }
- }
- }
-
- return to;
- },
- writable: true,
- configurable: true
- });
+ }
+
+ /**
+ * @constructor
+ * @param {Function} fn
+ */
+ function Promise$1(fn) {
+ if (!(this instanceof Promise$1))
+ throw new TypeError('Promises must be constructed via new');
+ if (typeof fn !== 'function') throw new TypeError('not a function');
+ /** @type {!number} */
+ this._state = 0;
+ /** @type {!boolean} */
+ this._handled = false;
+ /** @type {Promise|undefined} */
+ this._value = undefined;
+ /** @type {!Array} */
+ this._deferreds = [];
+
+ doResolve(fn, this);
+ }
+
+ function handle(self, deferred) {
+ while (self._state === 3) {
+ self = self._value;
}
-
- if (!Array.prototype.find) {
- Object.defineProperty(Array.prototype, 'find', {
- value: function value(predicate) {
- // 1. Let O be ? ToObject(this value).
- if (this == null) {
- throw new TypeError('"this" is null or not defined');
- }
-
- var o = Object(this);
-
- // 2. Let len be ? ToLength(? Get(O, 'length')).
- var len = o.length >>> 0;
-
- // 3. If IsCallable(predicate) is false, throw a TypeError exception.
- if (typeof predicate !== 'function') {
- throw new TypeError('predicate must be a function');
- }
-
- // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
- var thisArg = arguments[1];
-
- // 5. Let k be 0.
- var k = 0;
-
- // 6. Repeat, while k < len
- while (k < len) {
- // a. Let Pk be ! ToString(k).
- // b. Let kValue be ? Get(O, Pk).
- // c. Let testResult be ToBoolean(? Call(predicate, T, � kValue, k, O �)).
- // d. If testResult is true, return kValue.
- var kValue = o[k];
- if (predicate.call(thisArg, kValue, k, o)) {
- return kValue;
- }
- // e. Increase k by 1.
- k++;
- }
-
- // 7. Return undefined.
- return undefined;
- }
- });
+ if (self._state === 0) {
+ self._deferreds.push(deferred);
+ return;
}
-
- if (!Array.prototype.findIndex) {
- Object.defineProperty(Array.prototype, 'findIndex', {
- value: function value(predicate) {
- // 1. Let O be ? ToObject(this value).
- if (this == null) {
- throw new TypeError('"this" is null or not defined');
- }
-
- var o = Object(this);
-
- // 2. Let len be ? ToLength(? Get(O, "length")).
- var len = o.length >>> 0;
-
- // 3. If IsCallable(predicate) is false, throw a TypeError exception.
- if (typeof predicate !== 'function') {
- throw new TypeError('predicate must be a function');
- }
-
- // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
- var thisArg = arguments[1];
-
- // 5. Let k be 0.
- var k = 0;
-
- // 6. Repeat, while k < len
- while (k < len) {
- // a. Let Pk be ! ToString(k).
- // b. Let kValue be ? Get(O, Pk).
- // c. Let testResult be ToBoolean(? Call(predicate, T, � kValue, k, O �)).
- // d. If testResult is true, return k.
- var kValue = o[k];
- if (predicate.call(thisArg, kValue, k, o)) {
- return k;
- }
- // e. Increase k by 1.
- k++;
- }
-
- // 7. Return -1.
- return -1;
- }
- });
- }
-
- function init() {
- //layout the cat
- this.wrap = d3
- .select(this.element)
- .append('div')
- .attr('class', 'cat-wrap');
- this.layout(this);
-
- //initialize the settings
- this.setDefaults(this);
-
- //add others here!
-
- //create the controls
- this.controls.init(this);
- }
-
- function layout(cat) {
- /* Layout primary sections */
- cat.controls.wrap = cat.wrap.append('div').classed('cat-controls section', true);
- cat.chartWrap = cat.wrap.append('div').classed('cat-chart section', true);
- cat.dataWrap = cat.wrap
- .append('div')
- .classed('cat-data section', true)
- .classed('hidden', true);
-
- /* Layout CAT Controls Divs */
- cat.controls.wrap
- .append('h2')
- .classed('cat-controls-header', true)
- .text('Charting Application Tester 😼');
-
- cat.controls.submitWrap = cat.controls.wrap
- .append('div')
- .classed('control-section submit-section', true);
-
- cat.controls.rendererWrap = cat.controls.wrap
- .append('div')
- .classed('control-section renderer-section', true);
-
- cat.controls.dataWrap = cat.controls.wrap
- .append('div')
- .classed('control-section data-section', true);
-
- cat.controls.settingsWrap = cat.controls.wrap
- .append('div')
- .classed('control-section settings-section', true);
-
- cat.controls.environmentWrap = cat.controls.wrap
- .append('div')
- .classed('control-section environment-section', true);
- }
-
- function addControlsToggle() {
- var _this = this;
-
- var cat = this;
-
- this.controls.minimize = this.controls.submitWrap
- .append('div')
- .classed('cat-button cat-button--minimize hidden', true)
- .attr('title', 'Hide controls')
- .text('<<')
- .on('click', function() {
- _this.controls.wrap.classed('hidden', true);
- _this.chartWrap.style('margin-left', 0);
- _this.chartWrap.selectAll('.wc-chart').each(function(d) {
- try {
- d.draw();
- } catch (error) {}
- });
- _this.dataWrap.style('margin-left', 0);
- _this.controls.maximize = _this.wrap
- .insert('div', ':first-child')
- .classed('cat-button cat-button--maximize', true)
- .text('>>')
- .attr('title', 'Show controls')
- .on('click', function() {
- cat.controls.wrap.classed('hidden', false);
- cat.chartWrap.style('margin-left', '20%');
- cat.chartWrap.selectAll('.wc-chart').each(function(d) {
- try {
- d.draw();
- } catch (error) {}
- });
- cat.dataWrap.style('margin-left', '20%');
- d3.select(this).remove();
- });
- });
- }
-
- var _typeof =
- typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'
- ? function(obj) {
- return typeof obj;
- }
- : function(obj) {
- return obj &&
- typeof Symbol === 'function' &&
- obj.constructor === Symbol &&
- obj !== Symbol.prototype
- ? 'symbol'
- : typeof obj;
- };
-
- // Nice script loader from here: https://stackoverflow.com/questions/538745/how-to-tell-if-a-script-tag-failed-to-load
-
- function scriptLoader() {}
-
- scriptLoader.prototype = {
- timer: function timer(
- times, // number of times to try
- delay, // delay per try
- delayMore, // extra delay per try (additional to delay)
- test, // called each try, timer stops if this returns true
- failure, // called on failure
- result // used internally, shouldn't be passed
- ) {
- var me = this;
- if (times == -1 || times > 0) {
- setTimeout(function() {
- result = test() ? 1 : 0;
- me.timer(
- result ? 0 : times > 0 ? --times : times,
- delay + (delayMore ? delayMore : 0),
- delayMore,
- test,
- failure,
- result
- );
- }, result || delay < 0 ? 0.1 : delay);
- } else if (typeof failure == 'function') {
- setTimeout(failure, 1);
- }
- },
-
- addEvent: function addEvent(el, eventName, eventFunc) {
- if ((typeof el === 'undefined' ? 'undefined' : _typeof(el)) != 'object') {
- return false;
- }
-
- if (el.addEventListener) {
- el.addEventListener(eventName, eventFunc, false);
- return true;
- }
-
- if (el.attachEvent) {
- el.attachEvent('on' + eventName, eventFunc);
- return true;
- }
-
- return false;
- },
-
- // add script to dom
- require: function require(url, args) {
- var me = this;
- args = args || {};
-
- var scriptTag = document.createElement('script');
- var headTag = document.getElementsByTagName('head')[0];
- if (!headTag) {
- return false;
- }
-
- setTimeout(function() {
- var f = typeof args.success == 'function' ? args.success : function() {};
- args.failure = typeof args.failure == 'function' ? args.failure : function() {};
- var fail = function fail() {
- if (!scriptTag.__es) {
- scriptTag.__es = true;
- scriptTag.id = 'failed';
- args.failure(scriptTag);
- }
- };
- scriptTag.onload = function() {
- scriptTag.id = 'loaded';
- f(scriptTag);
- };
- scriptTag.type = 'text/javascript';
- scriptTag.async = typeof args.async == 'boolean' ? args.async : false;
- scriptTag.charset = 'utf-8';
- me.__es = false;
- me.addEvent(scriptTag, 'error', fail); // when supported
- // when error event is not supported fall back to timer
- me.timer(
- 15,
- 1000,
- 0,
- function() {
- return scriptTag.id == 'loaded';
- },
- function() {
- if (scriptTag.id != 'loaded') {
- fail();
- }
- }
- );
- scriptTag.src = url;
- setTimeout(function() {
- try {
- headTag.appendChild(scriptTag);
- } catch (e) {
- fail();
- }
- }, 1);
- }, typeof args.delay == 'number' ? args.delay : 1);
- return true;
+ self._handled = true;
+ Promise$1._immediateFn(function() {
+ var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
+ if (cb === null) {
+ (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
+ return;
+ }
+ var ret;
+ try {
+ ret = cb(self._value);
+ } catch (e) {
+ reject(deferred.promise, e);
+ return;
+ }
+ resolve(deferred.promise, ret);
+ });
+ }
+
+ function resolve(self, newValue) {
+ try {
+ // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+ if (newValue === self)
+ throw new TypeError('A promise cannot be resolved with itself.');
+ if (
+ newValue &&
+ (typeof newValue === 'object' || typeof newValue === 'function')
+ ) {
+ var then = newValue.then;
+ if (newValue instanceof Promise$1) {
+ self._state = 3;
+ self._value = newValue;
+ finale(self);
+ return;
+ } else if (typeof then === 'function') {
+ doResolve(bind(then, newValue), self);
+ return;
}
- };
-
- function loadPackageJson(cat) {
- return new Promise(function(resolve, reject) {
- cat.current.url =
- cat.current.version === 'master'
- ? (cat.current.rootURL || cat.config.rootURL) + '/' + cat.current.name
- : (cat.current.rootURL || cat.config.rootURL) +
- '/' +
- cat.current.name +
- '@' +
- cat.current.version;
- var xhr = new XMLHttpRequest();
- xhr.open('GET', cat.current.url + '/package.json');
- xhr.onload = function() {
- if (this.status === 200) {
- resolve(xhr.response);
- } else {
- reject({
- status: this.status,
- statusTxt: xhr.statusText
- });
- }
- };
- xhr.onerror = function() {
- reject({
- status: this.status,
- statusText: xhr.statusText
- });
- };
- xhr.send();
- });
- }
-
- function getCSS() {
- var current_css = [];
- d3.selectAll('link').each(function() {
- var obj = {};
- obj.sel = this;
- obj.link = d3.select(this).property('href');
- obj.disabled = d3.select(this).property('disabled');
- obj.filename = obj.link.substring(obj.link.lastIndexOf('/') + 1);
- obj.wrap = d3.select(this);
- current_css.push(obj);
- });
- return current_css;
+ }
+ self._state = 1;
+ self._value = newValue;
+ finale(self);
+ } catch (e) {
+ reject(self, e);
}
-
- function getJS() {
- var current_js = [];
- d3.selectAll('script').each(function() {
- var obj = {};
- obj.link = d3.select(this).property('src');
- obj.filename = obj.link.substring(obj.link.lastIndexOf('/') + 1);
- if (obj.link) {
- current_js.push(obj);
- }
- });
- return current_js;
- }
-
- function createChartExport(cat) {
- /* Get settings from current controls */
- var webcharts_version = cat.controls.libraryVersion.node().value;
- var renderer_version = cat.controls.versionSelect.node().value;
- var data_file = cat.controls.dataFileSelect.node().value;
- var data_file_path = cat.config.dataURL + data_file;
- var init_string = cat.current.sub
- ? cat.current.main + '.' + cat.current.sub
- : cat.current.main;
-
- var chart_config = JSON.stringify(cat.current.config, null, ' ');
- var renderer_css = '';
- if (cat.current.css) {
- var css_path =
- cat.config.rootURL +
- '/' +
- cat.current.name +
- '/' +
- renderer_version +
- '/' +
- cat.current.css;
- renderer_css = "";
+ }
+
+ function reject(self, newValue) {
+ self._state = 2;
+ self._value = newValue;
+ finale(self);
+ }
+
+ function finale(self) {
+ if (self._state === 2 && self._deferreds.length === 0) {
+ Promise$1._immediateFn(function() {
+ if (!self._handled) {
+ Promise$1._unhandledRejectionFn(self._value);
}
-
- /* Return a html for a working chart */
- var exampleTemplate =
- '\n\n\n \n\n
\n ' +
- cat.current.name +
- "\n\n \n\n \n \n \n\n \n " +
- renderer_css +
- "\n \n\n \n " +
- cat.current.name +
- ' created for ' +
- cat.current.defaultData +
- "
\n \n
\n \n\n \n\n";
- return exampleTemplate;
+ });
}
- function showEnv(cat) {
- /*build list of loaded CSS */
- var current_css = getCSS();
- var cssItems = cat.controls.cssList.selectAll('li').data(current_css);
- var newItems = cssItems.enter().append('li');
- var itemContents = newItems.append('span').property('title', function(d) {
- return d.link;
- });
-
- itemContents
- .append('a')
- .text(function(d) {
- return d.filename;
- })
- .attr('href', function(d) {
- return d.link;
- })
- .property('target', '_blank');
-
- var switchWrap = itemContents
- .append('label')
- .attr('class', 'switch')
- .classed('hidden', function(d) {
- return d.filename == 'cat.css';
- });
-
- var switchCheck = switchWrap
- .append('input')
- .property('type', 'checkbox')
- .property('checked', function(d) {
- return !d.disabled;
- });
- switchWrap.append('span').attr('class', 'slider round');
-
- switchCheck.on('click', function(d) {
- //load or unload css
- d.disabled = !d.disabled;
- d.wrap.property('disabled', d.disabled);
-
- //update toggle mark
- this.checked = !d.disabled;
- });
-
- cssItems.exit().remove();
-
- /*build list of loaded JS */
- var current_js = getJS();
- var jsItems = cat.controls.jsList.selectAll('li').data(current_js);
-
- jsItems
- .enter()
- .append('li')
- .append('a')
- .text(function(d) {
- return d.filename;
- })
- .property('title', function(d) {
- return d.link;
- })
- .attr('href', function(d) {
- return d.link;
- })
- .property('target', '_blank');
-
- jsItems.exit().remove();
+ for (var i = 0, len = self._deferreds.length; i < len; i++) {
+ handle(self, self._deferreds[i]);
}
-
- function renderChart(cat) {
- var rendererObj = cat.controls.rendererSelect.selectAll('option:checked').data()[0];
- cat.settings.sync(cat);
- //render the new chart with the current settings
- var dataFile = cat.controls.dataFileSelect.node().value;
- var dataObject = cat.config.dataFiles.find(function(f) {
- return f.label == dataFile;
- });
- var version = cat.controls.versionSelect.node().value;
- cat.current.main = cat.controls.mainFunction.node().value;
- cat.current.sub = cat.controls.subFunction.node().value;
-
- function render(error, data) {
- if (error) {
- cat.status.loadStatus(cat.statusDiv, false, dataFilePath);
- } else {
- cat.status.loadStatus(cat.statusDiv, true, dataFilePath);
- if (cat.current.sub) {
- var myChart = window[cat.current.main][cat.current.sub](
- '.cat-chart',
- cat.current.config
- );
- cat.status.chartCreateStatus(cat.statusDiv, cat.current.main, cat.current.sub);
- } else {
- var myChart = window[cat.current.main]('.cat-chart .chart', cat.current.config);
- cat.status.chartCreateStatus(cat.statusDiv, cat.current.main);
- }
-
- cat.current.htmlExport = createChartExport(cat); // save the source code before init
-
- try {
- myChart.init(data);
- } catch (err) {
- cat.status.chartInitStatus(cat.statusDiv, false, err);
- } finally {
- cat.status.chartInitStatus(cat.statusDiv, true, null, cat.current.htmlExport);
-
- // save to server button
- if (cat.config.useServer) {
- cat.status.saveToServer(cat);
- }
- showEnv(cat);
-
- //don't print any new statuses until a new chart is rendered
- cat.printStatus = false;
- }
- }
- }
-
- if (dataObject.user_loaded) {
- dataObject.json = d3.csv.parse(dataObject.csv_raw);
- render(false, dataObject.json);
- } else {
- var dataFilePath = dataObject.path + dataFile;
- d3.csv(dataFilePath, function(error, data) {
- render(error, data);
- });
+ self._deferreds = null;
+ }
+
+ /**
+ * @constructor
+ */
+ function Handler(onFulfilled, onRejected, promise) {
+ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+ this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+ this.promise = promise;
+ }
+
+ /**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+ function doResolve(fn, self) {
+ var done = false;
+ try {
+ fn(
+ function(value) {
+ if (done) return;
+ done = true;
+ resolve(self, value);
+ },
+ function(reason) {
+ if (done) return;
+ done = true;
+ reject(self, reason);
}
+ );
+ } catch (ex) {
+ if (done) return;
+ done = true;
+ reject(self, ex);
}
+ }
- function loadRenderer(cat) {
- var promisedPackage = loadPackageJson(cat);
- promisedPackage.then(function(response) {
- cat.current.package = JSON.parse(response);
- cat.current.js_url =
- cat.current.url + '/' + cat.current.package.main.replace(/^\.?\/?/, '');
- cat.current.css_url = cat.current.css ? cat.current.url + '/' + cat.current.css : null;
-
- if (cat.current.css) {
- var current_css = getCSS().filter(function(f) {
- return f.link == cat.current.css_url;
- });
- var css_loaded = current_css.length > 0;
- if (!css_loaded) {
- var link = document.createElement('link');
- link.href = cat.current.css_url;
-
- link.type = 'text/css';
- link.rel = 'stylesheet';
- document.getElementsByTagName('head')[0].appendChild(link);
- } else if (current_css[0].disabled) {
- //enable the css if it's disabled
- d3.select(current_css[0].sel).property('disabled', false);
- cat.controls.cssList
- .selectAll('li')
- .filter(function(d) {
- return d.link == cat.current.css_url;
- })
- .select('input')
- .property('checked', true);
- }
- }
-
- var current_js = getJS().filter(function(f) {
- return f.link == cat.current.js_url;
- });
- var js_loaded = current_js.length > 0;
-
- if (!js_loaded) {
- var loader = new scriptLoader();
- loader.require(cat.current.js_url, {
- async: true,
- success: function success() {
- cat.status.loadStatus(
- cat.statusDiv,
- true,
- cat.current.js_url,
- cat.current.name,
- cat.current.version
- );
- renderChart(cat);
- },
- failure: function failure() {
- cat.status.loadStatus(
- cat.statusDiv,
- false,
- cat.current.js_url,
- cat.current.name,
- cat.current.version
- );
- }
- });
- } else {
- cat.status.loadStatus(
- cat.statusDiv,
- true,
- cat.current.js_url,
- cat.current.name,
- cat.current.version
- );
- renderChart(cat);
- }
- });
- }
+ Promise$1.prototype['catch'] = function(onRejected) {
+ return this.then(null, onRejected);
+ };
- function loadLibrary(cat) {
- var version = cat.controls.libraryVersion.node().value;
- var library = 'webcharts'; //hardcode to webcharts for now - could generalize later
+ Promise$1.prototype.then = function(onFulfilled, onRejected) {
+ // @ts-ignore
+ var prom = new this.constructor(noop);
- // --- load css --- //
- var cssPath =
- version !== 'master'
- ? cat.config.rootURL + '/Webcharts@' + version + '/css/webcharts.css'
- : cat.config.rootURL + '/Webcharts/css/webcharts.css';
+ handle(this, new Handler(onFulfilled, onRejected, prom));
+ return prom;
+ };
- var current_css = getCSS().filter(function(f) {
- return f.link == cssPath;
- });
- var css_loaded = current_css.length > 0;
- if (!css_loaded) {
- //load the css if it isn't already loaded
- var link = document.createElement('link');
- link.href = cssPath;
- link.type = 'text/css';
- link.rel = 'stylesheet';
- document.getElementsByTagName('head')[0].appendChild(link);
- } else if (current_css[0].disabled) {
- //enable the css if it's disabled
- d3.select(current_css[0].sel).property('disabled', false);
- cat.controls.cssList
- .selectAll('li')
- .filter(function(d) {
- return d.link == cssPath;
- })
- .select('input')
- .property('checked', true);
- }
+ Promise$1.prototype['finally'] = finallyConstructor;
- // --- load js --- //
- var rendererPath =
- version !== 'master'
- ? cat.config.rootURL + '/' + library + '@' + version + '/build/webcharts.js'
- : cat.config.rootURL + '/Webcharts/build/webcharts.js';
+ Promise$1.all = function(arr) {
+ return new Promise$1(function(resolve, reject) {
+ if (!arr || typeof arr.length === 'undefined')
+ throw new TypeError('Promise.all accepts an array');
+ var args = Array.prototype.slice.call(arr);
+ if (args.length === 0) return resolve([]);
+ var remaining = args.length;
- var current_js = getJS().filter(function(f) {
- return f.link == rendererPath;
- });
- var js_loaded = current_js.length > 0;
-
- if (!js_loaded) {
- var loader = new scriptLoader();
- loader.require(rendererPath, {
- async: true,
- success: function success() {
- cat.status.loadStatus(cat.statusDiv, true, rendererPath, library, version);
- loadRenderer(cat);
+ function res(i, val) {
+ try {
+ if (val && (typeof val === 'object' || typeof val === 'function')) {
+ var then = val.then;
+ if (typeof then === 'function') {
+ then.call(
+ val,
+ function(val) {
+ res(i, val);
},
- failure: function failure() {
- cat.status.loadStatus(cat.statusDiv, false, rendererPath, library, version);
- }
- });
- } else {
- cat.status.loadStatus(cat.statusDiv, true, rendererPath, library, version);
- loadRenderer(cat);
+ reject
+ );
+ return;
+ }
+ }
+ args[i] = val;
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ } catch (ex) {
+ reject(ex);
}
- }
+ }
- function addSubmitButton() {
- var _this = this;
-
- this.controls.submitButton = this.controls.submitWrap
- .append('button')
- .attr('class', 'submit')
- .text('Render Chart')
- .on('click', function() {
- _this.controls.minimize.classed('hidden', false);
- _this.dataWrap.classed('hidden', true);
- _this.chartWrap.classed('hidden', false);
-
- //Disable and/or remove previously loaded stylesheets.
- d3
- .selectAll('link')
- .filter(function() {
- return !this.href.indexOf('css/cat.css');
- })
- .property('disabled', true)
- .remove();
-
- d3
- .selectAll('style')
- .property('disabled', true)
- .remove();
-
- _this.chartWrap.selectAll('*').remove();
- _this.printStatus = true;
- _this.statusDiv = _this.chartWrap.append('div').attr('class', 'status');
- _this.statusDiv
- .append('div')
- .text('Starting to render the chart ... ')
- .classed('info', true);
-
- _this.chartWrap.append('div').attr('class', 'chart');
- loadLibrary(_this);
- });
- }
+ for (var i = 0; i < args.length; i++) {
+ res(i, args[i]);
+ }
+ });
+ };
- function initSubmit(cat) {
- addControlsToggle.call(cat);
- addSubmitButton.call(cat);
+ Promise$1.resolve = function(value) {
+ if (value && typeof value === 'object' && value.constructor === Promise$1) {
+ return value;
}
- function updateRenderer(select) {
- var _this = this;
-
- this.current = d3
- .select(select)
- .select('option:checked')
- .data()[0];
- this.current.version = 'master';
-
- //update the chart type configuration to the defaults for the selected renderer
- this.controls.mainFunction.node().value = this.current.main;
- this.controls.versionSelect.node().value = 'master';
- this.controls.subFunction.node().value = this.current.sub;
- this.controls.schema.node().value = this.current.schema;
-
- //update the selected data set to the default for the new rendererSection
- this.controls.dataFileSelect.selectAll('option').property('selected', function(d) {
- return _this.current.defaultData === d.label;
- });
-
- //Re-initialize the chart config section
- this.settings.set(this);
- }
-
- function initRendererSelect(cat) {
- cat.controls.rendererWrap.append('h3').text('1. Choose a Charting Library');
- cat.controls.rendererWrap.append('span').text('Library: ');
-
- cat.controls.rendererSelect = cat.controls.rendererWrap.append('select');
- cat.controls.rendererSelect
- .selectAll('option')
- .data(cat.config.renderers)
- .enter()
- .append('option')
- .text(function(d) {
- return d.name;
- });
-
- cat.controls.rendererSelect.on('change', function() {
- updateRenderer.call(cat, this);
- });
- cat.controls.rendererWrap.append('br');
- cat.controls.rendererWrap.append('span').text('Version: ');
- cat.controls.versionSelect = cat.controls.rendererWrap.append('input');
- cat.controls.versionSelect.node().value = 'master';
- cat.controls.versionSelect.on('input', function() {
- cat.current.version = this.value;
- });
- cat.controls.versionSelect.on('change', function() {
- cat.settings.set(cat);
- });
- cat.controls.rendererWrap.append('br');
-
- cat.controls.rendererWrap
- .append('a')
- .text('More Options')
- .style('text-decoration', 'underline')
- .style('color', 'blue')
- .style('cursor', 'pointer')
- .on('click', function() {
- d3.select(this).remove();
- cat.controls.rendererWrap.selectAll('*').classed('hidden', false);
- });
-
- //specify the code to create the chart
- cat.controls.rendererWrap
- .append('span')
- .text(' Init: ')
- .classed('hidden', true);
- cat.controls.mainFunction = cat.controls.rendererWrap
- .append('input')
- .classed('hidden', true);
- cat.controls.mainFunction.node().value = cat.current.main;
- cat.controls.rendererWrap
- .append('span')
- .text('.')
- .classed('hidden', true);
- cat.controls.subFunction = cat.controls.rendererWrap
- .append('input')
- .classed('hidden', true);
- cat.controls.subFunction.node().value = cat.current.sub;
- cat.controls.rendererWrap.append('br').classed('hidden', true);
- //Webcharts versionSelect
- cat.controls.rendererWrap
- .append('span')
- .text('Webcharts Version: ')
- .classed('hidden', true);
- cat.controls.libraryVersion = cat.controls.rendererWrap
- .append('input')
- .classed('hidden', true);
- cat.controls.libraryVersion.node().value = 'master';
- cat.controls.rendererWrap.append('br').classed('hidden', true);
-
- cat.controls.rendererWrap
- .append('span')
- .text('Schema: ')
- .classed('hidden', true);
- cat.controls.schema = cat.controls.rendererWrap.append('input').classed('hidden', true);
- cat.controls.schema.node().value = cat.current.schema;
- cat.controls.rendererWrap.append('br').classed('hidden', true);
-
- //add enter listener
- cat.controls.addEnterEventListener(cat.controls.rendererWrap, cat);
- }
-
- function showDataPreview(cat) {
- cat.dataWrap.classed('hidden', false);
- cat.chartWrap.classed('hidden', true);
- cat.dataWrap.selectAll('*').remove();
-
- if (cat.dataPreview) {
- cat.dataPreview.destroy();
- }
-
- var dataFile = cat.controls.dataFileSelect.node().value;
- var dataObject = cat.config.dataFiles.find(function(f) {
- return f.label == dataFile;
- });
- var path = dataObject.path + dataObject.label;
-
- cat.dataWrap
- .append('button')
- .text('<< Close Data Preview')
- .on('click', function() {
- cat.dataWrap.classed('hidden', true);
- cat.chartWrap.classed('hidden', false);
- });
-
- cat.dataWrap.append('h3').text('Data Preview for ' + dataFile);
-
- cat.dataWrap
- .append('div')
- .attr('class', 'dataPreview')
- .style('overflow-x', 'overlay');
- cat.dataPreview = webCharts.createTable('.dataPreview');
- if (dataObject.user_loaded) {
- cat.dataPreview.init(d3.csv.parse(dataObject.csv_raw));
- } else {
- d3.csv(path, function(raw) {
- cat.dataPreview.init(raw);
- });
- }
- }
-
- function initDataSelect(cat) {
- cat.controls.dataWrap.append('h3').text('2. Choose a data Set');
- cat.controls.dataFileSelect = cat.controls.dataWrap.append('select');
-
- cat.controls.dataWrap
- .append('span')
- .html('🔍')
- .style('cursor', 'pointer')
- .on('click', function() {
- showDataPreview(cat);
- });
-
- cat.controls.dataFileSelect
- .selectAll('option')
- .data(cat.config.dataFiles)
- .enter()
- .append('option')
- .text(function(d) {
- return d.label;
- })
- .property('selected', function(d) {
- return cat.current.defaultData == d.label ? true : null;
- });
- }
+ return new Promise$1(function(resolve) {
+ resolve(value);
+ });
+ };
+
+ Promise$1.reject = function(value) {
+ return new Promise$1(function(resolve, reject) {
+ reject(value);
+ });
+ };
+
+ Promise$1.race = function(values) {
+ return new Promise$1(function(resolve, reject) {
+ for (var i = 0, len = values.length; i < len; i++) {
+ values[i].then(resolve, reject);
+ }
+ });
+ };
+
+ // Use polyfill for setImmediate for performance gains
+ Promise$1._immediateFn =
+ (typeof setImmediate === 'function' &&
+ function(fn) {
+ setImmediate(fn);
+ }) ||
+ function(fn) {
+ setTimeoutFunc(fn, 0);
+ };
- function initFileLoad() {
- var cat = this;
- //draw the control
- var loadLabel = cat.controls.dataWrap.append('p').style('margin', 0);
-
- loadLabel
- .append('small')
- .text('Use local .csv file:')
- .append('sup')
- .html('ⓘ')
- .property(
- 'title',
- 'Render a chart using a local file. File is added to the data set list, and is only available for a single session and is not saved.'
- )
- .style('cursor', 'help');
-
- var loadStatus = loadLabel
- .append('small')
- .attr('class', 'loadStatus')
- .style('float', 'right')
- .text('Select a csv to load');
-
- cat.controls.dataFileLoad = cat.controls.dataWrap
- .append('input')
- .attr('type', 'file')
- .attr('class', 'file-load-input')
- .on('change', function() {
- if (this.value.slice(-4).toLowerCase() == '.csv') {
- loadStatus.text(this.files[0].name + ' ready to load').style('color', 'green');
- cat.controls.dataFileLoadButton.attr('disabled', null);
- } else {
- loadStatus.text(this.files[0].name + ' is not a csv').style('color', 'red');
- cat.controls.dataFileLoadButton.attr('disabled', true);
- }
- });
-
- cat.controls.dataFileLoadButton = cat.controls.dataWrap
- .append('button')
- .text('Load')
- .attr('class', 'file-load-button')
- .attr('disabled', true)
- .on('click', function(d) {
- //credit to https://jsfiddle.net/Ln37kqc0/
- var files = cat.controls.dataFileLoad.node().files;
-
- if (files.length <= 0) {
- //shouldn't happen since button is disabled when no file is present, but ...
- console.log('No file selected ...');
- return false;
- }
-
- var fr = new FileReader();
- fr.onload = function(e) {
- // get the current date/time
- var d = new Date();
- var n = d3.time.format('%X')(d);
-
- //make an object for the file
- var dataObject = {
- label: files[0].name + ' (added at ' + n + ')',
- user_loaded: true,
- csv_raw: e.target.result
- };
- cat.config.dataFiles.push(dataObject);
-
- //add it to the select dropdown
- cat.controls.dataFileSelect
- .append('option')
- .datum(dataObject)
- .text(function(d) {
- return d.label;
- })
- .attr('selected', true);
-
- //clear the file input & disable the load button
- loadStatus.text(files[0].name + ' loaded').style('color', 'green');
-
- cat.controls.dataFileLoadButton.attr('disabled', true);
- cat.controls.dataFileLoad.property('value', '');
- };
-
- fr.readAsText(files.item(0));
- });
+ Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) {
+ if (typeof console !== 'undefined' && console) {
+ console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
}
-
- function initChartConfig(cat) {
- var settingsHeading = cat.controls.settingsWrap
- .append('h3')
- .html('3. Customize the Chart ');
-
- cat.controls.settingsWrap.append('span').text('Settings: ');
-
- /*
- //////////////////////////////////////
- //initialize the config status icon
- //////////////////////////////////////
- cat.controls.settingsStatus = settingsSection
- .append("div")
- .style("font-size", "1.5em")
- .style("float", "right")
- .style("cursor", "pointer");
- settingsSection.append("br");
- */
-
- //////////////////////////////////////////////////////////////////////
- //radio buttons to toggle between "text" and "form" based settings
- /////////////////////////////////////////////////////////////////////
- cat.controls.settingsTypeText = cat.controls.settingsWrap
- .append('input')
- .attr('class', 'radio')
- .property('type', 'radio')
- .property('name', 'settingsType')
- .property('value', 'text');
- cat.controls.settingsWrap.append('span').text('text');
- cat.controls.settingsTypeForm = cat.controls.settingsWrap
- .append('input')
- .attr('class', 'radio')
- .property('type', 'radio')
- .property('name', 'settingsType')
- .property('value', 'form');
- cat.controls.settingsWrap.append('span').text('form');
- cat.controls.settingsType = cat.controls.settingsWrap.selectAll('input[type="radio"]');
-
- cat.controls.settingsType.on('change', function(d) {
- cat.settings.sync(cat); //first sync the current settings to both views
-
- //then update to the new view, and update controls.
- cat.current.settingsView = this.value; //
- if (cat.current.settingsView == 'text') {
- cat.controls.settingsInput.classed('hidden', false);
- cat.controls.settingsForm.classed('hidden', true);
- } else if (cat.current.settingsView == 'form') {
- cat.controls.settingsInput.classed('hidden', true);
- cat.controls.settingsForm.classed('hidden', false);
- }
- });
- cat.controls.settingsWrap.append('br');
-
- //////////////////////////////////////////////////////////////////////
- //text input section
- /////////////////////////////////////////////////////////////////////
- cat.controls.settingsInput = cat.controls.settingsWrap
- .append('textarea')
- .attr('rows', 10)
- .style('width', '90%')
- .text('{}');
-
- //////////////////////////////////////////////////////////////////////
- //wrapper for the form
- /////////////////////////////////////////////////////////////////////
- cat.controls.settingsForm = cat.controls.settingsWrap
- .append('div')
- .attr('class', 'settingsForm')
- .append('form');
-
- //set the text/form settings for the first renderer
- cat.settings.set(cat);
+ };
+
+ /** @suppress {undefinedVars} */
+ var globalNS = (function() {
+ // the only reliable means to get the global object is
+ // `Function('return this')()`
+ // However, this causes CSP violations in Chrome apps.
+ if (typeof self !== 'undefined') {
+ return self;
}
-
- function initEnvConfig(cat) {
- var settingsHeading = cat.controls.environmentWrap.append('h3').html('4. Environment ');
-
- cat.controls.cssList = cat.controls.environmentWrap.append('ul').attr('class', 'cssList');
- cat.controls.cssList.append('h5').text('Loaded Stylesheets');
-
- cat.controls.jsList = cat.controls.environmentWrap.append('ul').attr('class', 'jsList');
- cat.controls.jsList.append('h5').text('Loaded javascript');
-
- showEnv(cat);
+ if (typeof window !== 'undefined') {
+ return window;
}
-
- function init$1(cat) {
- cat.current = cat.config.renderers[0];
- cat.current.version = 'master';
- initSubmit(cat);
- initRendererSelect(cat);
- initDataSelect(cat);
- initFileLoad.call(cat);
- initChartConfig(cat);
- initEnvConfig(cat);
+ if (typeof global !== 'undefined') {
+ return global;
}
+ throw new Error('unable to locate global object');
+ })();
+
+ if (!('Promise' in globalNS)) {
+ globalNS['Promise'] = Promise$1;
+ } else if (!globalNS.Promise.prototype['finally']) {
+ globalNS.Promise.prototype['finally'] = finallyConstructor;
+ }
+
+ if (typeof Object.assign != 'function') {
+ Object.defineProperty(Object, 'assign', {
+ value: function assign(target, varArgs) {
+
+ if (target == null) {
+ // TypeError if undefined or null
+ throw new TypeError('Cannot convert undefined or null to object');
+ }
- function addEnterEventListener(selection, cat) {
- //Add Enter event listener to all controls.
- selection.selectAll('select,input').each(function() {
- this.addEventListener('keypress', function(e) {
- var key = e.which || e.keyCode;
-
- //13 is Enter
- if (key === 13) cat.controls.submitButton.node().click();
- });
- });
- }
-
- /*------------------------------------------------------------------------------------------------\
- Define controls object.
- \------------------------------------------------------------------------------------------------*/
-
- var controls = {
- init: init$1,
- addEnterEventListener: addEnterEventListener
- };
-
- var defaultSettings = {
- useServer: false,
- rootURL: null,
- dataURL: null,
- dataFiles: [],
- renderers: []
- };
+ var to = Object(target);
- function setDefaults(cat) {
- cat.config.useServer = cat.config.useServer || defaultSettings.useServer;
- cat.config.rootURL = cat.config.rootURL || defaultSettings.rootURL;
- cat.config.dataURL = cat.config.dataURL || defaultSettings.dataURL;
- cat.config.dataFiles = cat.config.dataFiles || defaultSettings.dataFiles;
- cat.config.renderers = cat.config.renderers || defaultSettings.renderers;
-
- cat.config.dataFiles = cat.config.dataFiles.map(function(df) {
- return typeof df == 'string'
- ? { label: df, path: cat.config.dataURL, user_loaded: false }
- : df;
- });
- }
+ for (var index = 1; index < arguments.length; index++) {
+ var nextSource = arguments[index];
- function makeForm(cat, obj) {
- d3
- .select('.settingsForm form')
- .selectAll('*')
- .remove();
-
- //define form from settings schema
- cat.current.form = brutusin['json-forms'].create(cat.current.schemaObj);
-
- if (!obj) {
- //Render form with default schema settings.
- cat.current.form.render(d3.select('.settingsForm form').node());
-
- //Define renderer settings.
- cat.current.config = cat.current.form.getData();
-
- //Update text settings with default schema settings.
- //cat.controls.settingsInput.node().value = JSON.stringify(cat.current.config, null, 4);
- var json = JSON.stringify(cat.current.config, null, 4);
- cat.controls.settingsInput.attr('rows', json.split('\n').length);
- cat.controls.settingsInput.html(json);
- } else
- //Render form with updated text settings.
- cat.current.form.render(d3.select('.settingsForm form').node(), cat.current.config);
-
- d3
- .select('.settingsForm form')
- .selectAll('.glyphicon-remove')
- .text('X');
-
- //handle submission with the "render chart" button
- d3.select('.settingsForm form .form-actions input').remove();
- //format the form a little bit so that we can dodge bootstrap
- d3.selectAll('i.icon-plus-sign').text('+');
- d3.selectAll('i.icon-minus-sign').text('-');
-
- //add enter listener
- cat.controls.addEnterEventListener(cat.controls.wrap.select('.settingsForm'), cat);
- }
+ if (nextSource != null) {
+ // Skip over if undefined or null
+ for (var nextKey in nextSource) {
+ // Avoid bugs when hasOwnProperty is shadowed
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
+ to[nextKey] = nextSource[nextKey];
+ }
+ }
+ }
+ }
- function setStatus(cat, statusVal) {
- var statusOptions = [
- {
- key: 'valid',
- symbol: '✔',
- color: 'green',
- details:
- "Settings match the current schema. Click 'Render Chart' to draw the chart."
- },
- {
- key: 'invalid',
- symbol: '✘',
- color: 'red',
- details:
- "Settings do not match the current schema. You can still click 'Render Chart' to try to draw the chart, but it might not work as expected."
- },
- {
- key: 'unknown',
- symbol: '?',
- color: 'blue',
- details:
- "You've loaded a schema, but the setting have changed. Click 'Validate Settings' to see if they're valid or you can click 'Render Chart' and see what happens."
- },
- {
- key: 'no schema',
- symbol: 'NA',
- color: '#999',
- details:
- "No Schema loaded. Cannot validate the current settings. You can click 'Render Chart' and see what happens."
- }
- ];
+ return to;
+ },
+ writable: true,
+ configurable: true
+ });
+ }
+
+ if (!Array.prototype.find) {
+ Object.defineProperty(Array.prototype, 'find', {
+ value: function value(predicate) {
+ // 1. Let O be ? ToObject(this value).
+ if (this == null) {
+ throw new TypeError('"this" is null or not defined');
+ }
- var myStatus = statusOptions.filter(function(d) {
- return d.key == statusVal;
- })[0];
+ var o = Object(this);
- cat.controls.settingsStatus
- .html(myStatus.symbol)
- .style('color', myStatus.color)
- .attr('title', myStatus.details);
- }
+ // 2. Let len be ? ToLength(? Get(O, 'length')).
+ var len = o.length >>> 0;
- function validateSchema(cat) {
- // consider: http://epoberezkin.github.io/ajv/#getting-started
- // var Ajv = require('ajv');
- // var ajv = new Ajv(); // options can be passed, e.g. {allErrors: true}
- // var validate = ajv.compile(cat.);
- return true;
- }
+ // 3. If IsCallable(predicate) is false, throw a TypeError exception.
+ if (typeof predicate !== 'function') {
+ throw new TypeError('predicate must be a function');
+ }
- function set$1(cat) {
- // load the schema (if any) and see if it is validate
- cat.current.schemaPath = [
- cat.current.rootURL || cat.config.rootURL,
- cat.current.version !== 'master'
- ? cat.current.name + '@' + cat.current.version
- : cat.current.name,
- cat.current.schema
- ].join('/');
-
- cat.current.settingsView = 'text';
- cat.controls.settingsInput.value = '{}';
- cat.current.config = {};
-
- d3.json(cat.current.schemaPath, function(error, schemaObj) {
- if (error) {
- console.log('No schema loaded.');
- cat.current.hasValidSchema = false;
- cat.current.schemaObj = null;
- } else {
- // attempt to validate the schema
- console.log('Schema found ...');
- cat.current.hasValidSchema = validateSchema(schemaObj);
- cat.current.settingsView = cat.current.hasValidSchema ? 'form' : 'text';
- cat.current.schemaObj = cat.current.hasValidSchema ? schemaObj : null;
- }
- //set the radio buttons
- cat.controls.settingsTypeText.property('checked', cat.current.settingsView == 'text');
+ // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
+ var thisArg = arguments[1];
+
+ // 5. Let k be 0.
+ var k = 0;
+
+ // 6. Repeat, while k < len
+ while (k < len) {
+ // a. Let Pk be ! ToString(k).
+ // b. Let kValue be ? Get(O, Pk).
+ // c. Let testResult be ToBoolean(? Call(predicate, T, � kValue, k, O �)).
+ // d. If testResult is true, return kValue.
+ var kValue = o[k];
+ if (predicate.call(thisArg, kValue, k, o)) {
+ return kValue;
+ }
+ // e. Increase k by 1.
+ k++;
+ }
- cat.controls.settingsTypeForm
- .property('checked', cat.current.settingsView == 'form')
- .property('disabled', !cat.current.hasValidSchema);
+ // 7. Return undefined.
+ return undefined;
+ }
+ });
+ }
+
+ if (!Array.prototype.findIndex) {
+ Object.defineProperty(Array.prototype, 'findIndex', {
+ value: function value(predicate) {
+ // 1. Let O be ? ToObject(this value).
+ if (this == null) {
+ throw new TypeError('"this" is null or not defined');
+ }
- // Show/Hide sections
- cat.controls.settingsInput.classed('hidden', cat.current.settingsView != 'text');
- cat.controls.settingsForm.classed('hidden', cat.current.settingsView != 'form');
+ var o = Object(this);
- //update the text or make the schema
- cat.controls.settingsInput.node().value = JSON5.stringify(cat.current.config, null, 4);
+ // 2. Let len be ? ToLength(? Get(O, "length")).
+ var len = o.length >>> 0;
- if (cat.current.hasValidSchema) {
- console.log('... and it is valid. Making a nice form.');
- makeForm(cat);
- }
- });
- }
-
- function sync(cat, printStatus) {
- function IsJsonString(str) {
- try {
- JSON5.parse(str);
- } catch (e) {
- return false;
- }
- return true;
- }
+ // 3. If IsCallable(predicate) is false, throw a TypeError exception.
+ if (typeof predicate !== 'function') {
+ throw new TypeError('predicate must be a function');
+ }
- // set current config
- if (cat.current.settingsView == 'text') {
- var text = cat.controls.settingsInput.node().value;
-
- if (IsJsonString(text)) {
- var settings = JSON5.parse(text);
- var json = JSON.stringify(settings, null, 4);
-
- if (cat.printStatus) {
- cat.statusDiv
- .append('div')
- .html('Successfully loaded settings from text input.')
- .classed('success', true);
- }
-
- cat.controls.settingsInput.node().value = json;
- cat.current.config = settings;
- } else {
- if (cat.printStatus) {
- cat.statusDiv
- .append('div')
- .html(
- "Couldn't load settings from text. Check to see if you have valid JSON."
- )
- .classed('error', true);
- }
- }
+ // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
+ var thisArg = arguments[1];
+
+ // 5. Let k be 0.
+ var k = 0;
+
+ // 6. Repeat, while k < len
+ while (k < len) {
+ // a. Let Pk be ! ToString(k).
+ // b. Let kValue be ? Get(O, Pk).
+ // c. Let testResult be ToBoolean(? Call(predicate, T, � kValue, k, O �)).
+ // d. If testResult is true, return k.
+ var kValue = o[k];
+ if (predicate.call(thisArg, kValue, k, o)) {
+ return k;
+ }
+ // e. Increase k by 1.
+ k++;
+ }
- if (cat.current.hasValidSchema) {
- makeForm(cat, cat.current.config);
- }
- } else if (cat.current.settingsView == 'form') {
- //this submits the form which:
- //- saves the current object
- //- updates the hidden text view
- //$(".settingsForm form").trigger("submit");
- //get settings object from form
- cat.current.config = cat.current.form.getData();
- //update settings text field to match form
- cat.controls.settingsInput.node().value = JSON.stringify(cat.current.config, null, 4);
+ // 7. Return -1.
+ return -1;
+ }
+ });
+ }
+
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+ } : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+
+ var slicedToArray = function () {
+ function sliceIterator(arr, i) {
+ var _arr = [];
+ var _n = true;
+ var _d = false;
+ var _e = undefined;
+
+ try {
+ for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
+ _arr.push(_s.value);
+
+ if (i && _arr.length === i) break;
}
- }
-
- /*------------------------------------------------------------------------------------------------\
- Define controls object.
- \------------------------------------------------------------------------------------------------*/
-
- var settings = {
- set: set$1,
- sync: sync,
- setStatus: setStatus
- };
-
- function chartCreateStatus(statusDiv, main, sub) {
- var message = sub
- ? 'Created the chart by calling ' + main + '.' + sub + '().'
- : 'Created the chart by calling ' + main + '().';
-
- statusDiv
- .append('div')
- .html(message)
- .classed('info', true);
- }
-
- function chartInitStatus(statusDiv, success, err, htmlExport) {
- if (success) {
- //hide all non-error statuses
- statusDiv.selectAll('div:not(.error)').classed('hidden', true);
-
- // Print basic success message
- statusDiv
- .append('div')
- .attr('class', 'initSuccess')
- .html(
- "All Done. Your chart should be below. Show full log"
- )
- .classed('info', true);
-
- //Click to show all statuses
- statusDiv
- .select('div.initSuccess')
- .select('span.showLog')
- .style('cursor', 'pointer')
- .style('text-decoration', 'underline')
- .style('float', 'right')
- .on('click', function() {
- d3.select(this).remove();
- statusDiv.selectAll('div').classed('hidden', false);
- });
-
- //generic caution (hidden by default)
- statusDiv
- .append('div')
- .classed('hidden', true)
- .classed('info', true)
- .html(
- "ⓘ Just because there are no errors doesn't mean there can't be problems. If things look strange, it might be a problem with the settings/data combo or with the renderer itself."
- );
-
- //export source code (via copy/paste)
- statusDiv
- .append('div')
- .classed('hidden', true)
- .classed('export', true)
- .classed('minimized', true)
- .html("Click to see chart's full source code");
-
- statusDiv.select('div.export.minimized').on('click', function() {
- d3.select(this).classed('minimized', false);
- d3.select(this).html('Source code for chart:');
- d3
- .select(this)
- .append('code')
- .html(
- htmlExport
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/\n/g, '
')
- .replace(/ /g, ' ')
- );
- });
- } else {
- //if init fails (success == false)
- statusDiv
- .append('div')
- .html(
- "There might've been some problems initializing the chart. Errors include:
" +
- err +
- ''
- )
- .classed('error', true);
+ } catch (err) {
+ _d = true;
+ _e = err;
+ } finally {
+ try {
+ if (!_n && _i["return"]) _i["return"]();
+ } finally {
+ if (_d) throw _e;
}
- }
+ }
- function saveToServer(cat) {
- var serverDiv = cat.statusDiv
- .append('div')
- .attr('class', 'info')
- .text('Enter your name and click save for a reusable URL. ');
- var nameInput = serverDiv.append('input').property('placeholder', 'Name');
- var saveButton = serverDiv
- .append('button')
- .text('Save')
- .property('disabled', true);
-
- nameInput.on('input', function() {
- saveButton.property('disabled', nameInput.node().value.length == 0);
- });
-
- saveButton.on('click', function() {
- //remove the form
- d3.select(this).remove();
- nameInput.remove();
-
- //format an object for the post
- var dataFile = cat.controls.dataFileSelect.node().value;
- var dataFilePath = cat.config.dataURL + dataFile;
- var chartObj = {
- name: nameInput.node().value,
- renderer: cat.current.name,
- version: cat.controls.versionSelect.node().value,
- dataFile: dataFilePath,
- chart: btoa(cat.current.htmlExport)
- };
-
- //post the object, get a URL back
- $.post('./export/', chartObj, function(data) {
- serverDiv.html("Chart saved as " + data.url + '');
- }).fail(function() {
- serverDiv.text("Sorry. Couldn't save the chart.").classed('error', true);
- console.warn('Error :( Something went wrong saving the chart.');
- });
- });
+ return _arr;
}
- function loadStatus(statusDiv, passed, path, library, version) {
- var message = passed ? 'Successfully loaded ' + path : 'Failed to load ' + path;
-
- if ((library != undefined) & (version != undefined))
- message = message + ' (Library: ' + library + ', Version: ' + version + ')';
-
- statusDiv
- .append('div')
- .html(message)
- .classed('error', !passed);
- }
-
- /*------------------------------------------------------------------------------------------------\
- Define controls object.
- \------------------------------------------------------------------------------------------------*/
-
- var status = {
- chartCreateStatus: chartCreateStatus,
- chartInitStatus: chartInitStatus,
- saveToServer: saveToServer,
- loadStatus: loadStatus
+ return function (arr, i) {
+ if (Array.isArray(arr)) {
+ return arr;
+ } else if (Symbol.iterator in Object(arr)) {
+ return sliceIterator(arr, i);
+ } else {
+ throw new TypeError("Invalid attempt to destructure non-iterable instance");
+ }
};
-
- function createCat() {
- var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'body';
- var config = arguments[1];
-
- var cat = {
- element: element,
- config: config,
- init: init,
- layout: layout,
- controls: controls,
- setDefaults: setDefaults,
- settings: settings,
- status: status
- };
-
- return cat;
- }
-
- var index = {
- createCat: createCat
- };
-
- return index;
-});
+ }();
+
+ function getVersions(repo) {
+ var apiURL = this.config.apiURL + '/' + repo;
+ var branches = fetch(apiURL + '/branches').then(function (response) {
+ return response.json();
+ });
+ var releases = fetch(apiURL + '/releases').then(function (response) {
+ return response.json();
+ });
+
+ return Promise.all([branches, releases]).then(function (values) {
+ var _values = slicedToArray(values, 2),
+ branches = _values[0],
+ releases = _values[1];
+
+ branches.sort(function (a, b) {
+ return a.name === 'master' ? -1 : b.name === 'master' ? 1 : a.name < b.name ? -1 : 1;
+ });
+ return d3.merge(values);
+ }).catch(function (err) {
+ console.log(err);
+ });
+ }
+
+ function updateSelect(select, data) {
+ select.selectAll('option').data(data).enter().append('option').text(function (d) {
+ return d.label;
+ });
+ }
+
+ function loadPackageJSON(repo, version) {
+ var cdnURL = this.config.cdnURL + '/' + repo;
+ var pkgURL = version === 'master' ? cdnURL + '/package.json' : cdnURL + '@' + version + '/package.json';
+
+ return new Promise(function (resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', pkgURL);
+ xhr.onload = function () {
+ if (this.status === 200) {
+ resolve(xhr.response);
+ } else {
+ reject({
+ status: this.status,
+ statusTxt: xhr.statusText
+ });
+ }
+ };
+ xhr.onerror = function () {
+ reject({
+ status: this.status,
+ statusText: xhr.statusText
+ });
+ };
+ xhr.send();
+ });
+ }
+
+ // Nice script loader from here: https://stackoverflow.com/questions/538745/how-to-tell-if-a-script-tag-failed-to-load
+
+ function scriptLoader() {}
+
+ scriptLoader.prototype = {
+ timer: function timer(times, // number of times to try
+ delay, // delay per try
+ delayMore, // extra delay per try (additional to delay)
+ test, // called each try, timer stops if this returns true
+ failure, // called on failure
+ result // used internally, shouldn't be passed
+ ) {
+ var me = this;
+ if (times == -1 || times > 0) {
+ setTimeout(function () {
+ result = test() ? 1 : 0;
+ me.timer(result ? 0 : times > 0 ? --times : times, delay + (delayMore ? delayMore : 0), delayMore, test, failure, result);
+ }, result || delay < 0 ? 0.1 : delay);
+ } else if (typeof failure == 'function') {
+ setTimeout(failure, 1);
+ }
+ },
+
+ addEvent: function addEvent(el, eventName, eventFunc) {
+ if ((typeof el === 'undefined' ? 'undefined' : _typeof(el)) != 'object') {
+ return false;
+ }
+
+ if (el.addEventListener) {
+ el.addEventListener(eventName, eventFunc, false);
+ return true;
+ }
+
+ if (el.attachEvent) {
+ el.attachEvent('on' + eventName, eventFunc);
+ return true;
+ }
+
+ return false;
+ },
+
+ // add script to dom
+ require: function require(url, args) {
+ var me = this;
+ args = args || {};
+
+ var scriptTag = document.createElement('script');
+ var headTag = document.getElementsByTagName('head')[0];
+ if (!headTag) {
+ return false;
+ }
+
+ setTimeout(function () {
+ var f = typeof args.success == 'function' ? args.success : function () {};
+ args.failure = typeof args.failure == 'function' ? args.failure : function () {};
+ var fail = function fail() {
+ if (!scriptTag.__es) {
+ scriptTag.__es = true;
+ scriptTag.id = 'failed';
+ args.failure(scriptTag);
+ }
+ };
+ scriptTag.onload = function () {
+ scriptTag.id = 'loaded';
+ f(scriptTag);
+ };
+ scriptTag.type = 'text/javascript';
+ scriptTag.async = typeof args.async == 'boolean' ? args.async : false;
+ scriptTag.charset = 'utf-8';
+ me.__es = false;
+ me.addEvent(scriptTag, 'error', fail); // when supported
+ // when error event is not supported fall back to timer
+ me.timer(15, 1000, 0, function () {
+ return scriptTag.id == 'loaded';
+ }, function () {
+ if (scriptTag.id != 'loaded') {
+ fail();
+ }
+ });
+ scriptTag.src = url;
+ setTimeout(function () {
+ try {
+ headTag.appendChild(scriptTag);
+ } catch (e) {
+ fail();
+ }
+ }, 1);
+ }, typeof args.delay == 'number' ? args.delay : 1);
+ return scriptTag;
+ }
+ };
+
+ function loadFiles(repo, pkg, branch, css) {
+ var version = branch || pkg.version;
+ var cdnURL = this.config.cdnURL + '/' + repo;
+
+ //Load .css file
+ if (css) {
+ var cssURL = version === 'master' ? cdnURL + '/' + css : cdnURL + '@' + version + '/' + css;
+ var link = document.createElement('link');
+ link.href = cssURL;
+ link.type = 'text/css';
+ link.rel = 'stylesheet';
+ document.getElementsByTagName('head')[0].appendChild(link);
+ }
+
+ //Load .js file
+ var jsURL = version === 'master' ? cdnURL + '/' + pkg.main.replace(/^\.?\/?/, '') : cdnURL + '@' + version + '/' + pkg.main.replace(/^\.?\/?/, '');
+
+ var loader = new scriptLoader();
+ var script = loader.require(jsURL, {
+ async: true,
+ success: function success() {
+ console.log('Loaded ' + jsURL + '.');
+ },
+ failure: function failure() {
+ console.warn('Failed to load ' + jsURL + '.');
+ }
+ });
+ }
+
+ function updateFields(version) {
+ var _this = this;
+
+ this.controls.mainFunction.node().value = this.chartingApplication.main;
+ this.controls.subFunction.node().value = this.chartingApplication.sub;
+ this.controls.schema.node().value = this.chartingApplication.schema;
+ this.controls.dataFileSelect.selectAll('option').property('selected', function (d) {
+ return _this.chartingApplication.defaultData === d.label;
+ });
+ }
+
+ var utilities = {
+ getVersions: getVersions,
+ updateSelect: updateSelect,
+ loadPackageJSON: loadPackageJSON,
+ loadFiles: loadFiles,
+ scriptLoader: scriptLoader,
+ updateFields: updateFields
+ };
+
+ var defaultSettings = {
+ useServer: false,
+ rootURL: 'https://github.com/RhoInc',
+ dataURL: 'https://raw.githubusercontent.com/RhoInc/data-library/master/data/',
+ chartingLibrary: {
+ name: 'Webcharts',
+ css: 'css/webcharts.css',
+ versions: []
+ },
+ renderers: [],
+ dataFiles: []
+ };
+
+ function setDefaults() {
+ var _this = this;
+
+ this.config.useServer = this.config.useServer || defaultSettings.useServer;
+ this.config.rootURL = this.config.rootURL || defaultSettings.rootURL;
+ this.config.apiURL = this.config.rootURL.replace('github.com', 'api.github.com/repos');
+ this.config.cdnURL = this.config.rootURL.replace('github.com', 'cdn.jsdelivr.net/gh');
+ this.config.dataURL = this.config.dataURL || defaultSettings.dataURL;
+ this.config.chartingLibrary = this.config.chartingLibrary || defaultSettings.chartingLibrary;
+ this.config.renderers = this.config.renderers || defaultSettings.renderers;
+ this.config.dataFiles = this.config.dataFiles || defaultSettings.dataFiles;
+
+ this.config.renderers.forEach(function (renderer) {
+ renderer.label = renderer.label || renderer.name;
+ });
+
+ this.config.dataFiles = this.config.dataFiles.map(function (df) {
+ return typeof df === 'string' ? { label: df, path: _this.config.dataURL, user_loaded: false } : df;
+ });
+ }
+
+ function toggleDisplayOfControls() {
+ var _this = this;
+
+ var styleSheet = Array.from(document.styleSheets).find(function (styleSheet) {
+ return styleSheet.href.indexOf('cat.css') > -1;
+ });
+ var controlsWidth = Array.from(styleSheet.cssRules).find(function (cssRule) {
+ return cssRule.selectorText === '.cat-wrap .cat-controls';
+ }).style.width;
+
+ //Hide controls.
+ this.hideControls.on('click', function () {
+ _this.controls.wrap.classed('hidden', true);
+ _this.chartWrap.style('margin-left', 0);
+ _this.chartWrap.selectAll('.wc-chart').each(function (d) {
+ try {
+ d.draw();
+ } catch (error) {}
+ });
+ _this.dataWrap.style('margin-left', 0);
+ _this.hideControls.classed('hidden', true);
+ _this.showControls.classed('hidden', false);
+ });
+
+ //Show controls.
+ this.showControls.on('click', function () {
+ _this.controls.wrap.classed('hidden', false);
+ _this.chartWrap.style('margin-left', controlsWidth);
+ _this.chartWrap.selectAll('.wc-chart').each(function (d) {
+ try {
+ d.draw();
+ } catch (error) {}
+ });
+ _this.dataWrap.style('margin-left', controlsWidth);
+ _this.hideControls.classed('hidden', false);
+ _this.showControls.classed('hidden', true);
+ });
+ }
+
+ function renderChart() {
+ this.controls.submitWrap = this.controls.wrap.append('div').classed('control-section submit-section', true);
+ this.controls.submitButton = this.controls.submitWrap.append('button').attr('class', 'submit').text('Render Chart');
+ }
+
+ function library() {
+ this.controls.rendererWrap.append('span').text('Library: ');
+ this.controls.rendererSelect = this.controls.rendererWrap.append('select');
+ this.controls.rendererWrap.append('br');
+ }
+
+ function version() {
+ this.controls.rendererWrap.append('span').text('Version: ');
+ this.controls.versionSelect = this.controls.rendererWrap.append('select');
+ this.controls.rendererWrap.append('br');
+ }
+
+ function init() {
+ this.controls.rendererWrap.append('span').text(' Init: ');
+ //.classed('hidden', true);
+ this.controls.mainFunction = this.controls.rendererWrap.append('input'); //.classed('hidden', true);
+ this.controls.rendererWrap.append('span').text('.');
+ //.classed('hidden', true);
+ this.controls.subFunction = this.controls.rendererWrap.append('input'); //.classed('hidden', true);
+ this.controls.rendererWrap.append('br'); //.classed('hidden', true);
+ }
+
+ function webchartsVersion() {
+ this.controls.rendererWrap.append('span').text('Webcharts Version: ');
+ //.classed('hidden', true);
+ this.controls.libraryVersion = this.controls.rendererWrap.append('select');
+ //.classed('hidden', true);
+ this.controls.rendererWrap.append('br'); //.classed('hidden', true);
+ }
+
+ function schema() {
+ this.controls.rendererWrap.append('span').text('Schema: ');
+ //.classed('hidden', true);
+ this.controls.schema = this.controls.rendererWrap.append('input'); //.classed('hidden', true);
+ this.controls.rendererWrap.append('br'); //.classed('hidden', true);
+ }
+
+ function chooseAChartingLibrary() {
+ this.controls.rendererWrap = this.controls.wrap.append('div').classed('control-section renderer-section', true);
+ this.controls.rendererWrap.append('h3').text('1. Choose a Charting Library');
+ library.call(this);
+ version.call(this);
+ //moreOptions.call(this);
+ init.call(this);
+ schema.call(this);
+ webchartsVersion.call(this);
+ }
+
+ function dataFile() {
+ this.controls.dataFileSelect = this.controls.dataWrap.append('select');
+ }
+
+ function loadADataFile() {
+ var loadLabel = this.controls.dataWrap.append('p').style('margin', 0);
+ loadLabel.append('small').text('Use local .csv file:').append('sup').html('ⓘ').property('title', 'Render a chart using a local file. File is added to the data set list, and is only available for a single session and is not saved.').style('cursor', 'help');
+ this.controls.loadStatus = loadLabel.append('small').attr('class', 'loadStatus').style('float', 'right').text('Select a csv to load');
+ this.controls.dataFileLoad = this.controls.dataWrap.append('input').attr('type', 'file').attr('class', 'file-load-input');
+ this.controls.dataFileLoadButton = this.controls.dataWrap.append('button').text('Load').attr('class', 'file-load-button').attr('disabled', true);
+ }
+
+ function chooseADataset() {
+ this.controls.dataWrap = this.controls.wrap.append('div').classed('control-section data-section', true);
+ this.controls.dataWrap.append('h3').text('2. Choose a Dataset');
+ dataFile.call(this);
+ this.controls.viewData = this.controls.dataWrap.append('span').html('🔍').style('cursor', 'pointer');
+ loadADataFile.call(this);
+ }
+
+ function customizeTheChart() {
+ this.controls.settingsWrap = this.controls.wrap.append('div').classed('control-section settings-section', true);
+ this.controls.settingsWrap.append('h3').html('3. Customize the Chart ');
+ this.controls.settingsWrap.append('span').text('Settings: ');
+ this.controls.settingsTypeText = this.controls.settingsWrap.append('input').attr('class', 'radio').property('type', 'radio').property('name', 'settingsType').property('value', 'text');
+ this.controls.settingsWrap.append('span').text('text');
+ this.controls.settingsTypeForm = this.controls.settingsWrap.append('input').attr('class', 'radio').property('type', 'radio').property('name', 'settingsType').property('value', 'form');
+ this.controls.settingsWrap.append('span').text('form');
+ this.controls.settingsType = this.controls.settingsWrap.selectAll('input[type="radio"]');
+ this.controls.settingsWrap.append('br');
+ this.controls.settingsInput = this.controls.settingsWrap.append('textarea').attr('rows', 10).style('width', '90%').text('{}');
+ this.controls.settingsForm = this.controls.settingsWrap.append('div').attr('class', 'settingsForm').append('form');
+ }
+
+ function environment() {
+ this.controls.environmentWrap = this.controls.wrap.append('div').classed('control-section environment-section', true);
+ this.controls.environmentWrap.append('h3').html('4. Environment ');
+ this.controls.cssList = this.controls.environmentWrap.append('ul').attr('class', 'cssList');
+ this.controls.cssList.append('h5').text('Loaded Stylesheets');
+ this.controls.jsList = this.controls.environmentWrap.append('ul').attr('class', 'jsList');
+ this.controls.jsList.append('h5').text('Loaded javascript');
+ }
+
+ function controls() {
+ this.controls.wrap.append('h2').classed('cat-controls-header', true).text('Charting Application Tester 😼');
+ renderChart.call(this);
+ chooseAChartingLibrary.call(this);
+ chooseADataset.call(this);
+ customizeTheChart.call(this);
+ environment.call(this);
+ }
+
+ function layout() {
+ this.wrap = d3.select(this.element).append('div').attr('class', 'cat-wrap');
+
+ //Controls display toggle
+ this.hideControls = this.wrap.append('div').classed('cat-button cat-button--hide-controls', true).attr('title', 'Hide controls').text('<<');
+ this.showControls = this.wrap.append('div').classed('cat-button cat-button--show-controls hidden', true).attr('title', 'Show controls').text('>>');
+ toggleDisplayOfControls.call(this);
+
+ //Controls
+ this.controls.wrap = this.wrap.append('div').classed('cat-controls section', true);
+ controls.call(this);
+
+ //Chart
+ this.chartWrap = this.wrap.append('div').classed('cat-chart section', true);
+
+ //Table
+ this.dataWrap = this.wrap.append('div').classed('cat-data section', true).classed('hidden', true);
+ }
+
+ function loadData() {
+ this.utilities.updateSelect.call(this, this.controls.rendererSelect, this.config.renderers);
+ this.utilities.updateSelect.call(this, this.controls.dataFileSelect, this.config.dataFiles);
+ }
+
+ function loadChartingLibrary() {
+ var _this = this;
+
+ this.utilities.getVersions.call(this, this.config.chartingLibrary.name).then(function (versions) {
+ _this.config.chartingLibrary.versions = versions.map(function (version) {
+ version.label = versions.tag_name ? version.tag_name : version.name;
+ return version;
+ });
+ _this.utilities.updateSelect.call(_this, _this.controls.libraryVersion, _this.config.chartingLibrary.versions);
+ });
+ this.utilities.loadPackageJSON.call(this, this.config.chartingLibrary.name, 'master').then(function (pkg) {
+ _this.config.chartingLibrary.pkg = JSON.parse(pkg);
+ _this.utilities.loadFiles.call(_this, _this.config.chartingLibrary.name, _this.config.chartingLibrary.pkg, 'master', _this.config.chartingLibrary.css);
+ });
+ }
+
+ function loadChartingApplication() {
+ var _this = this;
+
+ this.chartingApplication = this.config.renderers[0];
+ this.utilities.getVersions.call(this, this.chartingApplication.name).then(function (versions) {
+ _this.chartingApplication.versions = versions.map(function (version) {
+ version.label = versions.tag_name ? version.tag_name : version.name;
+ return version;
+ });
+ _this.utilities.updateSelect.call(_this, _this.controls.versionSelect, _this.chartingApplication.versions);
+ });
+ this.utilities.loadPackageJSON.call(this, this.chartingApplication.name, 'master').then(function (pkg) {
+ _this.chartingApplication.pkg = JSON.parse(pkg);
+ _this.utilities.loadFiles.call(_this, _this.chartingApplication.name, _this.chartingApplication.pkg, 'master', _this.chartingApplication.css);
+ _this.utilities.updateFields.call(_this, _this.chartingApplication.main, _this.chartingApplication.sub, _this.chartingApplication.schema);
+ });
+ }
+
+ function destroyChart() {
+ if (this.chartingApplicationInstance) {
+ if (this.chartingApplicationInstance && this.chartingApplicationInstance.destroy) {
+ if (this.chartingApplicationInstance && this.chartingApplicationInstance.destroy) this.chartingApplicationInstance.destroy();
+ } else {
+ this.chartWrap.selectAll('.wc-chart').each(function (chart) {
+ if (chart.destroy) chart.destroy();else {
+ //remove resize event listener
+ select(window).on('resize.' + chart.element + chart.id, null);
+
+ //destroy controls
+ if (chart.controls) {
+ chart.controls.destroy();
+ }
+
+ //unmount chart wrapper
+ chart.wrap.remove();
+ }
+ });
+ }
+ }
+
+ this.chartWrap.selectAll('*').remove();
+ }
+
+ function loadData$1() {
+ var dataFile = this.controls.dataFileSelect.node().value;
+ this.dataObject = this.config.dataFiles.find(function (f) {
+ return f.label == dataFile;
+ });
+ this.dataObject.dataFilePath = this.dataObject.path + dataFile;
+ return fetch(this.dataObject.dataFilePath).then(function (response) {
+ return response.text();
+ }).then(function (text) {
+ return d3.csv.parse(text);
+ });
+ }
+
+ function initializeChart(data) {
+ this.chartWrap.append('div').attr('class', 'chart');
+
+ //Pass element and settings to charting application.
+ if (this.chartingApplication.sub) {
+ this.chartingApplicationInstance = window[this.chartingApplication.main][this.chartingApplication.sub]('.cat-chart .chart', this.chartingApplication.config || {});
+ } else {
+ this.chartingApplicationInstance = window[this.chartingApplication.main]('.cat-chart .chart', this.chartingApplication.config || {});
+ }
+
+ //Pass data to charting application.
+ try {
+ this.chartingApplicationInstance.init(data);
+ } catch (err) {
+ console.warn(err);
+ }
+ }
+
+ /*
+ 1. Destroys the currently displayed chart if one has been rendered.
+ 2. Loads the selected data file.
+ 3. Initializes the selected charting application library.
+ */
+
+ function renderChart$1() {
+ var _this = this;
+
+ this.controls.submitButton.on('click', function () {
+ _this.dataWrap.classed('hidden', true);
+ _this.chartWrap.classed('hidden', false);
+ destroyChart.call(_this);
+ loadData$1.call(_this).then(function (json) {
+ initializeChart.call(_this, json);
+ });
+ });
+ }
+
+ function changeLibrary() {
+ var cat = this;
+
+ this.controls.rendererSelect.on('change', function (d) {
+ cat.chartingApplication = d3.select(this).selectAll('option:checked').datum();
+ cat.chartingApplication.version = 'master';
+ cat.controls.versionSelect.selectAll('option').property('selected', function (d) {
+ return d.label === cat.chartingApplication.version;
+ });
+ cat.utilities.getVersions.call(cat, cat.chartingApplication.name).then(function (versions) {
+ cat.chartingApplication.versions = versions.map(function (version) {
+ version.label = versions.tag_name ? version.tag_name : version.name;
+ return version;
+ });
+ cat.utilities.updateSelect.call(cat, cat.controls.versionSelect, cat.chartingApplication.versions);
+ });
+ cat.utilities.loadPackageJSON.call(cat, cat.chartingApplication.name, 'master').then(function (pkg) {
+ cat.chartingApplication.pkg = JSON.parse(pkg);
+ cat.utilities.loadFiles.call(cat, cat.chartingApplication.name, cat.chartingApplication.pkg, 'master', cat.chartingApplication.css);
+ cat.utilities.updateFields.call(cat, cat.chartingApplication.main, cat.chartingApplication.sub, cat.chartingApplication.schema);
+ });
+ });
+ }
+
+ function changeLibraryVersion() {
+ var cat = this;
+
+ this.controls.versionSelect.on('change', function (d) {
+ cat.chartingApplication.version = d3.select(this).selectAll('option:checked').datum().label;
+ cat.utilities.loadPackageJSON.call(cat, cat.chartingApplication.name, cat.chartingApplication.version).then(function (pkg) {
+ cat.chartingApplication.pkg = JSON.parse(pkg);
+ cat.utilities.loadFiles.call(cat, cat.chartingApplication.name, cat.chartingApplication.pkg, cat.chartingApplication.version, cat.chartingApplication.css);
+ cat.utilities.updateFields.call(cat, cat.chartingApplication.main, cat.chartingApplication.sub, cat.chartingApplication.schema);
+ });
+ });
+ }
+
+ function initializeControls() {
+ renderChart$1.call(this);
+ changeLibrary.call(this);
+ changeLibraryVersion.call(this);
+ }
+
+ function init$1() {
+ //settings
+ setDefaults.call(this);
+
+ //layout
+ layout.call(this);
+
+ //renderers and data files
+ loadData.call(this);
+
+ //load charting library
+ loadChartingLibrary.call(this);
+
+ //load charting application
+ loadChartingApplication.call(this);
+
+ //initialize controls
+ initializeControls.call(this);
+ }
+
+ //import controls from './cat/controls';
+ //import settings from './cat/settings';
+ //import status from './cat/status';
+
+ function createCat() {
+ var element = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'body';
+ var config = arguments[1];
+
+ var cat = {
+ element: element,
+ config: config,
+ utilities: utilities,
+ init: init$1,
+ controls: {}
+ //settings,
+ //status
+ };
+
+ return cat;
+ }
+
+ var index = {
+ createCat: createCat
+ };
+
+ return index;
+
+})));
diff --git a/css/cat.css b/css/cat.css
index 687ef60..b6ab4b9 100644
--- a/css/cat.css
+++ b/css/cat.css
@@ -1,44 +1,51 @@
/* general */
-.hidden{
- display:none
+.hidden {
+ display: none
}
-.cat-wrap .cat-controls{
- position: relatvie;
- width:20%;
+.cat-wrap {
+ position: relative;
+}
+
+.cat-wrap .cat-controls {
+ width: 25%;
top: 0;
- bottom:0;
- position:fixed;
- overflow-y:scroll;
- overflow-x:scroll;
- background:#99badd;
- padding:0.5em;
- margin:0;
- border:1px solid #999;
-}
-
-.cat-wrap .cat-chart, .cat-wrap .cat-data{
- margin-left:20%;
+ bottom: 0;
+ position: fixed;
+ overflow-y: scroll;
+ overflow-x: scroll;
+ background: #99badd;
+ padding: 0.5em;
+ margin: 0;
+ border: 1px solid #999;
+}
+
+.cat-wrap .cat-chart, .cat-wrap .cat-data {
+ margin-left: 25%;
padding: 0 3em;
}
-.cat-wrap .cat-controls.hidden{
- display:none;
+.cat-wrap .cat-controls.hidden {
+ display: none;
+}
+
+.cat-wrap .cat-controls .cat-controls-header {
+ text-align: center;
}
-.cat-wrap .cat-controls h2{
- margin-top:0.1em;
- margin-bottom:0.2em;
- font-size:1.3em;
+.cat-wrap .cat-controls h2 {
+ margin-top: 0.1em;
+ margin-bottom: 0.2em;
+ font-size: 1.3em;
}
-.cat-wrap .cat-controls h3{
- margin-bottom:0;
- margin-top:0.2em;
+.cat-wrap .cat-controls h3 {
+ margin-bottom: 0;
+ margin-top: 0.2em;
}
-.cat-wrap .cat-controls div{
- margin-bottom:.5em;
+.cat-wrap .cat-controls div {
+ margin-bottom: .5em;
}
.cat-wrap .submit-section {
@@ -47,45 +54,49 @@
position: relative;
}
.cat-wrap .cat-button {
- position: absolute;
- left: 0;
- top: 5px;
+ position: fixed;
+ top: 10px;
+ left: 12px;
vertical-align: middle;
background: white;
cursor: pointer;
padding: 0 5px;
- font-size:1em;
- border:2px solid #7BAFD4;
+ font-size: 1em;
+ border: 2px solid #7BAFD4;
border-radius: 2px;
}
-.cat-wrap .cat-button--maximize {
- left: .75em;
- top: 10px;
+.cat-wrap .cat-button:hover {
+ background: #7BAFD4;
+ border: 2px solid black;
}
-.cat-wrap .cat-controls button.submit{
+.cat-wrap .cat-controls button.submit {
cursor: pointer;
margin: 0 auto;
- display:block;
- color:black;
- border:none;
- border-radius:3px;
+ display: block;
+ color: black;
+ border: none;
+ border-radius: 3px;
padding: 5px 10px;
- font-size:1em;
- background-color:white;
- border:2px solid #7BAFD4;
+ font-size: 1em;
+ background-color: white;
+ border: 2px solid #7BAFD4;
+}
+
+.cat-wrap .cat-controls button.submit:hover {
+ background-color: #7BAFD4;
+ color: white;
}
-.cat-wrap .cat-controls button.submit:hover{
- background-color:#7BAFD4;
- color:white;
+.cat-wrap .cat-controls .data-section select {
+ max-width: 100%;
}
-.cat-wrap .cat-controls .file-load-input{
- width:75%
+.cat-wrap .cat-controls .data-section .file-load-input {
+ width: 75%
}
-.cat-wrap .cat-controls .file-load-button{
- width:25%
+.cat-wrap .cat-controls .data-section .file-load-button {
+ width: 25%
}
@@ -96,16 +107,16 @@
rows: 10;
width: 100%;
}
-.cat-wrap .cat-controls .settings-section .prop-value{
- vertical-align:top;
- padding-bottom:0.1em;
+.cat-wrap .cat-controls .settings-section .prop-value {
+ vertical-align: top;
+ padding-bottom: 0.1em;
}
/* status formatting */
-.cat-wrap .cat-chart .status div{
+.cat-wrap .cat-chart .status div {
padding: 3px;
margin-bottom: 4px;
border: 1px solid transparent;
@@ -114,48 +125,48 @@
background-color: #dff0d8;
border-color: #d6e9c6;
}
-.cat-wrap .cat-chart .status{
- padding-bottom:.5em;
- margin-bottom:.5em;
- border-bottom:1px dotted #999;
+.cat-wrap .cat-chart .status {
+ padding-bottom: .5em;
+ margin-bottom: .5em;
+ border-bottom: 1px dotted #999;
}
-.cat-wrap .cat-chart .status div.error{
+.cat-wrap .cat-chart .status div.error {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
-.cat-wrap .cat-chart .status div.info{
+.cat-wrap .cat-chart .status div.info {
color: black;
background-color: white;
border-color: black;
}
-.cat-wrap .cat-chart .status div.export{
- font-size:0.6em;
+.cat-wrap .cat-chart .status div.export {
+ font-size: 0.6em;
color: black;
background-color: #ffffe5;
border-color: black;
}
-.cat-wrap .cat-chart .status div.export.minimized{
- font-size:1.0em;
+.cat-wrap .cat-chart .status div.export.minimized {
+ font-size: 1.0em;
}
-.cat-wrap .environment-section ul{
+.cat-wrap .environment-section ul {
list-style: none;
padding-left: 0.1em;
}
-.cat-wrap .environment-section ul h5{
- margin:0;
+.cat-wrap .environment-section ul h5 {
+ margin: 0;
}
/**********************************************************************
** Sliders from https://www.w3schools.com/howto/howto_css_switch.asp **
**********************************************************************/
/**/
.switch {
- margin-left:0.5em;
+ margin-left: 0.5em;
position: relative;
display: inline-block;
width: 30px;
@@ -166,7 +177,7 @@
}
/* Hide default HTML checkbox */
-.switch input {display:none;}
+.switch input {display: none;}
/* The slider */
.slider {
diff --git a/index.html b/index.html
index 4b9e1f8..aada08e 100644
--- a/index.html
+++ b/index.html
@@ -11,6 +11,7 @@
+
@@ -24,6 +25,7 @@
+
diff --git a/index.js b/index.js
index 573c7cd..692a193 100644
--- a/index.js
+++ b/index.js
@@ -1,7 +1,6 @@
-var myCatConfig = {
+const myCatConfig = {
useServer: false,
- rootURL: 'https://cdn.jsdelivr.net/gh/RhoInc',
- dataURL: 'https://raw.githubusercontent.com/RhoInc/data-library/master/data/',
+ repoURL: '../graphics/data',
renderers: [
{
name: 'web-codebook',
@@ -82,13 +81,13 @@ var myCatConfig = {
defaultData: 'clinical-trials/renderer-specific/adbds.csv'
},
{
- name: 'safety-eDISH',
+ name: 'hep-explorer',
main: 'safetyedish',
sub: null,
css: null,
schema: 'settings-schema.json',
defaultData: 'clinical-trials/renderer-specific/adbds.csv',
- rootURL: 'https://cdn.jsdelivr.net/gh/ASA-DIA-InteractiveSafetyGraphics',
+ rootURL: 'https://cdn.jsdelivr.net/gh/SafetyGraphics',
},
/**-------------------------------------------------------------------------------------------\
@@ -135,10 +134,38 @@ var myCatConfig = {
]
};
-myCatConfig.dataFiles = dataFiles.map(function(m){
- return m.rel_path.slice(7)
-});
+//Modify renderer objects.
+//myCatConfig.renderers
+// .forEach(function(renderer) {
+// renderer.rootURL = renderer.rootURL || myCatConfig.rootURL;
+// renderer.api_url = renderer.rootURL.replace('cdn.jsdelivr.net/gh', 'api.github.com/repos') + '/' + renderer.name;
+// renderer.branches_api_url = renderer.api_url + '/branches';
+// renderer.releases_api_url = renderer.api_url + '/releases';
+// });
-var myCat = cat.createCat('body',myCatConfig)
+//Map data file objects to a path relative to myCatConfig.dataURL.
+myCatConfig.dataFiles = dataFiles
+ .map(function(dataFile) {
+ return dataFile.rel_path.slice(7);
+ });
-myCat.init()
+//Instantiate CAT.
+const myCat = cat.createCat('body',myCatConfig);
+
+//Read in repository data from graphics repo.
+Promise
+ .all([
+ fetch(`${myCat.config.repoURL}/repos.json`),
+ fetch(`${myCat.config.repoURL}/releases.json`),
+ fetch(`${myCat.config.repoURL}/branches.json`),
+ ])
+ .then(responses => Promise.all(responses.map(response => response.json())))
+ .then(json => {
+ const [repos,releases,branches] = json;
+ myCat.repos = repos;
+ myCat.releases = releases;
+ myCat.branches = branches;
+
+ //Initialize CAT.
+ myCat.init();
+ });
diff --git a/package-lock.json b/package-lock.json
index 662c99c..c1b0f54 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,9 +1,23 @@
{
"name": "cat",
- "version": "0.9.0",
+ "version": "0.10.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+ "@handsontable/formulajs": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@handsontable/formulajs/-/formulajs-2.0.1.tgz",
+ "integrity": "sha512-jTdJO/6ZmuaHoiTdnraGbPkdnA7m0VMrZ54vWXi22WpwnsIKAWbqjWTwvDoSuEpcc7/YHVIVlSDtfXHKmaYhdQ==",
+ "requires": {
+ "@handsontable/jstat": "^1.0.0",
+ "bessel": "^0.2.0"
+ }
+ },
+ "@handsontable/jstat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@handsontable/jstat/-/jstat-1.0.0.tgz",
+ "integrity": "sha512-5XxZ9xIk6iSjrc1p5N/yI2dofBXp0IzZVgrkETDC196SxoJCRNOeKgM9fTHMhoxa02wuaZPLp6stojlppNxP/A=="
+ },
"@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
@@ -16,6 +30,14 @@
"integrity": "sha512-i1sl+WCX2OCHeUi9oi7PiCNUtYFrpWhpcx878vpeq/tlZTKzcFdHePlyFHVbWqeuKN0SRPl/9ZFDSTsfv9h7VQ==",
"dev": true
},
+ "@types/pikaday": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@types/pikaday/-/pikaday-1.6.0.tgz",
+ "integrity": "sha512-cnKjF7i6oA1ADxQdSWHcEStLZeiH8qbf6l7B9O88PhLgnmbUMM62ali0/PaDtINm6ezpNcqtERWL6Y+pAgHKQQ==",
+ "requires": {
+ "moment": ">=2.14.0"
+ }
+ },
"abab": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
@@ -754,6 +776,14 @@
"tweetnacl": "^0.14.3"
}
},
+ "bessel": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/bessel/-/bessel-0.2.0.tgz",
+ "integrity": "sha1-E8s5zSkjMhnsLacl4LoMZvtGtvI=",
+ "requires": {
+ "voc": "^1.1.0"
+ }
+ },
"bignumber.js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz",
@@ -1408,6 +1438,26 @@
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
"dev": true
},
+ "handsontable": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/handsontable/-/handsontable-7.0.3.tgz",
+ "integrity": "sha512-CRwrI6VFcNhTSiTtIrLhdofuOBb9itNmbUsl4ntlu61qnEpyRBqT7Vv5wT+icRtBlq4czlYIWKMz8GGZe3e2ow==",
+ "requires": {
+ "@types/pikaday": "1.6.0",
+ "core-js": "^3.0.0",
+ "hot-formula-parser": "^3.0.1",
+ "moment": "2.20.1",
+ "numbro": "2.1.1",
+ "pikaday": "1.5.1"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.1.2.tgz",
+ "integrity": "sha512-3poRGjbu56leCtZCZCzCgQ7GcKOflDFnjWIepaPFUsM0IXUBrne10sl3aa2Bkcz3+FjRdIxBe9dAMhIJmEnQNA=="
+ }
+ }
+ },
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -1443,6 +1493,15 @@
"os-tmpdir": "^1.0.1"
}
},
+ "hot-formula-parser": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/hot-formula-parser/-/hot-formula-parser-3.0.1.tgz",
+ "integrity": "sha512-QhYPVlVh/GF/hHtBp+MwgDp5kpgrrjeJi3d3/GxTWtqwLBOOM4KlZT/YWcsfZj5JE68MNvFgj3ZzYpkGyvGtwA==",
+ "requires": {
+ "@handsontable/formulajs": "^2.0.1",
+ "tiny-emitter": "^2.0.1"
+ }
+ },
"http-errors": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
@@ -1590,9 +1649,9 @@
"dev": true
},
"jquery": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz",
- "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg=="
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
+ "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
},
"js-tokens": {
"version": "3.0.2",
@@ -1852,6 +1911,11 @@
"minimist": "0.0.8"
}
},
+ "moment": {
+ "version": "2.20.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz",
+ "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg=="
+ },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -1877,6 +1941,21 @@
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true
},
+ "numbro": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/numbro/-/numbro-2.1.1.tgz",
+ "integrity": "sha512-H3VamlHyqYYomNngAbrl/CT92DnOSC2rJxx6hfZrgj0NVnqxAtOvGbwgpOYjv4ASgxodDWBSYHJ1ZxaEq2lfTg==",
+ "requires": {
+ "bignumber.js": "^4.0.4"
+ },
+ "dependencies": {
+ "bignumber.js": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz",
+ "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA=="
+ }
+ }
+ },
"nwmatcher": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.3.tgz",
@@ -2048,6 +2127,14 @@
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true
},
+ "pikaday": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.5.1.tgz",
+ "integrity": "sha1-CkhUm8GhTqHQjEQHTXYbwvK/z9M=",
+ "requires": {
+ "moment": "2.x"
+ }
+ },
"pixelmatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
@@ -2584,6 +2671,11 @@
"integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
"dev": true
},
+ "tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+ },
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
diff --git a/package.json b/package.json
index c533dc1..8dada60 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cat",
- "version": "0.9.1",
+ "version": "0.10.0",
"description": "The Charting Application Tester (CAT) lets users make and adjust web graphics on the fly.",
"module": "./src/index.js",
"main": "./build/cat.js",
@@ -12,17 +12,18 @@
"format-bundle": "prettier --print-width=100 --tab-width=4 --single-quote --write ./build/cat.js",
"watch": "rollup -c -w",
"start": "node server/app.js",
- "initServer": "node server/initServer.js"
+ "init-server": "node server/initServer.js"
},
"author": "Rho, Inc.",
"license": "MIT",
"dependencies": {
+ "body-parser": "~1",
"d3": "^3.5.14",
- "webcharts": "~1",
- "jquery": "~3",
- "json5": "^0.5.1",
"express": "~4",
- "body-parser": "~1"
+ "handsontable": "^7.0.3",
+ "jquery": "^3.4.1",
+ "json5": "^0.5.1",
+ "webcharts": "~1"
},
"devDependencies": {
"babel-plugin-external-helpers": "^6.22.0",
diff --git a/src/cat/controls.js b/src/cat/controls.js
index 27701fc..ff182aa 100644
--- a/src/cat/controls.js
+++ b/src/cat/controls.js
@@ -5,7 +5,7 @@
import { init } from './controls/init';
import addEnterEventListener from './addEnterEventListener';
-export const controls = {
+export default {
init,
addEnterEventListener
};
diff --git a/src/cat/controls/init.js b/src/cat/controls/init.js
index 4774b73..c212131 100644
--- a/src/cat/controls/init.js
+++ b/src/cat/controls/init.js
@@ -5,13 +5,13 @@ import { initFileLoad } from './initFileLoad';
import { initChartConfig } from './initChartConfig';
import { initEnvConfig } from './initEnvConfig';
-export function init(cat) {
- cat.current = cat.config.renderers[0];
- cat.current.version = 'master';
- initSubmit(cat);
- initRendererSelect(cat);
- initDataSelect(cat);
- initFileLoad.call(cat);
- initChartConfig(cat);
- initEnvConfig(cat);
+export function init() {
+ this.current = this.config.renderers[0];
+ this.current.version = 'master';
+ initSubmit.call(this);
+ initRendererSelect.call(this);
+ initDataSelect.call(this);
+ initFileLoad.call(this);
+ initChartConfig.call(this);
+ initEnvConfig.call(this);
}
diff --git a/src/cat/controls/initChartConfig.js b/src/cat/controls/initChartConfig.js
index 0b3352f..fac82a1 100644
--- a/src/cat/controls/initChartConfig.js
+++ b/src/cat/controls/initChartConfig.js
@@ -1,40 +1,7 @@
-export function initChartConfig(cat) {
- var settingsHeading = cat.controls.settingsWrap.append('h3').html('3. Customize the Chart ');
+export function initChartConfig() {
+ const cat = this;
- cat.controls.settingsWrap.append('span').text('Settings: ');
-
- /*
- //////////////////////////////////////
- //initialize the config status icon
- //////////////////////////////////////
- cat.controls.settingsStatus = settingsSection
- .append("div")
- .style("font-size", "1.5em")
- .style("float", "right")
- .style("cursor", "pointer");
- settingsSection.append("br");
-*/
-
- //////////////////////////////////////////////////////////////////////
- //radio buttons to toggle between "text" and "form" based settings
- /////////////////////////////////////////////////////////////////////
- cat.controls.settingsTypeText = cat.controls.settingsWrap
- .append('input')
- .attr('class', 'radio')
- .property('type', 'radio')
- .property('name', 'settingsType')
- .property('value', 'text');
- cat.controls.settingsWrap.append('span').text('text');
- cat.controls.settingsTypeForm = cat.controls.settingsWrap
- .append('input')
- .attr('class', 'radio')
- .property('type', 'radio')
- .property('name', 'settingsType')
- .property('value', 'form');
- cat.controls.settingsWrap.append('span').text('form');
- cat.controls.settingsType = cat.controls.settingsWrap.selectAll('input[type="radio"]');
-
- cat.controls.settingsType.on('change', function(d) {
+ this.controls.settingsType.on('change', function(d) {
cat.settings.sync(cat); //first sync the current settings to both views
//then update to the new view, and update controls.
@@ -47,25 +14,6 @@ export function initChartConfig(cat) {
cat.controls.settingsForm.classed('hidden', false);
}
});
- cat.controls.settingsWrap.append('br');
-
- //////////////////////////////////////////////////////////////////////
- //text input section
- /////////////////////////////////////////////////////////////////////
- cat.controls.settingsInput = cat.controls.settingsWrap
- .append('textarea')
- .attr('rows', 10)
- .style('width', '90%')
- .text('{}');
-
- //////////////////////////////////////////////////////////////////////
- //wrapper for the form
- /////////////////////////////////////////////////////////////////////
- cat.controls.settingsForm = cat.controls.settingsWrap
- .append('div')
- .attr('class', 'settingsForm')
- .append('form');
- //set the text/form settings for the first renderer
- cat.settings.set(cat);
+ this.settings.set(this);
}
diff --git a/src/cat/controls/initDataSelect.js b/src/cat/controls/initDataSelect.js
index f708266..53c88d3 100644
--- a/src/cat/controls/initDataSelect.js
+++ b/src/cat/controls/initDataSelect.js
@@ -1,24 +1,12 @@
import { showDataPreview } from '../datapreview/showDataPreview';
-export function initDataSelect(cat) {
- cat.controls.dataWrap.append('h3').text('2. Choose a data Set');
- cat.controls.dataFileSelect = cat.controls.dataWrap.append('select');
-
- cat.controls.dataWrap
- .append('span')
- .html('🔍')
- .style('cursor', 'pointer')
+export function initDataSelect() {
+ this.controls.viewData
.on('click', function() {
- showDataPreview(cat);
+ showDataPreview(this);
});
- cat.controls.dataFileSelect
+ this.controls.dataFileSelect
.selectAll('option')
- .data(cat.config.dataFiles)
- .enter()
- .append('option')
- .text(d => d.label)
- .property('selected', function(d) {
- return cat.current.defaultData == d.label ? true : null;
- });
+ .property('selected', d => this.current.defaultData === d);
}
diff --git a/src/cat/controls/initEnvConfig.js b/src/cat/controls/initEnvConfig.js
index a3c9562..b07c841 100644
--- a/src/cat/controls/initEnvConfig.js
+++ b/src/cat/controls/initEnvConfig.js
@@ -1,13 +1,5 @@
import { showEnv } from '../env/showEnv';
-export function initEnvConfig(cat) {
- var settingsHeading = cat.controls.environmentWrap.append('h3').html('4. Environment ');
-
- cat.controls.cssList = cat.controls.environmentWrap.append('ul').attr('class', 'cssList');
- cat.controls.cssList.append('h5').text('Loaded Stylesheets');
-
- cat.controls.jsList = cat.controls.environmentWrap.append('ul').attr('class', 'jsList');
- cat.controls.jsList.append('h5').text('Loaded javascript');
-
- showEnv(cat);
+export function initEnvConfig() {
+ showEnv(this);
}
diff --git a/src/cat/controls/initFileLoad.js b/src/cat/controls/initFileLoad.js
index 6bd9607..0a0f009 100644
--- a/src/cat/controls/initFileLoad.js
+++ b/src/cat/controls/initFileLoad.js
@@ -1,29 +1,7 @@
export function initFileLoad() {
- var cat = this;
- //draw the control
- var loadLabel = cat.controls.dataWrap.append('p').style('margin', 0);
+ const cat = this;
- loadLabel
- .append('small')
- .text('Use local .csv file:')
- .append('sup')
- .html('ⓘ')
- .property(
- 'title',
- 'Render a chart using a local file. File is added to the data set list, and is only available for a single session and is not saved.'
- )
- .style('cursor', 'help');
-
- var loadStatus = loadLabel
- .append('small')
- .attr('class', 'loadStatus')
- .style('float', 'right')
- .text('Select a csv to load');
-
- cat.controls.dataFileLoad = cat.controls.dataWrap
- .append('input')
- .attr('type', 'file')
- .attr('class', 'file-load-input')
+ this.controls.dataFileLoad
.on('change', function() {
if (this.value.slice(-4).toLowerCase() == '.csv') {
loadStatus.text(this.files[0].name + ' ready to load').style('color', 'green');
@@ -34,11 +12,7 @@ export function initFileLoad() {
}
});
- cat.controls.dataFileLoadButton = cat.controls.dataWrap
- .append('button')
- .text('Load')
- .attr('class', 'file-load-button')
- .attr('disabled', true)
+ this.controls.dataFileLoadButton
.on('click', function(d) {
//credit to https://jsfiddle.net/Ln37kqc0/
var files = cat.controls.dataFileLoad.node().files;
diff --git a/src/cat/controls/initRendererSelect.js b/src/cat/controls/initRendererSelect.js
index 3737446..0140424 100644
--- a/src/cat/controls/initRendererSelect.js
+++ b/src/cat/controls/initRendererSelect.js
@@ -1,74 +1,48 @@
-import updateRenderer from './initRendererSelect/updateRenderer';
+import unloadDOM from './initRendererSelect/unloadDOM';
+import loadPackageJSON from '../init/loadPackageJSON';
+import loadRenderer from '../init/loadRenderer';
+import getVersions from '../init/getVersions';
+import updateSettings from '../init/updateSettings';
-export function initRendererSelect(cat) {
- cat.controls.rendererWrap.append('h3').text('1. Choose a Charting Library');
- cat.controls.rendererWrap.append('span').text('Library: ');
+/*
+ 1. Removes the previous library's .js, .css, and/or stylesheet from the DOM.
+ 2. Updates the status section.
+ 3. Loads the master branch of the selected library.
+ 1. Loads the library's package.json file to know where the main .js file lives.
+ 2. Optionally loads the settings-schema.json file to populate the settings text/form if the library has a settings schema file.
+ 3. Loads the main .js file.
+ 4. Optionally loads the main .css file if the library has a .css file.
+ 4. Loads the branches and releases of the library.
+ 5. Updates the settings text/form.
+*/
- cat.controls.rendererSelect = cat.controls.rendererWrap.append('select');
- cat.controls.rendererSelect
+export function initRendererSelect() {
+ unloadDOM.call(this);
+ //updateStatus.call(this);
+
+ const cat = this;
+ this.controls.rendererSelect
.selectAll('option')
- .data(cat.config.renderers)
+ .data(this.config.renderers)
.enter()
.append('option')
.text(d => d.name);
-
- cat.controls.rendererSelect.on('change', function() {
+ this.controls.rendererSelect.on('change', function() {
updateRenderer.call(cat, this);
+ getVersions(cat.controls.versionSelect, cat.current.api_url);
});
- cat.controls.rendererWrap.append('br');
- cat.controls.rendererWrap.append('span').text('Version: ');
- cat.controls.versionSelect = cat.controls.rendererWrap.append('input');
- cat.controls.versionSelect.node().value = 'master';
- cat.controls.versionSelect.on('input', function() {
+ this.controls.versionSelect.on('change', function() {
+ console.log(this.value);
cat.current.version = this.value;
- });
- cat.controls.versionSelect.on('change', function() {
cat.settings.set(cat);
});
- cat.controls.rendererWrap.append('br');
-
- cat.controls.rendererWrap
- .append('a')
- .text('More Options')
- .style('text-decoration', 'underline')
- .style('color', 'blue')
- .style('cursor', 'pointer')
+ this.controls.moreOptions
.on('click', function() {
d3.select(this).remove();
cat.controls.rendererWrap.selectAll('*').classed('hidden', false);
});
-
- //specify the code to create the chart
- cat.controls.rendererWrap
- .append('span')
- .text(' Init: ')
- .classed('hidden', true);
- cat.controls.mainFunction = cat.controls.rendererWrap.append('input').classed('hidden', true);
- cat.controls.mainFunction.node().value = cat.current.main;
- cat.controls.rendererWrap
- .append('span')
- .text('.')
- .classed('hidden', true);
- cat.controls.subFunction = cat.controls.rendererWrap.append('input').classed('hidden', true);
- cat.controls.subFunction.node().value = cat.current.sub;
- cat.controls.rendererWrap.append('br').classed('hidden', true);
- //Webcharts versionSelect
- cat.controls.rendererWrap
- .append('span')
- .text('Webcharts Version: ')
- .classed('hidden', true);
- cat.controls.libraryVersion = cat.controls.rendererWrap.append('input').classed('hidden', true);
- cat.controls.libraryVersion.node().value = 'master';
- cat.controls.rendererWrap.append('br').classed('hidden', true);
-
- cat.controls.rendererWrap
- .append('span')
- .text('Schema: ')
- .classed('hidden', true);
- cat.controls.schema = cat.controls.rendererWrap.append('input').classed('hidden', true);
- cat.controls.schema.node().value = cat.current.schema;
- cat.controls.rendererWrap.append('br').classed('hidden', true);
-
- //add enter listener
- cat.controls.addEnterEventListener(cat.controls.rendererWrap, cat);
+ this.controls.mainFunction.node().value = this.current.main;
+ this.controls.subFunction.node().value = this.current.sub;
+ this.controls.schema.node().value = this.current.schema;
+ this.controls.addEnterEventListener(this.controls.rendererWrap, cat);
}
diff --git a/src/cat/controls/initRendererSelect/getVersions.js b/src/cat/controls/initRendererSelect/getVersions.js
new file mode 100644
index 0000000..ced9b2b
--- /dev/null
+++ b/src/cat/controls/initRendererSelect/getVersions.js
@@ -0,0 +1,27 @@
+export default function getVersions(
+ select,
+ repo = 'https://api.github.com/repos/RhoInc/Webcharts'
+) {
+ const branches = fetch(`${repo}/branches`).then(response => response.json());
+ const releases = fetch(`${repo}/releases`).then(response => response.json());
+
+ Promise.all([branches, releases])
+ .then(values => {
+ const [branches, releases] = values;
+ branches.sort(
+ (a, b) =>
+ a.name === 'master' ? -1 : b.name === 'master' ? 1 : a.name < b.name ? -1 : 1
+ );
+ select.selectAll('option').remove();
+ select
+ .selectAll('option')
+ .data(d3.merge(values))
+ .enter()
+ .append('option')
+ .text(d => d.tag_name || d.name)
+ .property('selected', d => d.name === 'master');
+ })
+ .catch(err => {
+ console.log(err);
+ });
+}
diff --git a/src/cat/controls/initRendererSelect/loadDOM.js b/src/cat/controls/initRendererSelect/loadDOM.js
new file mode 100644
index 0000000..da2dd4b
--- /dev/null
+++ b/src/cat/controls/initRendererSelect/loadDOM.js
@@ -0,0 +1,2 @@
+export default function loadDOM() {
+}
diff --git a/src/cat/controls/initRendererSelect/unloadDOM.js b/src/cat/controls/initRendererSelect/unloadDOM.js
new file mode 100644
index 0000000..93d8460
--- /dev/null
+++ b/src/cat/controls/initRendererSelect/unloadDOM.js
@@ -0,0 +1,14 @@
+export default function unloadDOM() {
+ d3
+ .selectAll('link')
+ .filter(function() {
+ return !this.href.indexOf('css/cat.css');
+ })
+ .property('disabled', true)
+ .remove();
+
+ d3
+ .selectAll('style')
+ .property('disabled', true)
+ .remove();
+}
diff --git a/src/cat/controls/initRendererSelect/updateRenderer.js b/src/cat/controls/initRendererSelect/updateRenderer.js
index d8fdc8e..127b716 100644
--- a/src/cat/controls/initRendererSelect/updateRenderer.js
+++ b/src/cat/controls/initRendererSelect/updateRenderer.js
@@ -1,4 +1,5 @@
export default function updateRenderer(select) {
+ this.previous = _.clone(this.current);
this.current = d3
.select(select)
.select('option:checked')
diff --git a/src/cat/controls/initSubmit.js b/src/cat/controls/initSubmit.js
index 535457c..e4c60e2 100644
--- a/src/cat/controls/initSubmit.js
+++ b/src/cat/controls/initSubmit.js
@@ -1,7 +1,24 @@
-import addControlsToggle from './initSubmit/addControlsToggle';
-import addSubmitButton from './initSubmit/addSubmitButton';
+import destroyChart from './initSubmit/destroyChart';
+import updateStatus from './initSubmit/updateStatus';
+import loadData from './initSubmit/loadData';
+import initializeChart from './initSubmit/initializeChart';
-export function initSubmit(cat) {
- addControlsToggle.call(cat);
- addSubmitButton.call(cat);
+/*
+ 1. Destroys the currently displayed chart if one has been rendered.
+ 2. Updates the status section.
+ 3. Loads the selected data file.
+ 4. Initializes the selected charting application library.
+*/
+
+export function initSubmit() {
+ this.controls.submitButton.on('click', () => {
+ this.dataWrap.classed('hidden', true);
+ this.chartWrap.classed('hidden', false);
+ destroyChart.call(this);
+ updateStatus.call(this);
+ loadData.call(this)
+ .then(json => {
+ initializeChart.call(this, json);
+ });
+ });
}
diff --git a/src/cat/controls/initSubmit/addControlsToggle.js b/src/cat/controls/initSubmit/addControlsToggle.js
deleted file mode 100644
index 205c369..0000000
--- a/src/cat/controls/initSubmit/addControlsToggle.js
+++ /dev/null
@@ -1,35 +0,0 @@
-export default function addControlsToggle() {
- const cat = this;
-
- this.controls.minimize = this.controls.submitWrap
- .append('div')
- .classed('cat-button cat-button--minimize hidden', true)
- .attr('title', 'Hide controls')
- .text('<<')
- .on('click', () => {
- this.controls.wrap.classed('hidden', true);
- this.chartWrap.style('margin-left', 0);
- this.chartWrap.selectAll('.wc-chart').each(function(d) {
- try {
- d.draw();
- } catch (error) {}
- });
- this.dataWrap.style('margin-left', 0);
- this.controls.maximize = this.wrap
- .insert('div', ':first-child')
- .classed('cat-button cat-button--maximize', true)
- .text('>>')
- .attr('title', 'Show controls')
- .on('click', function() {
- cat.controls.wrap.classed('hidden', false);
- cat.chartWrap.style('margin-left', '20%');
- cat.chartWrap.selectAll('.wc-chart').each(function(d) {
- try {
- d.draw();
- } catch (error) {}
- });
- cat.dataWrap.style('margin-left', '20%');
- d3.select(this).remove();
- });
- });
-}
diff --git a/src/cat/controls/initSubmit/addSubmitButton.js b/src/cat/controls/initSubmit/addSubmitButton.js
deleted file mode 100644
index 9dd805b..0000000
--- a/src/cat/controls/initSubmit/addSubmitButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import { loadLibrary } from '../../loadLibrary';
-
-export default function addSubmitButton() {
- this.controls.submitButton = this.controls.submitWrap
- .append('button')
- .attr('class', 'submit')
- .text('Render Chart')
- .on('click', () => {
- this.controls.minimize.classed('hidden', false);
- this.dataWrap.classed('hidden', true);
- this.chartWrap.classed('hidden', false);
-
- //Disable and/or remove previously loaded stylesheets.
- d3
- .selectAll('link')
- .filter(function() {
- return !this.href.indexOf('css/cat.css');
- })
- .property('disabled', true)
- .remove();
-
- d3
- .selectAll('style')
- .property('disabled', true)
- .remove();
-
- this.chartWrap.selectAll('*').remove();
- this.printStatus = true;
- this.statusDiv = this.chartWrap.append('div').attr('class', 'status');
- this.statusDiv
- .append('div')
- .text('Starting to render the chart ... ')
- .classed('info', true);
-
- this.chartWrap.append('div').attr('class', 'chart');
- loadLibrary(this);
- });
-}
diff --git a/src/cat/controls/initSubmit/destroyChart.js b/src/cat/controls/initSubmit/destroyChart.js
new file mode 100644
index 0000000..39e7ad2
--- /dev/null
+++ b/src/cat/controls/initSubmit/destroyChart.js
@@ -0,0 +1,30 @@
+export default function destroyChart() {
+ if (this.previous) {
+ if (this.previous.instance && this.previous.instance.destroy) {
+ console.log('destroy');
+ console.log(this.previous);
+ if (this.previous.instance && this.previous.instance.destroy)
+ this.previous.instance.destroy();
+ } else {
+ console.log('no destroy');
+ console.log(this.previous);
+ this.chartWrap.selectAll('.wc-chart').each(function(chart) {
+ if (chart.destroy) chart.destroy();
+ else {
+ //remove resize event listener
+ select(window).on('resize.' + chart.element + chart.id, null);
+
+ //destroy controls
+ if (chart.controls) {
+ chart.controls.destroy();
+ }
+
+ //unmount chart wrapper
+ chart.wrap.remove();
+ }
+ });
+ }
+ }
+
+ this.chartWrap.selectAll('*').remove();
+}
diff --git a/src/cat/controls/initSubmit/initializeChart.js b/src/cat/controls/initSubmit/initializeChart.js
new file mode 100644
index 0000000..b47c20a
--- /dev/null
+++ b/src/cat/controls/initSubmit/initializeChart.js
@@ -0,0 +1,42 @@
+import { loadLibrary } from '../../loadLibrary';
+
+export default function initializeChart(data) {
+ this.chartWrap.append('div').attr('class', 'chart');
+
+ this.status.loadStatus(this.statusDiv, true, this.dataObject.dataFilePath);
+
+ if (this.current.sub) {
+ this.current.instance = window[this.current.main][this.current.sub](
+ '.cat-chart',
+ this.current.config
+ );
+ this.status.chartCreateStatus(this.statusDiv, this.current.main, this.current.sub);
+ } else {
+ this.current.instance = window[this.current.main](
+ '.cat-chart .chart',
+ this.current.config
+ );
+ this.status.chartCreateStatus(this.statusDiv, this.current.main);
+ }
+
+ //this.current.htmlExport = createChartExport(this); // save the source code before init
+
+ try {
+ this.current.instance.init(data);
+ } catch (err) {
+ this.status.chartInitStatus(this.statusDiv, false, err);
+ } finally {
+ this.status.chartInitStatus(this.statusDiv, true, null, this.current.htmlExport);
+
+ // save to server button
+ if (this.config.useServer) {
+ this.status.saveToServer(this);
+ }
+ //showEnv(this);
+
+ //don't print any new statuses until a new chart is rendered
+ this.printStatus = false;
+ }
+
+ this.current.rendered = true;
+}
diff --git a/src/cat/controls/initSubmit/loadData.js b/src/cat/controls/initSubmit/loadData.js
new file mode 100644
index 0000000..d8add0c
--- /dev/null
+++ b/src/cat/controls/initSubmit/loadData.js
@@ -0,0 +1,8 @@
+export default function loadData() {
+ const dataFile = this.controls.dataFileSelect.node().value;
+ this.dataObject = this.config.dataFiles.find(f => f.label == dataFile);
+ this.dataObject.dataFilePath = this.dataObject.path + dataFile;
+ return fetch(this.dataObject.dataFilePath)
+ .then(response => response.text())
+ .then(text => d3.csv.parse(text));
+}
diff --git a/src/cat/controls/initSubmit/updateStatus.js b/src/cat/controls/initSubmit/updateStatus.js
new file mode 100644
index 0000000..050543b
--- /dev/null
+++ b/src/cat/controls/initSubmit/updateStatus.js
@@ -0,0 +1,8 @@
+export default function updateStatus() {
+ this.printStatus = true;
+ this.statusDiv = this.chartWrap.append('div').attr('class', 'status');
+ this.statusDiv
+ .append('div')
+ .text('Starting to render the chart ... ')
+ .classed('info', true);
+}
diff --git a/src/cat/datapreview/showDataPreview.js b/src/cat/datapreview/showDataPreview.js
index 428904d..48bfd5b 100644
--- a/src/cat/datapreview/showDataPreview.js
+++ b/src/cat/datapreview/showDataPreview.js
@@ -24,13 +24,57 @@ export function showDataPreview(cat) {
cat.dataWrap
.append('div')
.attr('class', 'dataPreview')
- .style('overflow-x', 'overlay');
- cat.dataPreview = webCharts.createTable('.dataPreview');
+ // .style('overflow-x', 'overlay');
+ //cat.dataPreview = webCharts.createTable('.dataPreview');
if (dataObject.user_loaded) {
- cat.dataPreview.init(d3.csv.parse(dataObject.csv_raw));
+ dataObject.data = d3.csv.parse(dataObject.csv_raw);
+ handsOnTable(dataObject.data);
+ //cat.dataPreview.init(d3.csv.parse(dataObject.csv_raw));
} else {
d3.csv(path, function(raw) {
- cat.dataPreview.init(raw);
+ dataObject.data = raw;
+ handsOnTable(dataObject.data);
+ //cat.dataPreview.init(raw);
});
}
+
+ function handsOnTable(data) {
+ const colHeaders = Object.keys(data[0]);
+ const table = new Handsontable(
+ cat.dataWrap.select('.dataPreview').node(),
+ {
+ data: data.map(d => Object.keys(d).map(key => d[key])),
+ colHeaders,
+ columns: colHeaders.map(_ => { return {type: 'text'}; }),
+ rowHeaders: true,
+ dropdownMenu: true,
+ filters: true,
+ afterChange: function(changes) {
+ dataObject.json = this.getData()
+ .map(d => {
+ return d.reduce(
+ (acc,cur,i) => {
+ acc[colHeaders[i]] = cur;
+ return acc;
+ },
+ {}
+ );
+ });
+ },
+ afterFilter: function(changes) {
+ dataObject.json = this.getData()
+ .map(d => {
+ return d.reduce(
+ (acc,cur,i) => {
+ acc[colHeaders[i]] = cur;
+ return acc;
+ },
+ {}
+ );
+ });
+ },
+ licenseKey: 'non-commercial-and-evaluation',
+ },
+ );
+ }
}
diff --git a/src/cat/defaultSettings.js b/src/cat/defaultSettings.js
index 61f21b6..df92123 100644
--- a/src/cat/defaultSettings.js
+++ b/src/cat/defaultSettings.js
@@ -1,9 +1,12 @@
-const defaultSettings = {
+export default {
useServer: false,
- rootURL: null,
- dataURL: null,
+ rootURL: 'https://github.com/RhoInc',
+ dataURL: 'https://raw.githubusercontent.com/RhoInc/data-library/master/data/',
+ chartingLibrary: {
+ name: 'Webcharts',
+ css: 'css/webcharts.css',
+ versions: [],
+ },
+ renderers: [],
dataFiles: [],
- renderers: []
};
-
-export default defaultSettings;
diff --git a/src/cat/init.js b/src/cat/init.js
index 563002c..0e7bc77 100644
--- a/src/cat/init.js
+++ b/src/cat/init.js
@@ -1,16 +1,26 @@
-export function init() {
- //layout the cat
- this.wrap = d3
- .select(this.element)
- .append('div')
- .attr('class', 'cat-wrap');
- this.layout(this);
+import setDefaults from './init/setDefaults';
+import layout from './init/layout';
+import loadData from './init/loadData';
+import loadChartingLibrary from './init/loadChartingLibrary';
+import loadChartingApplication from './init/loadChartingApplication';
+import initializeControls from './init/initializeControls';
- //initialize the settings
- this.setDefaults(this);
+export default function init() {
+ //settings
+ setDefaults.call(this);
- //add others here!
+ //layout
+ layout.call(this);
- //create the controls
- this.controls.init(this);
+ //renderers and data files
+ loadData.call(this);
+
+ //load charting library
+ loadChartingLibrary.call(this);
+
+ //load charting application
+ loadChartingApplication.call(this);
+
+ //initialize controls
+ initializeControls.call(this);
}
diff --git a/src/cat/init/initializeControls.js b/src/cat/init/initializeControls.js
new file mode 100644
index 0000000..c9ca4f1
--- /dev/null
+++ b/src/cat/init/initializeControls.js
@@ -0,0 +1,9 @@
+import renderChart from './initializeControls/renderChart';
+import changeLibrary from './initializeControls/changeLibrary';
+import changeLibraryVersion from './initializeControls/changeLibraryVersion';
+
+export default function initializeControls() {
+ renderChart.call(this);
+ changeLibrary.call(this);
+ changeLibraryVersion.call(this);
+}
diff --git a/src/cat/init/initializeControls/changeLibrary.js b/src/cat/init/initializeControls/changeLibrary.js
new file mode 100644
index 0000000..fd44387
--- /dev/null
+++ b/src/cat/init/initializeControls/changeLibrary.js
@@ -0,0 +1,26 @@
+export default function changeLibrary() {
+ const cat = this;
+
+ this.controls.rendererSelect.on('change', function(d) {
+ cat.chartingApplication = d3.select(this).selectAll('option:checked').datum();
+ cat.chartingApplication.version = 'master';
+ cat.controls.versionSelect.selectAll('option').property('selected', d => d.label === cat.chartingApplication.version);
+ cat.utilities.getVersions.call(cat, cat.chartingApplication.name)
+ .then(versions => {
+ cat.chartingApplication.versions = versions
+ .map(version => {
+ version.label = versions.tag_name
+ ? version.tag_name
+ : version.name;
+ return version;
+ });
+ cat.utilities.updateSelect.call(cat, cat.controls.versionSelect, cat.chartingApplication.versions);
+ });
+ cat.utilities.loadPackageJSON.call(cat, cat.chartingApplication.name, 'master')
+ .then(pkg => {
+ cat.chartingApplication.pkg = JSON.parse(pkg);
+ cat.utilities.loadFiles.call(cat, cat.chartingApplication.name, cat.chartingApplication.pkg, 'master', cat.chartingApplication.css);
+ cat.utilities.updateFields.call(cat, cat.chartingApplication.main, cat.chartingApplication.sub, cat.chartingApplication.schema);
+ });
+ });
+}
diff --git a/src/cat/init/initializeControls/changeLibraryVersion.js b/src/cat/init/initializeControls/changeLibraryVersion.js
new file mode 100644
index 0000000..d678dc5
--- /dev/null
+++ b/src/cat/init/initializeControls/changeLibraryVersion.js
@@ -0,0 +1,13 @@
+export default function changeLibraryVersion() {
+ const cat = this;
+
+ this.controls.versionSelect.on('change', function(d) {
+ cat.chartingApplication.version = d3.select(this).selectAll('option:checked').datum().label;
+ cat.utilities.loadPackageJSON.call(cat, cat.chartingApplication.name, cat.chartingApplication.version)
+ .then(pkg => {
+ cat.chartingApplication.pkg = JSON.parse(pkg);
+ cat.utilities.loadFiles.call(cat, cat.chartingApplication.name, cat.chartingApplication.pkg, cat.chartingApplication.version, cat.chartingApplication.css);
+ cat.utilities.updateFields.call(cat, cat.chartingApplication.main, cat.chartingApplication.sub, cat.chartingApplication.schema);
+ });
+ });
+}
diff --git a/src/cat/init/initializeControls/renderChart.js b/src/cat/init/initializeControls/renderChart.js
new file mode 100644
index 0000000..59191dc
--- /dev/null
+++ b/src/cat/init/initializeControls/renderChart.js
@@ -0,0 +1,21 @@
+import destroyChart from './renderChart/destroyChart';
+import loadData from './renderChart/loadData';
+import initializeChart from './renderChart/initializeChart';
+
+/*
+ 1. Destroys the currently displayed chart if one has been rendered.
+ 2. Loads the selected data file.
+ 3. Initializes the selected charting application library.
+*/
+
+export default function renderChart() {
+ this.controls.submitButton.on('click', () => {
+ this.dataWrap.classed('hidden', true);
+ this.chartWrap.classed('hidden', false);
+ destroyChart.call(this);
+ loadData.call(this)
+ .then(json => {
+ initializeChart.call(this, json);
+ });
+ });
+}
diff --git a/src/cat/init/initializeControls/renderChart/destroyChart.js b/src/cat/init/initializeControls/renderChart/destroyChart.js
new file mode 100644
index 0000000..18be5b3
--- /dev/null
+++ b/src/cat/init/initializeControls/renderChart/destroyChart.js
@@ -0,0 +1,26 @@
+export default function destroyChart() {
+ if (this.chartingApplicationInstance) {
+ if (this.chartingApplicationInstance && this.chartingApplicationInstance.destroy) {
+ if (this.chartingApplicationInstance && this.chartingApplicationInstance.destroy)
+ this.chartingApplicationInstance.destroy();
+ } else {
+ this.chartWrap.selectAll('.wc-chart').each(function(chart) {
+ if (chart.destroy) chart.destroy();
+ else {
+ //remove resize event listener
+ select(window).on('resize.' + chart.element + chart.id, null);
+
+ //destroy controls
+ if (chart.controls) {
+ chart.controls.destroy();
+ }
+
+ //unmount chart wrapper
+ chart.wrap.remove();
+ }
+ });
+ }
+ }
+
+ this.chartWrap.selectAll('*').remove();
+}
diff --git a/src/cat/init/initializeControls/renderChart/initializeChart.js b/src/cat/init/initializeControls/renderChart/initializeChart.js
new file mode 100644
index 0000000..92214f0
--- /dev/null
+++ b/src/cat/init/initializeControls/renderChart/initializeChart.js
@@ -0,0 +1,23 @@
+export default function initializeChart(data) {
+ this.chartWrap.append('div').attr('class', 'chart');
+
+ //Pass element and settings to charting application.
+ if (this.chartingApplication.sub) {
+ this.chartingApplicationInstance = window[this.chartingApplication.main][this.chartingApplication.sub](
+ '.cat-chart .chart',
+ this.chartingApplication.config || {}
+ );
+ } else {
+ this.chartingApplicationInstance = window[this.chartingApplication.main](
+ '.cat-chart .chart',
+ this.chartingApplication.config || {}
+ );
+ }
+
+ //Pass data to charting application.
+ try {
+ this.chartingApplicationInstance.init(data);
+ } catch (err) {
+ console.warn(err);
+ }
+}
diff --git a/src/cat/init/initializeControls/renderChart/loadData.js b/src/cat/init/initializeControls/renderChart/loadData.js
new file mode 100644
index 0000000..d8add0c
--- /dev/null
+++ b/src/cat/init/initializeControls/renderChart/loadData.js
@@ -0,0 +1,8 @@
+export default function loadData() {
+ const dataFile = this.controls.dataFileSelect.node().value;
+ this.dataObject = this.config.dataFiles.find(f => f.label == dataFile);
+ this.dataObject.dataFilePath = this.dataObject.path + dataFile;
+ return fetch(this.dataObject.dataFilePath)
+ .then(response => response.text())
+ .then(text => d3.csv.parse(text));
+}
diff --git a/src/cat/init/layout.js b/src/cat/init/layout.js
new file mode 100644
index 0000000..248d463
--- /dev/null
+++ b/src/cat/init/layout.js
@@ -0,0 +1,35 @@
+import toggleDisplayOfControls from './layout/toggleDisplayOfControls';
+import controls from './layout/controls';
+
+export default function layout() {
+ this.wrap = d3
+ .select(this.element)
+ .append('div')
+ .attr('class', 'cat-wrap');
+
+ //Controls display toggle
+ this.hideControls = this.wrap
+ .append('div')
+ .classed('cat-button cat-button--hide-controls', true)
+ .attr('title', 'Hide controls')
+ .text('<<');
+ this.showControls = this.wrap
+ .append('div')
+ .classed('cat-button cat-button--show-controls hidden', true)
+ .attr('title', 'Show controls')
+ .text('>>');
+ toggleDisplayOfControls.call(this);
+
+ //Controls
+ this.controls.wrap = this.wrap.append('div').classed('cat-controls section', true);
+ controls.call(this);
+
+ //Chart
+ this.chartWrap = this.wrap.append('div').classed('cat-chart section', true);
+
+ //Table
+ this.dataWrap = this.wrap
+ .append('div')
+ .classed('cat-data section', true)
+ .classed('hidden', true);
+}
diff --git a/src/cat/init/layout/chart.js b/src/cat/init/layout/chart.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/cat/init/layout/controls.js b/src/cat/init/layout/controls.js
new file mode 100644
index 0000000..05fbd9a
--- /dev/null
+++ b/src/cat/init/layout/controls.js
@@ -0,0 +1,17 @@
+import renderChart from './controls/renderChart';
+import chooseAChartingLibrary from './controls/chooseAChartingLibrary';
+import chooseADataset from './controls/chooseADataset';
+import customizeTheChart from './controls/customizeTheChart';
+import environment from './controls/environment';
+
+export default function controls() {
+ this.controls.wrap
+ .append('h2')
+ .classed('cat-controls-header', true)
+ .text('Charting Application Tester 😼');
+ renderChart.call(this);
+ chooseAChartingLibrary.call(this);
+ chooseADataset.call(this);
+ customizeTheChart.call(this);
+ environment.call(this);
+}
diff --git a/src/cat/init/layout/controls/chooseAChartingLibrary.js b/src/cat/init/layout/controls/chooseAChartingLibrary.js
new file mode 100644
index 0000000..0f42d11
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseAChartingLibrary.js
@@ -0,0 +1,19 @@
+import library from './chooseAChartingLibrary/library';
+import version from './chooseAChartingLibrary/version';
+import moreOptions from './chooseAChartingLibrary/moreOptions';
+import init from './chooseAChartingLibrary/init';
+import webchartsVersion from './chooseAChartingLibrary/webchartsVersion';
+import schema from './chooseAChartingLibrary/schema';
+
+export default function chooseAChartingLibrary() {
+ this.controls.rendererWrap = this.controls.wrap
+ .append('div')
+ .classed('control-section renderer-section', true);
+ this.controls.rendererWrap.append('h3').text('1. Choose a Charting Library');
+ library.call(this);
+ version.call(this);
+ //moreOptions.call(this);
+ init.call(this);
+ schema.call(this);
+ webchartsVersion.call(this);
+}
diff --git a/src/cat/init/layout/controls/chooseAChartingLibrary/init.js b/src/cat/init/layout/controls/chooseAChartingLibrary/init.js
new file mode 100644
index 0000000..b595f6e
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseAChartingLibrary/init.js
@@ -0,0 +1,13 @@
+export default function init() {
+ this.controls.rendererWrap
+ .append('span')
+ .text(' Init: ')
+ //.classed('hidden', true);
+ this.controls.mainFunction = this.controls.rendererWrap.append('input')//.classed('hidden', true);
+ this.controls.rendererWrap
+ .append('span')
+ .text('.')
+ //.classed('hidden', true);
+ this.controls.subFunction = this.controls.rendererWrap.append('input')//.classed('hidden', true);
+ this.controls.rendererWrap.append('br')//.classed('hidden', true);
+}
diff --git a/src/cat/init/layout/controls/chooseAChartingLibrary/library.js b/src/cat/init/layout/controls/chooseAChartingLibrary/library.js
new file mode 100644
index 0000000..f1b8f7e
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseAChartingLibrary/library.js
@@ -0,0 +1,5 @@
+export default function library() {
+ this.controls.rendererWrap.append('span').text('Library: ');
+ this.controls.rendererSelect = this.controls.rendererWrap.append('select');
+ this.controls.rendererWrap.append('br');
+}
diff --git a/src/cat/init/layout/controls/chooseAChartingLibrary/moreOptions.js b/src/cat/init/layout/controls/chooseAChartingLibrary/moreOptions.js
new file mode 100644
index 0000000..d27627b
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseAChartingLibrary/moreOptions.js
@@ -0,0 +1,8 @@
+export default function moreOptions() {
+ this.controls.moreOptions = this.controls.rendererWrap
+ .append('a')
+ .text('More Options')
+ .style('text-decoration', 'underline')
+ .style('color', 'blue')
+ .style('cursor', 'pointer');
+}
diff --git a/src/cat/init/layout/controls/chooseAChartingLibrary/schema.js b/src/cat/init/layout/controls/chooseAChartingLibrary/schema.js
new file mode 100644
index 0000000..90852ae
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseAChartingLibrary/schema.js
@@ -0,0 +1,8 @@
+export default function schema() {
+ this.controls.rendererWrap
+ .append('span')
+ .text('Schema: ')
+ //.classed('hidden', true);
+ this.controls.schema = this.controls.rendererWrap.append('input')//.classed('hidden', true);
+ this.controls.rendererWrap.append('br')//.classed('hidden', true);
+}
diff --git a/src/cat/init/layout/controls/chooseAChartingLibrary/version.js b/src/cat/init/layout/controls/chooseAChartingLibrary/version.js
new file mode 100644
index 0000000..ae58a9e
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseAChartingLibrary/version.js
@@ -0,0 +1,5 @@
+export default function version() {
+ this.controls.rendererWrap.append('span').text('Version: ');
+ this.controls.versionSelect = this.controls.rendererWrap.append('select');
+ this.controls.rendererWrap.append('br');
+}
diff --git a/src/cat/init/layout/controls/chooseAChartingLibrary/webchartsVersion.js b/src/cat/init/layout/controls/chooseAChartingLibrary/webchartsVersion.js
new file mode 100644
index 0000000..b24f218
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseAChartingLibrary/webchartsVersion.js
@@ -0,0 +1,10 @@
+export default function webchartsVersion() {
+ this.controls.rendererWrap
+ .append('span')
+ .text('Webcharts Version: ')
+ //.classed('hidden', true);
+ this.controls.libraryVersion = this.controls.rendererWrap
+ .append('select')
+ //.classed('hidden', true);
+ this.controls.rendererWrap.append('br')//.classed('hidden', true);
+}
diff --git a/src/cat/init/layout/controls/chooseADataset.js b/src/cat/init/layout/controls/chooseADataset.js
new file mode 100644
index 0000000..414829f
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseADataset.js
@@ -0,0 +1,15 @@
+import dataFile from './chooseADataset/dataFile';
+import loadADataFile from './chooseADataset/loadADataFile';
+
+export default function chooseADataset() {
+ this.controls.dataWrap = this.controls.wrap
+ .append('div')
+ .classed('control-section data-section', true);
+ this.controls.dataWrap.append('h3').text('2. Choose a Dataset');
+ dataFile.call(this);
+ this.controls.viewData = this.controls.dataWrap
+ .append('span')
+ .html('🔍')
+ .style('cursor', 'pointer');
+ loadADataFile.call(this);
+}
diff --git a/src/cat/init/layout/controls/chooseADataset/dataFile.js b/src/cat/init/layout/controls/chooseADataset/dataFile.js
new file mode 100644
index 0000000..6e5469f
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseADataset/dataFile.js
@@ -0,0 +1,3 @@
+export default function dataFile() {
+ this.controls.dataFileSelect = this.controls.dataWrap.append('select');
+}
diff --git a/src/cat/init/layout/controls/chooseADataset/loadADataFile.js b/src/cat/init/layout/controls/chooseADataset/loadADataFile.js
new file mode 100644
index 0000000..7c2c141
--- /dev/null
+++ b/src/cat/init/layout/controls/chooseADataset/loadADataFile.js
@@ -0,0 +1,27 @@
+export default function loadADataFile() {
+ const loadLabel = this.controls.dataWrap.append('p').style('margin', 0);
+ loadLabel
+ .append('small')
+ .text('Use local .csv file:')
+ .append('sup')
+ .html('ⓘ')
+ .property(
+ 'title',
+ 'Render a chart using a local file. File is added to the data set list, and is only available for a single session and is not saved.'
+ )
+ .style('cursor', 'help');
+ this.controls.loadStatus = loadLabel
+ .append('small')
+ .attr('class', 'loadStatus')
+ .style('float', 'right')
+ .text('Select a csv to load');
+ this.controls.dataFileLoad = this.controls.dataWrap
+ .append('input')
+ .attr('type', 'file')
+ .attr('class', 'file-load-input');
+ this.controls.dataFileLoadButton = this.controls.dataWrap
+ .append('button')
+ .text('Load')
+ .attr('class', 'file-load-button')
+ .attr('disabled', true);
+}
diff --git a/src/cat/init/layout/controls/customizeTheChart.js b/src/cat/init/layout/controls/customizeTheChart.js
new file mode 100644
index 0000000..d9daf4b
--- /dev/null
+++ b/src/cat/init/layout/controls/customizeTheChart.js
@@ -0,0 +1,32 @@
+export default function customizeTheChart() {
+ this.controls.settingsWrap = this.controls.wrap
+ .append('div')
+ .classed('control-section settings-section', true);
+ this.controls.settingsWrap.append('h3').html('3. Customize the Chart ');
+ this.controls.settingsWrap.append('span').text('Settings: ');
+ this.controls.settingsTypeText = this.controls.settingsWrap
+ .append('input')
+ .attr('class', 'radio')
+ .property('type', 'radio')
+ .property('name', 'settingsType')
+ .property('value', 'text');
+ this.controls.settingsWrap.append('span').text('text');
+ this.controls.settingsTypeForm = this.controls.settingsWrap
+ .append('input')
+ .attr('class', 'radio')
+ .property('type', 'radio')
+ .property('name', 'settingsType')
+ .property('value', 'form');
+ this.controls.settingsWrap.append('span').text('form');
+ this.controls.settingsType = this.controls.settingsWrap.selectAll('input[type="radio"]');
+ this.controls.settingsWrap.append('br');
+ this.controls.settingsInput = this.controls.settingsWrap
+ .append('textarea')
+ .attr('rows', 10)
+ .style('width', '90%')
+ .text('{}');
+ this.controls.settingsForm = this.controls.settingsWrap
+ .append('div')
+ .attr('class', 'settingsForm')
+ .append('form');
+}
diff --git a/src/cat/init/layout/controls/environment.js b/src/cat/init/layout/controls/environment.js
new file mode 100644
index 0000000..36b0a4d
--- /dev/null
+++ b/src/cat/init/layout/controls/environment.js
@@ -0,0 +1,10 @@
+export default function environment() {
+ this.controls.environmentWrap = this.controls.wrap
+ .append('div')
+ .classed('control-section environment-section', true);
+ this.controls.environmentWrap.append('h3').html('4. Environment ');
+ this.controls.cssList = this.controls.environmentWrap.append('ul').attr('class', 'cssList');
+ this.controls.cssList.append('h5').text('Loaded Stylesheets');
+ this.controls.jsList = this.controls.environmentWrap.append('ul').attr('class', 'jsList');
+ this.controls.jsList.append('h5').text('Loaded javascript');
+}
diff --git a/src/cat/init/layout/controls/renderChart.js b/src/cat/init/layout/controls/renderChart.js
new file mode 100644
index 0000000..c15a295
--- /dev/null
+++ b/src/cat/init/layout/controls/renderChart.js
@@ -0,0 +1,9 @@
+export default function renderChart() {
+ this.controls.submitWrap = this.controls.wrap
+ .append('div')
+ .classed('control-section submit-section', true);
+ this.controls.submitButton = this.controls.submitWrap
+ .append('button')
+ .attr('class', 'submit')
+ .text('Render Chart')
+}
diff --git a/src/cat/init/layout/table.js b/src/cat/init/layout/table.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/cat/init/layout/toggleDisplayOfControls.js b/src/cat/init/layout/toggleDisplayOfControls.js
new file mode 100644
index 0000000..17355d7
--- /dev/null
+++ b/src/cat/init/layout/toggleDisplayOfControls.js
@@ -0,0 +1,36 @@
+export default function toggleDisplayOfControls() {
+ const styleSheet = Array.from(document.styleSheets).find(
+ styleSheet => styleSheet.href.indexOf('cat.css') > -1
+ );
+ const controlsWidth = Array.from(styleSheet.cssRules).find(
+ cssRule => cssRule.selectorText === '.cat-wrap .cat-controls'
+ ).style.width;
+
+ //Hide controls.
+ this.hideControls.on('click', () => {
+ this.controls.wrap.classed('hidden', true);
+ this.chartWrap.style('margin-left', 0);
+ this.chartWrap.selectAll('.wc-chart').each(function(d) {
+ try {
+ d.draw();
+ } catch (error) {}
+ });
+ this.dataWrap.style('margin-left', 0);
+ this.hideControls.classed('hidden', true);
+ this.showControls.classed('hidden', false);
+ });
+
+ //Show controls.
+ this.showControls.on('click', () => {
+ this.controls.wrap.classed('hidden', false);
+ this.chartWrap.style('margin-left', controlsWidth);
+ this.chartWrap.selectAll('.wc-chart').each(function(d) {
+ try {
+ d.draw();
+ } catch (error) {}
+ });
+ this.dataWrap.style('margin-left', controlsWidth);
+ this.hideControls.classed('hidden', false);
+ this.showControls.classed('hidden', true);
+ });
+}
diff --git a/src/cat/init/loadChartingApplication.js b/src/cat/init/loadChartingApplication.js
new file mode 100644
index 0000000..f9f3bfa
--- /dev/null
+++ b/src/cat/init/loadChartingApplication.js
@@ -0,0 +1,20 @@
+export default function loadChartingApplication() {
+ this.chartingApplication = this.config.renderers[0];
+ this.utilities.getVersions.call(this, this.chartingApplication.name)
+ .then(versions => {
+ this.chartingApplication.versions = versions
+ .map(version => {
+ version.label = versions.tag_name
+ ? version.tag_name
+ : version.name;
+ return version;
+ });
+ this.utilities.updateSelect.call(this, this.controls.versionSelect, this.chartingApplication.versions);
+ });
+ this.utilities.loadPackageJSON.call(this, this.chartingApplication.name, 'master')
+ .then(pkg => {
+ this.chartingApplication.pkg = JSON.parse(pkg);
+ this.utilities.loadFiles.call(this, this.chartingApplication.name, this.chartingApplication.pkg, 'master', this.chartingApplication.css);
+ this.utilities.updateFields.call(this, this.chartingApplication.main, this.chartingApplication.sub, this.chartingApplication.schema);
+ });
+}
diff --git a/src/cat/init/loadChartingLibrary.js b/src/cat/init/loadChartingLibrary.js
new file mode 100644
index 0000000..5cebafa
--- /dev/null
+++ b/src/cat/init/loadChartingLibrary.js
@@ -0,0 +1,18 @@
+export default function loadChartingLibrary() {
+ this.utilities.getVersions.call(this, this.config.chartingLibrary.name)
+ .then(versions => {
+ this.config.chartingLibrary.versions = versions
+ .map(version => {
+ version.label = versions.tag_name
+ ? version.tag_name
+ : version.name;
+ return version;
+ });
+ this.utilities.updateSelect.call(this, this.controls.libraryVersion, this.config.chartingLibrary.versions);
+ });
+ this.utilities.loadPackageJSON.call(this, this.config.chartingLibrary.name, 'master')
+ .then(pkg => {
+ this.config.chartingLibrary.pkg = JSON.parse(pkg);
+ this.utilities.loadFiles.call(this, this.config.chartingLibrary.name, this.config.chartingLibrary.pkg, 'master', this.config.chartingLibrary.css);
+ });
+}
diff --git a/src/cat/init/loadData.js b/src/cat/init/loadData.js
new file mode 100644
index 0000000..85d7461
--- /dev/null
+++ b/src/cat/init/loadData.js
@@ -0,0 +1,4 @@
+export default function loadData() {
+ this.utilities.updateSelect.call(this, this.controls.rendererSelect, this.config.renderers);
+ this.utilities.updateSelect.call(this, this.controls.dataFileSelect, this.config.dataFiles);
+}
diff --git a/src/cat/init/setDefaults.js b/src/cat/init/setDefaults.js
new file mode 100644
index 0000000..3d750f7
--- /dev/null
+++ b/src/cat/init/setDefaults.js
@@ -0,0 +1,22 @@
+import defaultSettings from '../defaultSettings';
+
+export default function setDefaults() {
+ this.config.useServer = this.config.useServer || defaultSettings.useServer;
+ this.config.rootURL = this.config.rootURL || defaultSettings.rootURL;
+ this.config.apiURL = this.config.rootURL.replace('github.com', 'api.github.com/repos');
+ this.config.cdnURL = this.config.rootURL.replace('github.com', 'cdn.jsdelivr.net/gh');
+ this.config.dataURL = this.config.dataURL || defaultSettings.dataURL;
+ this.config.chartingLibrary = this.config.chartingLibrary || defaultSettings.chartingLibrary;
+ this.config.renderers = this.config.renderers || defaultSettings.renderers;
+ this.config.dataFiles = this.config.dataFiles || defaultSettings.dataFiles;
+
+ this.config.renderers.forEach(renderer => {
+ renderer.label = renderer.label || renderer.name;
+ });
+
+ this.config.dataFiles = this.config.dataFiles.map(df => {
+ return typeof df === 'string'
+ ? { label: df, path: this.config.dataURL, user_loaded: false }
+ : df;
+ });
+}
diff --git a/src/cat/layout.js b/src/cat/layout.js
deleted file mode 100644
index 9df959a..0000000
--- a/src/cat/layout.js
+++ /dev/null
@@ -1,35 +0,0 @@
-export function layout(cat) {
- /* Layout primary sections */
- cat.controls.wrap = cat.wrap.append('div').classed('cat-controls section', true);
- cat.chartWrap = cat.wrap.append('div').classed('cat-chart section', true);
- cat.dataWrap = cat.wrap
- .append('div')
- .classed('cat-data section', true)
- .classed('hidden', true);
-
- /* Layout CAT Controls Divs */
- cat.controls.wrap
- .append('h2')
- .classed('cat-controls-header', true)
- .text('Charting Application Tester 😼');
-
- cat.controls.submitWrap = cat.controls.wrap
- .append('div')
- .classed('control-section submit-section', true);
-
- cat.controls.rendererWrap = cat.controls.wrap
- .append('div')
- .classed('control-section renderer-section', true);
-
- cat.controls.dataWrap = cat.controls.wrap
- .append('div')
- .classed('control-section data-section', true);
-
- cat.controls.settingsWrap = cat.controls.wrap
- .append('div')
- .classed('control-section settings-section', true);
-
- cat.controls.environmentWrap = cat.controls.wrap
- .append('div')
- .classed('control-section environment-section', true);
-}
diff --git a/src/cat/loadLibrary.js b/src/cat/loadLibrary.js
index 2e3b1cd..48a5d42 100644
--- a/src/cat/loadLibrary.js
+++ b/src/cat/loadLibrary.js
@@ -1,4 +1,4 @@
-import { scriptLoader } from '../util/scriptLoader';
+import scriptLoader from '../util/scriptLoader';
import { loadRenderer } from './loadRenderer';
import { getCSS } from './env/getCSS';
import { getJS } from './env/getJS';
diff --git a/src/cat/loadRenderer.js b/src/cat/loadRenderer.js
index 144691d..efc2ff2 100644
--- a/src/cat/loadRenderer.js
+++ b/src/cat/loadRenderer.js
@@ -1,76 +1,16 @@
-import loadPackageJson from './loadRenderer/loadPackageJson';
-import { getCSS } from './env/getCSS';
-import { getJS } from './env/getJS';
-import { scriptLoader } from '../util/scriptLoader';
-import { renderChart } from './renderChart';
+import loadWebcharts from './loadRenderer/loadWebcharts';
+import getVersions from './loadRenderer/getVersions';
+import loadPackageJSON from './loadRenderer/loadPackageJSON';
+import loadRenderer from './loadRenderer/loadRenderer';
-export function loadRenderer(cat) {
- const promisedPackage = loadPackageJson(cat);
- promisedPackage.then(function(response) {
- cat.current.package = JSON.parse(response);
- cat.current.js_url = `${cat.current.url}/${cat.current.package.main.replace(
- /^\.?\/?/,
- ''
- )}`;
- cat.current.css_url = cat.current.css ? `${cat.current.url}/${cat.current.css}` : null;
-
- if (cat.current.css) {
- var current_css = getCSS().filter(f => f.link == cat.current.css_url);
- var css_loaded = current_css.length > 0;
- if (!css_loaded) {
- var link = document.createElement('link');
- link.href = cat.current.css_url;
-
- link.type = 'text/css';
- link.rel = 'stylesheet';
- document.getElementsByTagName('head')[0].appendChild(link);
- } else if (current_css[0].disabled) {
- //enable the css if it's disabled
- d3.select(current_css[0].sel).property('disabled', false);
- cat.controls.cssList
- .selectAll('li')
- .filter(d => d.link == cat.current.css_url)
- .select('input')
- .property('checked', true);
- }
- }
-
- var current_js = getJS().filter(f => f.link == cat.current.js_url);
- var js_loaded = current_js.length > 0;
-
- if (!js_loaded) {
- var loader = new scriptLoader();
- loader.require(cat.current.js_url, {
- async: true,
- success: function() {
- cat.status.loadStatus(
- cat.statusDiv,
- true,
- cat.current.js_url,
- cat.current.name,
- cat.current.version
- );
- renderChart(cat);
- },
- failure: function() {
- cat.status.loadStatus(
- cat.statusDiv,
- false,
- cat.current.js_url,
- cat.current.name,
- cat.current.version
- );
- }
- });
- } else {
- cat.status.loadStatus(
- cat.statusDiv,
- true,
- cat.current.js_url,
- cat.current.name,
- cat.current.version
- );
- renderChart(cat);
- }
- });
+export default function loadRenderer() {
+ loadWebcharts.call(this, 'master');
+ getVersions.call(this, this.controls.libraryVersion);
+ this.current = this.config.renderers[0];
+ this.current.version = 'master';
+ loadPackageJSON.call(this)
+ .then(response => {
+ loadRenderer.call(this, 'master', response)
+ getVersions.call(this, this.controls.versionSelect, this.current.api_url);
+ });
}
diff --git a/src/cat/loadRenderer/getVersions.js b/src/cat/loadRenderer/getVersions.js
new file mode 100644
index 0000000..ced9b2b
--- /dev/null
+++ b/src/cat/loadRenderer/getVersions.js
@@ -0,0 +1,27 @@
+export default function getVersions(
+ select,
+ repo = 'https://api.github.com/repos/RhoInc/Webcharts'
+) {
+ const branches = fetch(`${repo}/branches`).then(response => response.json());
+ const releases = fetch(`${repo}/releases`).then(response => response.json());
+
+ Promise.all([branches, releases])
+ .then(values => {
+ const [branches, releases] = values;
+ branches.sort(
+ (a, b) =>
+ a.name === 'master' ? -1 : b.name === 'master' ? 1 : a.name < b.name ? -1 : 1
+ );
+ select.selectAll('option').remove();
+ select
+ .selectAll('option')
+ .data(d3.merge(values))
+ .enter()
+ .append('option')
+ .text(d => d.tag_name || d.name)
+ .property('selected', d => d.name === 'master');
+ })
+ .catch(err => {
+ console.log(err);
+ });
+}
diff --git a/src/cat/loadRenderer/loadData.js b/src/cat/loadRenderer/loadData.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/cat/loadRenderer/loadLibrary.js b/src/cat/loadRenderer/loadLibrary.js
new file mode 100644
index 0000000..a41fd27
--- /dev/null
+++ b/src/cat/loadRenderer/loadLibrary.js
@@ -0,0 +1,32 @@
+import scriptLoader from '../../util/scriptLoader';
+
+export default function loadWebcharts(library, version, response) {
+ // --- load css --- //
+ const cssPath =
+ version !== 'master'
+ ? this.config.rootURL + '/Webcharts@' + version + '/css/webcharts.css'
+ : this.config.rootURL + '/Webcharts/css/webcharts.css';
+
+ const link = document.createElement('link');
+ link.href = cssPath;
+ link.type = 'text/css';
+ link.rel = 'stylesheet';
+ document.getElementsByTagName('head')[0].appendChild(link);
+
+ // --- load js --- //
+ const rendererPath =
+ version !== 'master'
+ ? this.config.rootURL + '/' + library + '@' + version + '/build/webcharts.js'
+ : this.config.rootURL + '/Webcharts/build/webcharts.js';
+
+ const loader = new scriptLoader();
+ loader.require(rendererPath, {
+ async: true,
+ success: () => {
+ //this.status.loadStatus(this.statusDiv, true, rendererPath, library, version);
+ },
+ failure: () => {
+ //this.status.loadStatus(this.statusDiv, false, rendererPath, library, version);
+ }
+ });
+}
diff --git a/src/cat/loadRenderer/loadPackageJson.js b/src/cat/loadRenderer/loadPackageJson.js
index e4eedfc..829e322 100644
--- a/src/cat/loadRenderer/loadPackageJson.js
+++ b/src/cat/loadRenderer/loadPackageJson.js
@@ -1,13 +1,13 @@
-export default function loadPackageJson(cat) {
- return new Promise(function(resolve, reject) {
- cat.current.url =
- cat.current.version === 'master'
- ? `${cat.current.rootURL || cat.config.rootURL}/${cat.current.name}`
- : `${cat.current.rootURL || cat.config.rootURL}/${cat.current.name}@${
- cat.current.version
+export default function loadPackageJson() {
+ return new Promise((resolve, reject) => {
+ this.current.url =
+ this.current.version === 'master'
+ ? `${this.current.rootURL || this.config.rootURL}/${this.current.name}`
+ : `${this.current.rootURL || this.config.rootURL}/${this.current.name}@${
+ this.current.version
}`;
- var xhr = new XMLHttpRequest();
- xhr.open('GET', `${cat.current.url}/package.json`);
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', `${this.current.url}/package.json`);
xhr.onload = function() {
if (this.status === 200) {
resolve(xhr.response);
diff --git a/src/cat/loadRenderer/loadRenderer.js b/src/cat/loadRenderer/loadRenderer.js
new file mode 100644
index 0000000..14d0d25
--- /dev/null
+++ b/src/cat/loadRenderer/loadRenderer.js
@@ -0,0 +1,60 @@
+import scriptLoader from '../../util/scriptLoader';
+
+export default function loadRenderer(version, response) {
+ //Attach package.json.
+ this.current.package = JSON.parse(response);
+
+ //Update version.
+ this.controls.versionSelect.node().value = version || 'master';
+
+ //Update namespace and method.
+ this.controls.mainFunction.node().value = this.current.main;
+ this.controls.subFunction.node().value = this.current.sub;
+
+ //Update schema.
+ this.controls.schema.node().value = this.current.schema;
+
+ //Update data file.
+ this.controls.dataFileSelect
+ .selectAll('option')
+ .property('selected', d => this.current.defaultData === d.label);
+
+ //Load .css file
+ this.current.css_url = this.current.css ? `${this.current.url}/${this.current.css}` : null;
+ if (this.current.css) {
+ this.current.link = document.createElement('link');
+ this.current.link.href = this.current.css_url;
+
+ this.current.link.type = 'text/css';
+ this.current.link.rel = 'stylesheet';
+ document.getElementsByTagName('head')[0].appendChild(this.current.link);
+ }
+
+ //Load .js file
+ this.current.js_url = `${this.current.url}/${this.current.package.main.replace(
+ /^\.?\/?/,
+ ''
+ )}`;
+ const loader = new scriptLoader();
+ this.current.script = loader.require(this.current.js_url, {
+ async: true,
+ success: () => {
+ //this.status.loadStatus(
+ // this.statusDiv,
+ // true,
+ // this.current.js_url,
+ // this.current.name,
+ // version || this.current.version
+ //);
+ },
+ failure: () => {
+ //this.status.loadStatus(
+ // this.statusDiv,
+ // false,
+ // this.current.js_url,
+ // this.current.name,
+ // version || this.current.version
+ //);
+ }
+ });
+}
diff --git a/src/cat/loadRenderer/loadWebcharts.js b/src/cat/loadRenderer/loadWebcharts.js
new file mode 100644
index 0000000..afeb66a
--- /dev/null
+++ b/src/cat/loadRenderer/loadWebcharts.js
@@ -0,0 +1,35 @@
+import scriptLoader from '../../util/scriptLoader';
+
+export default function loadWebcharts(version) {
+ version = version || this.controls.libraryVersion.node().value;
+ const library = 'webcharts'; //hardcode to webcharts for now - could generalize later
+
+ // --- load css --- //
+ const cssPath =
+ version !== 'master'
+ ? this.config.rootURL + '/Webcharts@' + version + '/css/webcharts.css'
+ : this.config.rootURL + '/Webcharts/css/webcharts.css';
+
+ const link = document.createElement('link');
+ link.href = cssPath;
+ link.type = 'text/css';
+ link.rel = 'stylesheet';
+ document.getElementsByTagName('head')[0].appendChild(link);
+
+ // --- load js --- //
+ const rendererPath =
+ version !== 'master'
+ ? this.config.rootURL + '/' + library + '@' + version + '/build/webcharts.js'
+ : this.config.rootURL + '/Webcharts/build/webcharts.js';
+
+ const loader = new scriptLoader();
+ loader.require(rendererPath, {
+ async: true,
+ success: () => {
+ //this.status.loadStatus(this.statusDiv, true, rendererPath, library, version);
+ },
+ failure: () => {
+ //this.status.loadStatus(this.statusDiv, false, rendererPath, library, version);
+ }
+ });
+}
diff --git a/src/cat/loadRenderer/updateSettings.js b/src/cat/loadRenderer/updateSettings.js
new file mode 100644
index 0000000..3b8cb13
--- /dev/null
+++ b/src/cat/loadRenderer/updateSettings.js
@@ -0,0 +1,2 @@
+export default function updateSettings() {
+}
diff --git a/src/cat/renderChart.js b/src/cat/renderChart.js
index f1b0cf2..a9b8547 100644
--- a/src/cat/renderChart.js
+++ b/src/cat/renderChart.js
@@ -17,20 +17,23 @@ export function renderChart(cat) {
} else {
cat.status.loadStatus(cat.statusDiv, true, dataFilePath);
if (cat.current.sub) {
- var myChart = window[cat.current.main][cat.current.sub](
+ cat.current.instance = window[cat.current.main][cat.current.sub](
'.cat-chart',
cat.current.config
);
cat.status.chartCreateStatus(cat.statusDiv, cat.current.main, cat.current.sub);
} else {
- var myChart = window[cat.current.main]('.cat-chart .chart', cat.current.config);
+ cat.current.instance = window[cat.current.main](
+ '.cat-chart .chart',
+ cat.current.config
+ );
cat.status.chartCreateStatus(cat.statusDiv, cat.current.main);
}
cat.current.htmlExport = createChartExport(cat); // save the source code before init
try {
- myChart.init(data);
+ cat.current.instance.init(data);
} catch (err) {
cat.status.chartInitStatus(cat.statusDiv, false, err);
} finally {
@@ -46,15 +49,19 @@ export function renderChart(cat) {
cat.printStatus = false;
}
}
+ cat.current.rendered = true;
}
- if (dataObject.user_loaded) {
+ if (dataObject.json)
+ render(false, dataObject.json);
+ else if (dataObject.user_loaded) {
dataObject.json = d3.csv.parse(dataObject.csv_raw);
render(false, dataObject.json);
} else {
var dataFilePath = dataObject.path + dataFile;
d3.csv(dataFilePath, function(error, data) {
- render(error, data);
+ dataObject.json = data;
+ render(error, dataObject.json);
});
}
}
diff --git a/src/cat/setDefaults.js b/src/cat/setDefaults.js
deleted file mode 100644
index a23fce0..0000000
--- a/src/cat/setDefaults.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import defaultSettings from './defaultSettings';
-
-export function setDefaults(cat) {
- cat.config.useServer = cat.config.useServer || defaultSettings.useServer;
- cat.config.rootURL = cat.config.rootURL || defaultSettings.rootURL;
- cat.config.dataURL = cat.config.dataURL || defaultSettings.dataURL;
- cat.config.dataFiles = cat.config.dataFiles || defaultSettings.dataFiles;
- cat.config.renderers = cat.config.renderers || defaultSettings.renderers;
-
- cat.config.dataFiles = cat.config.dataFiles.map(function(df) {
- return typeof df == 'string'
- ? { label: df, path: cat.config.dataURL, user_loaded: false }
- : df;
- });
-}
diff --git a/src/cat/settings.js b/src/cat/settings.js
index 0f530af..f13013d 100644
--- a/src/cat/settings.js
+++ b/src/cat/settings.js
@@ -6,7 +6,7 @@ import { set } from './settings/set';
import { sync } from './settings/sync';
import { setStatus } from './settings/setStatus';
-export const settings = {
+export default {
set: set,
sync: sync,
setStatus: setStatus
diff --git a/src/cat/status.js b/src/cat/status.js
index e434bf4..1fd15e3 100644
--- a/src/cat/status.js
+++ b/src/cat/status.js
@@ -7,7 +7,7 @@ import { chartInitStatus } from './status/chartInitStatus';
import { saveToServer } from './status/saveToServer';
import { loadStatus } from './status/loadStatus';
-export const status = {
+export default {
chartCreateStatus: chartCreateStatus,
chartInitStatus: chartInitStatus,
saveToServer: saveToServer,
diff --git a/src/createCat.js b/src/createCat.js
index 7ee0e95..883c229 100644
--- a/src/createCat.js
+++ b/src/createCat.js
@@ -1,20 +1,18 @@
-import { init } from './cat/init';
-import { layout } from './cat/layout';
-import { controls } from './cat/controls';
-import { setDefaults } from './cat/setDefaults';
-import { settings } from './cat/settings';
-import { status } from './cat/status';
+import utilities from './util/utilities';
+import init from './cat/init';
+//import controls from './cat/controls';
+//import settings from './cat/settings';
+//import status from './cat/status';
export function createCat(element = 'body', config) {
- let cat = {
- element: element,
- config: config,
- init: init,
- layout: layout,
- controls: controls,
- setDefaults: setDefaults,
- settings: settings,
- status: status
+ const cat = {
+ element,
+ config,
+ utilities,
+ init,
+ controls: {},
+ //settings,
+ //status
};
return cat;
diff --git a/src/util/defaultSettings.js b/src/util/defaultSettings.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/util/getVersions.js b/src/util/getVersions.js
new file mode 100644
index 0000000..39098f4
--- /dev/null
+++ b/src/util/getVersions.js
@@ -0,0 +1,18 @@
+export default function getVersions(repo) {
+ const apiURL = this.config.apiURL + '/' + repo;
+ const branches = fetch(`${apiURL}/branches`).then(response => response.json());
+ const releases = fetch(`${apiURL}/releases`).then(response => response.json());
+
+ return Promise.all([branches, releases])
+ .then(values => {
+ const [branches, releases] = values;
+ branches.sort(
+ (a, b) =>
+ a.name === 'master' ? -1 : b.name === 'master' ? 1 : a.name < b.name ? -1 : 1
+ );
+ return d3.merge(values);
+ })
+ .catch(err => {
+ console.log(err);
+ });
+}
diff --git a/src/util/loadFiles.js b/src/util/loadFiles.js
new file mode 100644
index 0000000..57d9437
--- /dev/null
+++ b/src/util/loadFiles.js
@@ -0,0 +1,34 @@
+import scriptLoader from './scriptLoader';
+
+export default function loadFiles(repo, pkg, branch, css) {
+ const version = branch || pkg.version;
+ const cdnURL = this.config.cdnURL + '/' + repo;
+
+ //Load .css file
+ if (css) {
+ const cssURL = version === 'master'
+ ? cdnURL + '/' + css
+ : cdnURL + '@' + version + '/' + css;
+ const link = document.createElement('link');
+ link.href = cssURL;
+ link.type = 'text/css';
+ link.rel = 'stylesheet';
+ document.getElementsByTagName('head')[0].appendChild(link);
+ }
+
+ //Load .js file
+ const jsURL = version === 'master'
+ ? cdnURL + '/' + pkg.main.replace(/^\.?\/?/, '')
+ : cdnURL + '@' + version + '/' + pkg.main.replace(/^\.?\/?/, '');
+
+ const loader = new scriptLoader();
+ const script = loader.require(jsURL, {
+ async: true,
+ success: () => {
+ console.log(`Loaded ${jsURL}.`);
+ },
+ failure: () => {
+ console.warn(`Failed to load ${jsURL}.`);
+ }
+ });
+}
diff --git a/src/util/loadPackageJSON.js b/src/util/loadPackageJSON.js
new file mode 100644
index 0000000..444771c
--- /dev/null
+++ b/src/util/loadPackageJSON.js
@@ -0,0 +1,28 @@
+export default function loadPackageJSON(repo, version) {
+ const cdnURL = this.config.cdnURL + '/' + repo;
+ const pkgURL = version === 'master'
+ ? cdnURL + '/package.json'
+ : cdnURL + '@' + version + '/package.json';
+
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', pkgURL);
+ xhr.onload = function() {
+ if (this.status === 200) {
+ resolve(xhr.response);
+ } else {
+ reject({
+ status: this.status,
+ statusTxt: xhr.statusText
+ });
+ }
+ };
+ xhr.onerror = function() {
+ reject({
+ status: this.status,
+ statusText: xhr.statusText
+ });
+ };
+ xhr.send();
+ });
+}
diff --git a/src/util/scriptLoader.js b/src/util/scriptLoader.js
index f7a9c1c..0dc0345 100644
--- a/src/util/scriptLoader.js
+++ b/src/util/scriptLoader.js
@@ -1,6 +1,6 @@
// Nice script loader from here: https://stackoverflow.com/questions/538745/how-to-tell-if-a-script-tag-failed-to-load
-export function scriptLoader() {}
+export default function scriptLoader() {}
scriptLoader.prototype = {
timer: function(
@@ -100,6 +100,6 @@ scriptLoader.prototype = {
}
}, 1);
}, typeof args.delay == 'number' ? args.delay : 1);
- return true;
+ return scriptTag;
}
};
diff --git a/src/util/updateFields.js b/src/util/updateFields.js
new file mode 100644
index 0000000..4e46ea9
--- /dev/null
+++ b/src/util/updateFields.js
@@ -0,0 +1,8 @@
+export default function updateFields(version) {
+ this.controls.mainFunction.node().value = this.chartingApplication.main;
+ this.controls.subFunction.node().value = this.chartingApplication.sub;
+ this.controls.schema.node().value = this.chartingApplication.schema;
+ this.controls.dataFileSelect
+ .selectAll('option')
+ .property('selected', d => this.chartingApplication.defaultData === d.label);
+}
diff --git a/src/util/updateSelect.js b/src/util/updateSelect.js
new file mode 100644
index 0000000..4c09e05
--- /dev/null
+++ b/src/util/updateSelect.js
@@ -0,0 +1,8 @@
+export default function updateSelect(select, data) {
+ select
+ .selectAll('option')
+ .data(data)
+ .enter()
+ .append('option')
+ .text(d => d.label);
+}
diff --git a/src/util/utilities.js b/src/util/utilities.js
new file mode 100644
index 0000000..9e25491
--- /dev/null
+++ b/src/util/utilities.js
@@ -0,0 +1,15 @@
+import getVersions from './getVersions';
+import updateSelect from './updateSelect';
+import loadPackageJSON from './loadPackageJSON';
+import loadFiles from './loadFiles';
+import scriptLoader from './scriptLoader';
+import updateFields from './updateFields';
+
+export default {
+ getVersions,
+ updateSelect,
+ loadPackageJSON,
+ loadFiles,
+ scriptLoader,
+ updateFields,
+}
diff --git a/test-page/handsontable/index.css b/test-page/handsontable/index.css
new file mode 100644
index 0000000..7819db5
--- /dev/null
+++ b/test-page/handsontable/index.css
@@ -0,0 +1,33 @@
+@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300);
+* {
+ padding: 0;
+ margin: 0;
+ font-family: 'Open Sans';
+}
+#title {
+ width: 96%;
+ padding: 0 0 12px 0;
+ border-bottom: 2px solid lightgray;
+ margin: 24px 2% 12px 2%;
+ font-size: 32px;
+ font-weight: normal;
+}
+#subtitle {
+ width: 96%;
+ margin: 0 2% 12px 2%;
+ font-size: 24px;
+ font-weight: lighter;
+}
+#container {
+ width: 96%;
+ margin: 12px 2%;
+ display: inline-block;
+}
+#chart {
+ width: 50%;
+ float: left;
+}
+#table {
+ width: 50%;
+ float: right;
+}
diff --git a/test-page/handsontable/index.html b/test-page/handsontable/index.html
new file mode 100644
index 0000000..26e79f0
--- /dev/null
+++ b/test-page/handsontable/index.html
@@ -0,0 +1,27 @@
+
+
+
+ Hands-on Table: Test Page
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hands-on Table
+ Test Page
+
+
+
+
+
diff --git a/test-page/handsontable/index.js b/test-page/handsontable/index.js
new file mode 100644
index 0000000..55b08b5
--- /dev/null
+++ b/test-page/handsontable/index.js
@@ -0,0 +1,92 @@
+fetch('https://raw.githubusercontent.com/RhoInc/data-library/master/data/miscellaneous/climate-data.csv')
+ .then(response => response.text())
+ .then(text => {
+ //const chartSettings = {
+ // x: {
+ // type: 'linear',
+ // column: 'sepal width',
+ // label: 'Sepal Width'
+ // },
+ // y: {
+ // type: 'linear',
+ // column: 'sepal length',
+ // label: 'Sepal Length'
+ // },
+ // marks: [
+ // {
+ // type: 'circle',
+ // per: ['species', 'sepal width', 'sepal length'],
+ // tooltip: '[species]: $x/$y',
+ // attributes: {
+ // stroke: 'black'
+ // }
+ // }
+ // ],
+ // color_by: 'species',
+ // color_dom: null,
+ // legend: {
+ // label: 'Species',
+ // location: 'top'
+ // },
+ // resizable: false
+ //};
+ //const controls = new webCharts.createControls(
+ // document.getElementById('chart'),
+ // {
+ // inputs: [
+ // {
+ // type: 'subsetter',
+ // value_col: 'species',
+ // label: 'Species'
+ // }
+ // ]
+ // }
+ //);
+ //const chart = new webCharts.createChart(
+ // document.getElementById('chart'),
+ // chartSettings,
+ // controls
+ //);
+ const data = d3.csv.parse(text);
+ console.log(data);
+ //chart.init(data);
+ const colHeaders = Object.keys(data[0]);
+ const table = new Handsontable(
+ document.getElementById('table'),
+ {
+ data: data.map(d => Object.keys(d).map(key => d[key])),
+ colHeaders,
+ columns: colHeaders.map(_ => { return {name: _, type: 'text'}; }),
+ rowHeaders: true,
+ dropdownMenu: true,
+ filters: true,
+ //afterChange: function(changes) {
+ // const updatedData = this.getData()
+ // .map(d => {
+ // return d.reduce(
+ // (acc,cur,i) => {
+ // acc[colHeaders[i]] = cur;
+ // return acc;
+ // },
+ // {}
+ // );
+ // });
+ // chart.draw(updatedData);
+ //},
+ //afterFilter: function(changes) {
+ // const updatedData = this.getData()
+ // .map(d => {
+ // return d.reduce(
+ // (acc,cur,i) => {
+ // acc[colHeaders[i]] = cur;
+ // return acc;
+ // },
+ // {}
+ // );
+ // });
+ // chart.draw(updatedData);
+ //},
+ licenseKey: 'non-commercial-and-evaluation',
+ },
+ );
+ });