diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..0339d5d
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+ "presets": ["env"]
+}
diff --git a/build/queryOverview.js b/build/queryOverview.js
index e54fd94..ef5a7ac 100644
--- a/build/queryOverview.js
+++ b/build/queryOverview.js
@@ -1,13 +1,12 @@
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
- ? (module.exports = factory(require('d3'), require('webcharts')))
+ ? (module.exports = factory(require('webcharts')))
: typeof define === 'function' && define.amd
- ? define(['d3', 'webcharts'], factory)
- : (global.queryOverview = factory(global.d3, global.webCharts));
-})(this, function(d3$1, webcharts) {
+ ? define(['webcharts'], factory)
+ : (global.queryOverview = factory(global.webCharts));
+})(this, function(webcharts) {
'use strict';
- //[].find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function value(predicate) {
@@ -52,13 +51,9 @@
});
}
- //{}.assign
if (typeof Object.assign != 'function') {
Object.defineProperty(Object, 'assign', {
value: function assign(target, varArgs) {
- // .length of function is 2
- 'use strict';
-
if (target == null) {
// TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
@@ -87,7 +82,6 @@
});
}
- //[].findIndex
if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
value: function value(predicate) {
@@ -132,10 +126,8 @@
});
}
- d3.selection.prototype.moveToFront = function() {
- return this.each(function() {
- this.parentNode.appendChild(this);
- });
+ Array.prototype.flatMap = function(lambda) {
+ return Array.prototype.concat.apply([], this.map(lambda));
};
d3.selection.prototype.moveToBack = function() {
@@ -145,6 +137,12 @@
});
};
+ d3.selection.prototype.moveToFront = function() {
+ return this.each(function() {
+ this.parentNode.appendChild(this);
+ });
+ };
+
var _typeof =
typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol'
? function(obj) {
@@ -159,122 +157,6 @@
: typeof obj;
};
- var asyncGenerator = (function() {
- function AwaitValue(value) {
- this.value = value;
- }
-
- function AsyncGenerator(gen) {
- var front, back;
-
- function send(key, arg) {
- return new Promise(function(resolve, reject) {
- var request = {
- key: key,
- arg: arg,
- resolve: resolve,
- reject: reject,
- next: null
- };
-
- if (back) {
- back = back.next = request;
- } else {
- front = back = request;
- resume(key, arg);
- }
- });
- }
-
- function resume(key, arg) {
- try {
- var result = gen[key](arg);
- var value = result.value;
-
- if (value instanceof AwaitValue) {
- Promise.resolve(value.value).then(
- function(arg) {
- resume('next', arg);
- },
- function(arg) {
- resume('throw', arg);
- }
- );
- } else {
- settle(result.done ? 'return' : 'normal', result.value);
- }
- } catch (err) {
- settle('throw', err);
- }
- }
-
- function settle(type, value) {
- switch (type) {
- case 'return':
- front.resolve({
- value: value,
- done: true
- });
- break;
-
- case 'throw':
- front.reject(value);
- break;
-
- default:
- front.resolve({
- value: value,
- done: false
- });
- break;
- }
-
- front = front.next;
-
- if (front) {
- resume(front.key, front.arg);
- } else {
- back = null;
- }
- }
-
- this._invoke = send;
-
- if (typeof gen.return !== 'function') {
- this.return = undefined;
- }
- }
-
- if (typeof Symbol === 'function' && Symbol.asyncIterator) {
- AsyncGenerator.prototype[Symbol.asyncIterator] = function() {
- return this;
- };
- }
-
- AsyncGenerator.prototype.next = function(arg) {
- return this._invoke('next', arg);
- };
-
- AsyncGenerator.prototype.throw = function(arg) {
- return this._invoke('throw', arg);
- };
-
- AsyncGenerator.prototype.return = function(arg) {
- return this._invoke('return', arg);
- };
-
- return {
- wrap: function(fn) {
- return function() {
- return new AsyncGenerator(fn.apply(this, arguments));
- };
- },
- await: function(value) {
- return new AwaitValue(value);
- }
- };
- })();
-
function clone(obj) {
var copy = void 0;
@@ -310,90 +192,106 @@
throw new Error('Unable to copy [obj]! Its type is not supported.');
}
- var rendererSettings = {
- //query variables
- form_col: 'formoid',
- formDescription_col: 'ecrfpagename',
- field_col: 'fieldname',
- fieldDescription_col: 'fieldname', //there is not a dscriptive column in the test data prescribed by heather
- marking_group_col: 'markinggroup',
- visit_col: 'folderoid',
-
- //query open time settings
- open_col: 'open_time',
- open_category_col: 'Query Open Time Category',
- open_category_order: ['0-7 days', '8-14 days', '15-30 days', '>30 days'],
-
- //query age settings
- age_col: 'qdays',
- age_category_col: 'Query Age Category',
- age_category_order: null,
- age_category_colors: [
- '#fd8d3c',
- '#fc4e2a',
- '#e31a1c',
- '#bd0026',
- '#800026',
- '#1f78b4',
- 'gray'
- ],
-
- //query status settings
- status_col: 'querystatus',
- status_order: ['Open', 'Answered', 'Closed', 'Cancelled'],
- status_colors: ['#fb9a99', '#fdbf6f', '#1f78b4', 'gray'],
-
- groups: null,
- status_groups: null,
- site_col: 'sitename',
- filters: null,
- details: null,
- dropdown_size: 6,
- cutoff: 'All',
- alphabetize: true,
- exportable: true,
- nRowsPerPage: 10
- };
+ function rendererSettings() {
+ return {
+ //query variables
+ form_col: 'formoid',
+ formDescription_col: 'ecrfpagename',
+ field_col: 'fieldname',
+ fieldDescription_col: null,
+ site_col: 'sitename',
+ marking_group_col: 'markinggroup',
+ visit_col: 'folderoid',
+ color_by_col: 'queryage', // options: [ 'queryage' , 'querystatus' ] or any of status_groups[].value_col
+
+ //query age
+ age_statuses: ['Open'],
+ age_col: 'qdays',
+ age_cutoffs: [14, 28, 56, 112],
+ age_range_colors: [
+ '#ffffcc',
+ '#ffeda0',
+ '#fed976',
+ '#feb24c',
+ '#fd8d3c',
+ '#fc4e2a',
+ '#e31a1c',
+ '#bd0026',
+ '#800026'
+ ],
+
+ //query status
+ status_col: 'querystatus',
+ status_order: ['Open', 'Answered', 'Closed', 'Cancelled'],
+ status_colors: ['#fd8d3c', '#4daf4a', '#377eb8', '#999999'],
+
+ //query recency
+ recency_category_col: 'open_time',
+ recency_col: 'odays',
+ recency_cutoffs: [7, 14, 30],
+
+ //miscellany
+ groups: null,
+ status_groups: null,
+ filters: null,
+ dropdown_size: 6,
+ details: null,
+ bar_arrangement: 'stacked',
+ cutoff: 'All',
+ alphabetize: true,
+ truncate_listing_cells: true,
+ truncation_cutoff: 100
+ };
+ }
- var webchartsSettings = {
- x: {
- label: '# of Queries',
- behavior: 'flex'
- },
- y: {
- type: 'ordinal',
- column: null, // set in syncSettings()
- label: 'Form',
- sort: 'total-descending'
- },
- marks: [
- {
- type: 'bar',
- per: [null], // set in syncSettings()
- split: null, // set in syncSettings()
- arrange: 'stacked',
- summarizeX: 'count',
- tooltip: null // set in syncSettings()
- }
- ],
- color_by: null, // set in syncSettings()
- color_dom: null, // set in syncSettings()
- legend: {
- location: 'top',
- // label: 'Query Status',
- label: null,
- order: null // set in syncSettings()
- },
- range_band: 15,
- margin: {
- right: '50' // room for count annotation
- }
- };
+ function chartSettings() {
+ return {
+ x: {
+ label: '# of Queries',
+ column: null,
+ behavior: 'flex'
+ },
+ y: {
+ type: 'ordinal',
+ column: null, // set in syncSettings()
+ label: 'Form',
+ sort: null // set in syncSettings()
+ },
+ marks: [
+ {
+ type: 'bar',
+ per: [null], // set in syncSettings()
+ split: null, // set in syncSettings()
+ arrange: null, // set in syncSettings()
+ summarizeX: 'count',
+ tooltip: null // set in syncSettings()
+ }
+ ],
+ color_by: null, // set in syncSettings()
+ color_dom: null, // set in syncSettings()
+ legend: {
+ location: 'top',
+ label: null, // set in syncSettings()
+ order: null // set in syncSettings()
+ },
+ margin: {
+ right: '50' // room for count annotation
+ },
+ range_band: 25
+ };
+ }
+
+ function listingSettings() {
+ return {
+ nRowsPerPage: 25,
+ exportable: true
+ };
+ }
function arrayOfVariablesCheck(defaultVariables, userDefinedVariables) {
var validSetting =
userDefinedVariables instanceof Array && userDefinedVariables.length
- ? d3$1
+ ? d3
.merge([
defaultVariables,
userDefinedVariables.filter(function(item) {
@@ -411,7 +309,7 @@
})
])
.map(function(item) {
- var itemObject = {};
+ var itemObject = item instanceof Object ? Object.assign({}, item) : {};
itemObject.value_col = item instanceof Object ? item.value_col : item;
itemObject.label =
@@ -426,87 +324,202 @@
return validSetting;
}
- function syncSettings(settings) {
- var syncedSettings = clone(settings);
-
- //groups
+ function syncGroups(settings) {
var defaultGroups = [
- { value_col: syncedSettings.form_col, label: 'Form' },
+ { value_col: settings.form_col, label: 'Form' },
{ value_col: 'Form: Field', label: 'Form: Field' },
- { value_col: syncedSettings.site_col, label: 'Site' },
- { value_col: syncedSettings.marking_group_col, label: 'Marking Group' },
- { value_col: syncedSettings.visit_col, label: 'Visit/Folder' }
+ { value_col: settings.site_col, label: 'Site' },
+ { value_col: settings.marking_group_col, label: 'Marking Group' },
+ { value_col: settings.visit_col, label: 'Visit/Folder' }
];
- syncedSettings.groups = arrayOfVariablesCheck(defaultGroups, settings.groups);
+ settings.groups = settings.arrayOfVariablesCheck(defaultGroups, settings.groups);
+ }
+
+ function syncStatusGroups(settings) {
+ //age ranges
+ settings.ageRanges = settings.age_cutoffs.map(function(d, i) {
+ return i > 0 ? [settings.age_cutoffs[i - 1], d] : [0, d];
+ });
+ settings.ageRanges.push([settings.age_cutoffs[settings.age_cutoffs.length - 1], null]);
+
+ //age range categories
+ settings.ageRangeCategories = settings.age_cutoffs.every(function(age_range) {
+ return age_range % 7 === 0;
+ })
+ ? settings.ageRanges.map(function(ageRange, i) {
+ return i < settings.ageRanges.length - 1
+ ? ageRange
+ .map(function(days) {
+ return days / 7;
+ })
+ .join('-') + ' wks'
+ : '>' + ageRange[0] / 7 + ' wks';
+ })
+ : settings.ageRanges.map(function(ageRange, i) {
+ return i < settings.ageRanges.length - 1
+ ? ageRange.join('-') + ' days'
+ : '>' + ageRange[0] + ' days';
+ });
+
+ //age range colors
+ var ageRangeColors = settings.age_range_colors.slice(
+ settings.age_range_colors.length - settings.ageRanges.length
+ );
+ settings.status_order.forEach(function(status, i) {
+ if (settings.age_statuses.indexOf(status) < 0)
+ ageRangeColors.push(settings.status_colors[i]);
+ });
- //status_groups
+ //reconcile settings.status_order with settings.status_colors to ensure equal length
+ if (settings.status_order.length !== settings.status_colors.length) {
+ console.warn('The number of query statuses does not match the number of query colors:');
+ console.log(settings.status_order);
+ console.log(settings.status_colors);
+ }
+
+ //default status groups
var defaultStatusGroups = [
{
- value_col: syncedSettings.age_category_col,
- label: 'Query Age Category',
- order: syncedSettings.age_category_order,
- colors: syncedSettings.age_category_colors,
- derive_source: syncedSettings.age_col
+ value_col: 'queryage', // derived in ../chart/onInit/defineNewVariables
+ label: 'Query Age',
+ order: settings.ageRangeCategories.concat(
+ settings.status_order.filter(function(status) {
+ return settings.age_statuses.indexOf(status) < 0;
+ })
+ ),
+ colors: ageRangeColors
},
{
- value_col: syncedSettings.status_col,
+ value_col: settings.status_col,
label: 'Query Status',
- order: syncedSettings.status_order,
- colors: syncedSettings.status_colors
+ order: settings.status_order,
+ colors: settings.status_colors
}
];
- syncedSettings.status_groups = arrayOfVariablesCheck(
+
+ //add custom status groups
+ settings.status_groups = settings.arrayOfVariablesCheck(
defaultStatusGroups,
settings.status_groups
);
+ settings.status_group = settings.status_groups.find(function(status_group) {
+ return status_group.value_col === settings.color_by_col;
+ });
+ }
+ function syncWebchartsSettings(settings) {
//y-axis
- syncedSettings.y.column = syncedSettings.form_col;
+ settings.y.column = settings.form_col;
+ settings.y.sort = settings.alphabetize ? 'alphabetical-ascending' : 'total-descending';
+ settings.y.range_band = settings.range_band || 25;
+
+ //mark settings
+ settings.marks[0].per[0] = settings.form_col;
+ settings.marks[0].split = settings.status_group.value_col;
+ settings.marks[0].arrange = settings.bar_arrangement;
+ settings.marks[0].tooltip = '[' + settings.status_group.value_col + '] - $x queries';
//stratification
- syncedSettings.color_by = syncedSettings.status_groups[0].value_col;
- syncedSettings.color_dom = syncedSettings.status_groups[0].order
- ? syncedSettings.status_groups[0].order.slice()
+ settings.color_by = settings.status_group.value_col;
+ settings.color_dom = settings.status_group.order
+ ? settings.status_group.order.slice()
: null;
- syncedSettings.colors = syncedSettings.status_groups[0].colors;
- syncedSettings.legend.label = syncedSettings.status_groups[0].label;
- syncedSettings.legend.order = syncedSettings.status_groups[0].order
- ? syncedSettings.status_groups[0].order.slice()
+ settings.colors = settings.status_group.colors;
+
+ //legend
+ settings.legend.label = settings.status_group.label;
+ settings.legend.order = settings.status_group.order
+ ? settings.status_group.order.slice()
: null;
+ }
- //mark settings
- syncedSettings.marks[0].per[0] = syncedSettings.form_col;
- syncedSettings.marks[0].split = syncedSettings.color_by;
- syncedSettings.marks[0].tooltip = '[' + syncedSettings.color_by + '] - $x queries';
+ function syncFilters(settings) {
+ //recency ranges
+ settings.recencyRanges = settings.recency_cutoffs.map(function(d, i) {
+ return i > 0 ? [settings.recency_cutoffs[i - 1], d] : [0, d];
+ });
+ settings.recencyRanges.push([
+ settings.recency_cutoffs[settings.recency_cutoffs.length - 1],
+ null
+ ]);
+
+ //recency range categories
+ settings.recencyRangeCategories = settings.recency_cutoffs.every(function(recency_range) {
+ return recency_range % 7 === 0;
+ })
+ ? settings.recencyRanges.map(function(recencyRange, i) {
+ return i < settings.recencyRanges.length - 1
+ ? recencyRange
+ .map(function(days) {
+ return days / 7;
+ })
+ .join('-') + ' weeks'
+ : '>' + recencyRange[0] / 7 + ' weeks';
+ })
+ : settings.recencyRanges.map(function(recencyRange, i) {
+ return i < settings.recencyRanges.length - 1
+ ? recencyRange.join('-') + ' days'
+ : '>' + recencyRange[0] + ' days';
+ });
- //filters
+ //default filters
var defaultFilters = [
{
- value_col: syncedSettings.open_category_col,
- derive_source: syncedSettings.open_col,
- label: 'Query Open Time',
- multiple: true,
- order: syncedSettings.open_category_order
+ value_col: 'queryrecency',
+ label: 'Query Recency',
+ multiple: true
+ },
+ {
+ value_col: settings.form_col,
+ label: 'Form',
+ multiple: true
},
- { value_col: syncedSettings.form_col, label: 'Form', multiple: true },
- { value_col: syncedSettings.site_col, label: 'Site', multiple: true },
- { value_col: syncedSettings.marking_group_col, label: 'Marking Group', multiple: true },
- { value_col: syncedSettings.visit_col, label: 'Visit/Folder', multiple: true }
+ {
+ value_col: settings.site_col,
+ label: 'Site',
+ multiple: true
+ },
+ {
+ value_col: settings.marking_group_col,
+ label: 'Marking Group',
+ multiple: true
+ },
+ {
+ value_col: settings.visit_col,
+ label: 'Visit/Folder',
+ multiple: true
+ }
];
- syncedSettings.status_groups.reverse().forEach(function(status_group) {
- status_group.multiple = true;
- defaultFilters.unshift(clone(status_group));
- });
- syncedSettings.filters = arrayOfVariablesCheck(defaultFilters, settings.filters);
+ //add status group variables to list of filters
+ settings.status_groups
+ .slice()
+ .reverse()
+ .forEach(function(status_group) {
+ status_group.multiple = true;
+ defaultFilters.unshift(settings.clone(status_group));
+ });
+
+ //add custom filters
+ settings.filters = settings.arrayOfVariablesCheck(defaultFilters, settings.filters);
+ }
+
+ function syncCutoff(settings) {
+ if (!(+settings.cutoff > 0 || settings.cutoff === 'All')) settings.cutoff = 10;
+ }
- //cutoff
- if (!(+syncedSettings.cutoff > 0 || syncedSettings.cutoff === 'All'))
- syncedSettings.cutoff = 10;
+ function syncSettings(settings) {
+ var syncedSettings = Object.assign({}, clone(settings), {
+ clone: clone,
+ arrayOfVariablesCheck: arrayOfVariablesCheck
+ });
+ syncGroups(syncedSettings);
+ syncStatusGroups(syncedSettings);
+ syncWebchartsSettings(syncedSettings);
+ syncFilters(syncedSettings);
+ syncCutoff(syncedSettings);
+ syncCutoff(syncedSettings);
- //details
- syncedSettings.details = arrayOfVariablesCheck([], settings.details);
- if (syncedSettings.details.length === 0) delete syncedSettings.details;
return syncedSettings;
}
@@ -515,16 +528,14 @@
type: 'dropdown',
option: 'y.label',
label: 'Group by',
- description: 'variable toggle',
start: null, // set in syncControlInputs()
values: null, // set in syncControlInputs()
require: true
},
{
- type: 'dropdown',
+ type: 'radio',
label: 'Status Group',
- description: 'stratification',
- options: ['marks.0.split', 'color_by'], // will want to change tooltip too
+ option: 'color_by_col',
start: null, // set in syncControlInputs()
values: null, // set in syncControlInputs()
require: true
@@ -544,59 +555,259 @@
{
type: 'checkbox',
option: 'alphabetize',
- label: 'Alphabetical?'
+ label: 'Order Groups Alphabetically?'
}
];
- function syncControlInputs(controlInputs, settings) {
- var syncedControlInputs = clone(controlInputs);
-
- //Group by
- var groupByControl = syncedControlInputs.find(function(controlInput) {
+ function syncGroupBy(controlInputs, settings) {
+ var groupByControl = controlInputs.find(function(controlInput) {
return controlInput.label === 'Group by';
});
groupByControl.values = settings.groups.map(function(group) {
return group.label;
});
+ }
- //Status Group
- var statusGroupControl = syncedControlInputs.find(function(controlInput) {
+ function syncStatusGroup(controlInputs, settings) {
+ var statusGroupControl = controlInputs.find(function(controlInput) {
return controlInput.label === 'Status Group';
});
+ statusGroupControl.start = settings.color_by;
statusGroupControl.values = settings.status_groups.map(function(status_group) {
- return status_group.value_col;
+ return status_group.label;
});
+ }
- //filters
+ function syncFilters$1(controlInputs, settings) {
settings.filters.forEach(function(filter, i) {
filter.type = 'subsetter';
- filter.description = 'filter';
- syncedControlInputs.splice(2 + i, 0, filter);
+ controlInputs.splice(2 + i, 0, filter);
});
+ }
- //Show First N Groups
- var nGroupsControl = syncedControlInputs.find(function(controlInput) {
+ function syncShowFirstNGroups(controlInputs, settings) {
+ var nGroupsControl = controlInputs.find(function(controlInput) {
return controlInput.label === 'Show First N Groups';
});
if (nGroupsControl.values.indexOf(settings.cutoff.toString()) === -1) {
+ settings.cutoff = settings.cutoff.toString();
nGroupsControl.values.push(settings.cutoff.toString());
nGroupsControl.values.sort(function(a, b) {
return a === 'All' ? 1 : b === 'All' ? -1 : +a - +b;
});
} else settings.cutoff = settings.cutoff.toString() || nGroupsControl.values[0];
+ }
+
+ function syncControlInputs(controlInputs, settings) {
+ var syncedControlInputs = settings.clone(controlInputs);
+ syncGroupBy(syncedControlInputs, settings);
+ syncStatusGroup(syncedControlInputs, settings);
+ syncFilters$1(syncedControlInputs, settings);
+ syncShowFirstNGroups(syncedControlInputs, settings);
return syncedControlInputs;
}
var configuration = {
rendererSettings: rendererSettings,
- webchartsSettings: webchartsSettings,
- settings: Object.assign({}, rendererSettings, webchartsSettings),
+ chartSettings: chartSettings,
+ listingSettings: listingSettings,
+ settings: Object.assign({}, rendererSettings(), chartSettings(), listingSettings()),
syncSettings: syncSettings,
controlInputs: controlInputs,
syncControlInputs: syncControlInputs
};
+ function layout(element) {
+ var containers = {
+ main: d3
+ .select(element)
+ .append('div')
+ .classed('.query-overview', true)
+ };
+
+ containers.topRow = containers.main.append('div').classed('qo-row qo-row--top', true);
+ containers.controls = containers.topRow
+ .append('div')
+ .classed('qo-component qo-component--controls', true);
+ containers.chart = containers.controls
+ .append('div')
+ .classed('qo-component qo-component--chart', true);
+ containers.bottomRow = containers.main.append('div').classed('qo-row qo-row--bottom', true);
+ containers.listing = containers.bottomRow
+ .append('div')
+ .classed('qo-component qo-component--listing', true);
+
+ return containers;
+ }
+
+ function styles() {
+ var styles = [
+ /***--------------------------------------------------------------------------------------\
+ Framework
+ \--------------------------------------------------------------------------------------***/
+
+ '.query-overview {' + ' width: 100%;' + ' display: inline-block;' + '}',
+ '.qo-row {' + ' width: 100%;' + ' display: inline-block;' + '}',
+ '.qo-component {' + '}',
+ '.qo-row--top {' + '}',
+ '.qo-row--bottom {' + '}',
+
+ /***--------------------------------------------------------------------------------------\
+ Controls
+ \--------------------------------------------------------------------------------------***/
+
+ '.qo-component--controls {' + ' width: 100%;' + '}',
+ '.qo-component--controls .wc-controls {' + ' margin-bottom: 0;' + '}',
+ '.qo-control-grouping {' + ' display: inline-block;' + '}',
+ '.qo-button {' + ' float: left;' + ' display: block;' + '}',
+ '.qo-control-grouping--label,' +
+ '.wc-control-label {' +
+ ' cursor: help;' +
+ ' margin-bottom: 3px;' +
+ '}',
+ '.qo-control-grouping--label {' +
+ ' text-align: center;' +
+ ' width: 100%;' +
+ ' font-size: 24px;' +
+ ' border-bottom: 2px solid #aaa;' +
+ '}',
+ '.span-description {' + ' display: none !important;' + '}',
+
+ /****---------------------------------------------------------------------------------\
+ Other controls
+ \---------------------------------------------------------------------------------****/
+
+ '.qo-control-grouping--other-controls {' +
+ ' width: 20%;' +
+ ' float: right;' +
+ '}',
+ '.qo-control-grouping--other-controls .control-group {' +
+ ' width: 100%;' +
+ ' margin-bottom: 15px;' +
+ '}',
+ '.qo-control-grouping--other-controls .control-group:nth-child(n+3) {' +
+ ' border-top: 1px solid #aaa;' +
+ '}',
+ '.qo-control-grouping--other-controls .control-group .wc-control-label {' +
+ ' text-align: center;' +
+ ' font-size: 110%;' +
+ '}',
+
+ //dropdowns
+ '.qo-dropdown {' + '}',
+ '.qo-dropdown .wc-control-label {' + '}',
+ '.qo-dropdown .changer {' + ' margin: 0 auto;' + '}',
+
+ //radio buttons
+ '.qo-radio {' +
+ ' display: flex !important;' +
+ ' justify-content: center;' +
+ ' flex-wrap: wrap;' +
+ '}',
+ '.qo-radio .wc-control-label {' + ' width: 100%;' + '}',
+ '.qo-radio .radio {' + ' margin-top: 0 !important;' + '}',
+
+ //checkboxes
+ '.qo-checkbox {' +
+ ' display: flex !important;' +
+ ' justify-content: center;' +
+ '}',
+ '.qo-checkbox .wc-control-label {' + ' margin-right: 5px;' + '}',
+ '.qo-checkbox .changer {' + ' margin-top: 5px !important;' + '}',
+
+ /****---------------------------------------------------------------------------------\
+ Filters
+ \---------------------------------------------------------------------------------****/
+
+ '.qo-control-grouping--filters {' +
+ ' width: 20%;' +
+ ' float: left;' +
+ ' display: flex;' +
+ ' flex-wrap: wrap;' +
+ ' justify-content: space-evenly;' +
+ '}',
+ '.qo-subsetter {' +
+ ' margin: 5px 2px !important;' +
+ ' border-top: 1px solid #aaa;' +
+ ' padding-top: 5px;' +
+ '}',
+ '.qo-subsetter .wc-control-label {' +
+ ' margin: 0 5px 3px 0;' +
+ ' text-align: center;' +
+ '}',
+ '.qo-select-all {' + '}',
+ '.qo-subsetter .changer {' + ' margin: 0 auto;' + '}',
+
+ /***--------------------------------------------------------------------------------------\
+ Chart
+ \--------------------------------------------------------------------------------------***/
+
+ '.qo-component--chart {' +
+ ' width: 58%;' +
+ ' margin: 0 auto;' +
+ ' position: relative;' +
+ '}',
+ '.qo-button--reset-chart {' +
+ ' position: absolute;' +
+ ' top: 0;' +
+ ' left: 0;' +
+ ' z-index: 2;' +
+ ' width: 91px;' +
+ ' padding: 3px 0;' +
+ '}',
+ '.qo-button--undo {' +
+ ' position: absolute;' +
+ ' bottom: 0;' +
+ ' left: 0;' +
+ ' z-index: 2;' +
+ ' width: 91px;' +
+ ' padding: 3px 0;' +
+ '}',
+ '.qo-component--chart .wc-chart {' + ' z-index: 1;' + '}',
+ '.qo-component--chart .legend-title {' + ' cursor: help;' + '}',
+ '.qo-component--chart .legend-item {' +
+ ' cursor: pointer;' +
+ ' border-radius: 4px;' +
+ ' padding: 5px;' +
+ ' padding-left: 8px;' +
+ ' margin-right: 5px !important;' +
+ '}',
+ '.qo-footnote {' +
+ ' width: 100%;' +
+ ' text-align: center;' +
+ ' font-style: italic;' +
+ '}',
+
+ /***--------------------------------------------------------------------------------------\
+ Listing
+ \--------------------------------------------------------------------------------------***/
+
+ '.qo-component--listing {' + ' width: 100%;' + '}',
+ '.qo-button--reset-listing {' +
+ ' padding: 3px;' +
+ ' margin: 10px 5px 10px 0;' +
+ '}',
+ '.qo-table-container {' +
+ ' overflow-x: auto;' +
+ ' width: 100%;' +
+ ' transform: rotate(180deg);' +
+ ' -webkit-transform: rotate(180deg); ' +
+ '}',
+ '.qo-table {' +
+ ' width: 100%;' +
+ ' transform: rotate(180deg);' +
+ ' -webkit-transform: rotate(180deg); ' +
+ '}'
+ ];
+
+ //Attach styles to DOM.
+ var style = document.createElement('style');
+ style.type = 'text/css';
+ style.innerHTML = styles.join('\n');
+ document.getElementsByTagName('head')[0].appendChild(style);
+ }
+
function defineListingSettings() {
this.listing.config.cols = this.config.details
? this.config.details.map(function(d) {
@@ -617,60 +828,53 @@
function defineNewVariables() {
var _this = this;
+ var queryAgeCol = this.config.status_groups.find(function(status_group) {
+ return status_group.label === 'Query Age';
+ }).value_col;
+ var queryRecencyCol = this.config.filters.find(function(filter) {
+ return filter.label === 'Query Recency';
+ }).value_col;
+
this.raw_data.forEach(function(d) {
+ //Concatenate form and field to avoid duplicates across forms.
d['Form: Field'] = d[_this.config.form_col] + ': ' + d[_this.config.field_col];
- //Define query age category.
- if (!_this.config.age_category_order) {
- var queryAge =
- /^ *\d+ *$/.test(d[_this.config.age_col]) &&
- ['Closed', 'Cancelled'].indexOf(d[_this.config.status_col]) < 0
- ? +d[_this.config.age_col]
- : NaN;
- switch (true) {
- case queryAge <= 14:
- d['Query Age Category'] = '0-2 weeks';
- break;
- case queryAge <= 28:
- d['Query Age Category'] = '2-4 weeks';
- break;
- case queryAge <= 56:
- d['Query Age Category'] = '4-8 weeks';
- break;
- case queryAge <= 112:
- d['Query Age Category'] = '8-16 weeks';
- break;
- case queryAge > 112:
- d['Query Age Category'] = '>16 weeks';
- break;
- default:
- d['Query Age Category'] = d[_this.config.status_col];
- break;
- }
+ //Define query age.
+ if (_this.config.age_statuses.indexOf(d[_this.config.status_col]) < 0)
+ d[queryAgeCol] = d[_this.config.status_col];
+ else {
+ var age = +d[_this.config.age_col];
+ _this.config.ageRanges.forEach(function(ageRange, i) {
+ if (i === 0 && ageRange[0] <= age && age <= ageRange[1])
+ d[queryAgeCol] = _this.config.ageRangeCategories[i];
+ else if (i === _this.config.ageRanges.length - 1 && ageRange[0] < age)
+ d[queryAgeCol] = _this.config.ageRangeCategories[i];
+ else if (ageRange[0] < age && age <= ageRange[1])
+ d[queryAgeCol] = _this.config.ageRangeCategories[i];
+ });
}
- //Define query open time category.
- var openTime = /^ *\d+ *$/.test(d[_this.config.open_col])
- ? +d[_this.config.open_col]
- : NaN;
- switch (true) {
- case openTime <= 7:
- d['Query Open Time Category'] = '0-7 days';
- break;
- case openTime <= 14:
- d['Query Open Time Category'] = '8-14 days';
- break;
- case openTime <= 30:
- d['Query Open Time Category'] = '15-30 days';
- break;
- default:
- d['Query Open Time Category'] = '>30 days';
- break;
+ //Define query recency.
+ if (d.hasOwnProperty(_this.config.recency_category_col)) {
+ d[queryRecencyCol] = d[_this.config.recency_category_col] || 'N/A';
+ } else if (d.hasOwnProperty(_this.config.recency_col)) {
+ var recency = +d[_this.config.recency_col];
+ _this.config.recencyRanges.forEach(function(recencyRange, i) {
+ if (i === 0 && recencyRange[0] <= recency && recency <= recencyRange[1])
+ d[queryRecencyCol] = _this.config.recencyRangeCategories[i];
+ else if (
+ i === _this.config.recencyRanges.length - 1 &&
+ recencyRange[0] < recency
+ )
+ d[queryRecencyCol] = _this.config.recencyRangeCategories[i];
+ else if (recencyRange[0] < recency && recency <= recencyRange[1])
+ d[queryRecencyCol] = _this.config.recencyRangeCategories[i];
+ });
}
});
}
- function defineQueryStatuses() {
+ function defineQueryStatusSet() {
var _this = this;
var queryStatusInput = this.controls.config.inputs.find(function(input) {
@@ -679,74 +883,61 @@
var queryStatusGroup = this.config.status_groups.find(function(status_group) {
return status_group.value_col === _this.config.status_col;
});
- var queryStatusOrder = Array.isArray(queryStatusGroup.order)
- ? queryStatusGroup.order.concat(
- d3
+ var queryStatusOrder =
+ Array.isArray(queryStatusGroup.order) && queryStatusGroup.order.length
+ ? queryStatusGroup.order.concat(
+ d3
+ .set(
+ this.raw_data.map(function(d) {
+ return d[_this.config.status_col];
+ })
+ )
+ .values()
+ .filter(function(value) {
+ return queryStatusGroup.order.indexOf(value) < 0;
+ })
+ .sort()
+ )
+ : d3
.set(
this.raw_data.map(function(d) {
return d[_this.config.status_col];
})
)
.values()
- .filter(function(value) {
- return queryStatusGroup.order.indexOf(value) < 0;
- })
- .sort()
- )
- : d3
- .set(
- this.raw_data.map(function(d) {
- return d[_this.config.status_col];
- })
- )
- .values()
- .sort();
+ .sort();
queryStatusInput.order = queryStatusOrder;
queryStatusGroup.order = queryStatusOrder;
}
- function defineQueryAgeCategories() {
- var _this = this;
-
- var queryAgeCategoryInput = this.controls.config.inputs.find(function(input) {
- return input.value_col === _this.config.age_category_col;
+ function defineQueryRecencySet() {
+ var queryRecencyInput = this.controls.config.inputs.find(function(input) {
+ return input.value_col === 'queryrecency';
});
- var queryAgeCategoryGroup = this.config.status_groups.find(function(age_category_group) {
- return age_category_group.value_col === _this.config.age_category_col;
- });
- var queryStatusOrder = this.config.status_groups.find(function(status_group) {
- return status_group.value_col === _this.config.status_col;
- }).order;
- var queryAgeCategoryOrder = Array.isArray(queryAgeCategoryGroup.order)
- ? queryAgeCategoryGroup.order.concat(
- d3
- .set(
- this.raw_data.map(function(d) {
- return d[_this.config.age_category_col];
- })
- )
- .values()
- .filter(function(value) {
- return queryAgeCategoryGroup.order.indexOf(value) < 0;
- })
- .sort()
- )
- : d3
- .set(
- this.raw_data.map(function(d) {
- return d[_this.config.age_category_col];
- })
- )
- .values()
- .sort(function(a, b) {
- var aIndex = queryStatusOrder.indexOf(a);
- var bIndex = queryStatusOrder.indexOf(b);
- var diff = aIndex - bIndex;
- return diff ? diff : a < b ? -1 : 1;
- });
- queryAgeCategoryInput.order = queryAgeCategoryOrder;
- queryAgeCategoryGroup.order = queryAgeCategoryOrder;
+ if (this.raw_data[0].hasOwnProperty(this.config.recency_category_col)) {
+ queryRecencyInput.values = d3
+ .set(
+ this.raw_data.map(function(d) {
+ return d.queryrecency;
+ })
+ )
+ .values()
+ .sort(function(a, b) {
+ var anum = parseFloat(a);
+ var bnum = parseFloat(b);
+ var diff = anum - bnum;
+ return diff ? diff : a < b ? -1 : a > b ? 1 : 0;
+ });
+ } else if (this.raw_data[0].hasOwnProperty(this.config.recency_col))
+ queryRecencyInput.values = this.config.recencyRangeCategories;
+ else
+ this.controls.config.inputs.splice(
+ this.controls.config.inputs.findIndex(function(input) {
+ return input.value_col === 'queryrecency';
+ }),
+ 1
+ );
}
function removeInvalidControls() {
@@ -768,10 +959,10 @@
defineNewVariables.call(this);
//Define query statuses.
- defineQueryStatuses.call(this);
+ defineQueryStatusSet.call(this);
- //Define query age categories.
- defineQueryAgeCategories.call(this);
+ //Define query recency categories.
+ defineQueryRecencySet.call(this);
//Define detail listing settings.
defineListingSettings.call(this);
@@ -783,10 +974,125 @@
this.listing.init(this.raw_data);
}
+ function classControls() {
+ this.controls.controlGroups = this.controls.wrap
+ .selectAll('.control-group')
+ .attr('class', function(d) {
+ return (
+ 'control-group qo-' +
+ d.type +
+ ' qo-' +
+ d.type +
+ '--' +
+ d.label.toLowerCase().replace(/[^_a-zA-Z-]/g, '-')
+ );
+ });
+ }
+
+ function groupControls() {
+ var context = this;
+
+ //Group filters.
+ this.controls.filters = {
+ container: this.controls.wrap
+ .insert('div', '.qo-subsetter')
+ .classed('qo-control-grouping qo-control-grouping--filters', true)
+ };
+ this.controls.filters.container
+ .append('div')
+ .classed('qo-control-grouping--label', true)
+ .attr(
+ 'title',
+ 'Filters subset the data underlying the chart and listing.\nHover over filter labels to view more information about them.'
+ )
+ .text('Filters');
+ this.controls.filters.controlGroups = this.controls.wrap.selectAll('.qo-subsetter');
+ this.controls.filters.labels = this.controls.filters.controlGroups.selectAll(
+ '.wc-control-label'
+ );
+ this.controls.filters.selects = this.controls.filters.controlGroups.selectAll('.changer');
+ this.controls.filters.controlGroups.each(function(d) {
+ context.controls.filters.container.node().appendChild(this);
+ });
+
+ //Group other controls.
+ this.controls.otherControls = {
+ container: this.controls.wrap
+ .insert('div', ':first-child')
+ .classed('qo-control-grouping qo-control-grouping--other-controls', true)
+ };
+ this.controls.otherControls.label = this.controls.otherControls.container
+ .append('div')
+ .classed('qo-control-grouping--label', true)
+ .attr(
+ 'title',
+ 'Controls alter the display of the chart.\nHover over control labels to view more information about them.'
+ )
+ .text('Controls');
+ this.controls.otherControls.controlGroups = this.controls.wrap.selectAll(
+ '.control-group:not(.qo-subsetter)'
+ );
+ this.controls.otherControls.labels = this.controls.otherControls.controlGroups.selectAll(
+ '.wc-control-label'
+ );
+ this.controls.otherControls.controlGroups.each(function(d) {
+ context.controls.otherControls.container.node().appendChild(this);
+ });
+ }
+
+ function addControlTooltips() {
+ var tooltips = {
+ //other controls
+ 'Group by':
+ 'Controls the variable with which the queries are grouped; each group is plotted along the vertical axis of the chart.',
+ 'Status Group': 'Controls the variable with which the bars are subdivided.',
+ 'Bar Arrangement':
+ 'Controls the layout of the status groups.\n- stacked: status groups are plotted side-by-side horizontally\n- grouped: status groups are plotted side-by-side vertically',
+ 'Show First N Groups': 'Controls the number of groups displayed on the vertical axis.',
+ 'Order Groups Alphabetically?':
+ 'Controls the order of the groups; uncheck to sort groups by magnitude (largest to smallest number of queries) instead of alphabetically.',
+
+ //filters
+ 'Query Age':
+ 'Open queries are broken down into how long they have been open. All other queries are classified by status (answered, closed, cancelled).',
+ 'Query Status':
+ 'Open: site has not responded to the issue\nAnswered: site has responded to issue; DM needs to review\nClosed: issue resolved\nCancelled: query cancelled by DM',
+ 'Query Recency':
+ 'Number of days a query has been open, regardless of its current status (applies only to queries opened in the past 30 days)',
+ Form:
+ 'CRF page abbreviation; hover over the abbreviation in the chart to see its full name.',
+ Site: 'Name or ID of site',
+ 'Marking Group': 'Entity that opened the query',
+ 'Visit/Folder':
+ 'Visit/folder abbreviation; hover over the visit/folder abbreviation in the chart to see the full name.'
+ };
+ this.controls.controlGroups.each(function(d) {
+ var tooltip =
+ tooltips[d.label] ||
+ 'This ' +
+ d.type +
+ ' controls ' +
+ (d.value_col || d.option || d.options.join(', ')) +
+ '.';
+ if (tooltips[d.label] === undefined)
+ console.warn(
+ 'The control labeled ' +
+ d.label +
+ ' does not have a curated tooltip. Defaulting to ' +
+ tooltip +
+ '.'
+ );
+ d3
+ .select(this)
+ .selectAll('.wc-control-label')
+ .attr('title', tooltip);
+ });
+ }
+
function updateGroupByOptions() {
var context = this;
- var groupByControl = this.controls.wrap
+ this.controls.wrap
.selectAll('.control-group select')
.filter(function(d) {
return d.label === 'Group by';
@@ -810,6 +1116,64 @@
});
}
+ function addGroupByHighlight() {
+ var _this = this;
+
+ this.controls.otherControls.controlGroups
+ .filter(function(d) {
+ return d.label === 'Group by';
+ })
+ .on('mouseover', function() {
+ _this.svg.selectAll('.y.axis .axis-title').style({
+ 'font-weight': 'bold',
+ 'text-decoration': 'underline',
+ fill: 'red'
+ });
+ })
+ .on('mouseout', function() {
+ _this.svg.selectAll('.y.axis .axis-title').style({
+ 'font-weight': 'normal',
+ 'text-decoration': 'none',
+ fill: 'black'
+ });
+ });
+ }
+
+ function checkInitialStatusGroup() {
+ var _this = this;
+
+ this.controls.otherControls.controlGroups
+ .filter(function(d) {
+ return d.label === 'Status Group';
+ })
+ .selectAll('.radio')
+ .selectAll('.changer')
+ .property('checked', function(d) {
+ return d === _this.config.legend.label;
+ });
+ }
+
+ function addStatusGroupHighlight() {
+ var _this = this;
+
+ this.controls.otherControls.controlGroups
+ .filter(function(d) {
+ return d.label === 'Status Group';
+ })
+ .on('mouseover', function() {
+ _this.legend.select('.legend-title').style({
+ 'text-decoration': 'underline',
+ color: 'red'
+ });
+ })
+ .on('mouseout', function() {
+ _this.legend.select('.legend-title').style({
+ 'text-decoration': 'none',
+ color: 'black'
+ });
+ });
+ }
+
function customizeMultiSelects() {
var context = this;
@@ -844,6 +1208,97 @@
});
}
+ function addSelectAll() {
+ var context = this;
+
+ this.controls.filters.labels
+ .filter(function(d) {
+ return d.multiple;
+ })
+ .each(function(d) {
+ var label = d3
+ .select(this)
+ .html(d.label + ' ');
+ var checkbox = label
+ .select('input')
+ .datum(d)
+ .attr('title', 'Deselect all ' + d.label + ' options')
+ .property('checked', true)
+ .on('click', function(di) {
+ var checkbox = d3.select(this);
+ var checked = this.checked;
+
+ //Update checkbox tooltip.
+ checkbox.attr(
+ 'title',
+ checked
+ ? 'Deselect all ' + di.label + ' options'
+ : 'Select all ' + di.label + ' options'
+ );
+
+ //Update filter object.
+ var filter = context.filters.find(function(filter) {
+ return filter.col === di.value_col;
+ });
+ if (checked) filter.val = filter.choices;
+ else filter.val = [];
+
+ //Redraw.
+ context.draw();
+ });
+ });
+ this.controls.filters.checkboxes = this.controls.filters.labels.selectAll('.qo-select-all');
+ }
+
+ function updateSelectAll(d, selectedOptions) {
+ //Update filter object.
+ var filter = this.filters.find(function(filter) {
+ return filter.col === d.value_col;
+ });
+ filter.val = d.multiple ? selectedOptions : selectedOptions.pop();
+
+ //Update checkbox.
+ if (d.multiple) {
+ var checked = filter.val.length === filter.choices.length;
+ var checkbox = this.controls.filters.checkboxes
+ .filter(function(di) {
+ return di.value_col === d.value_col;
+ })
+ .attr(
+ 'title',
+ checked
+ ? 'Deselect all ' + d.label + ' options'
+ : 'Select all ' + d.label + ' options'
+ )
+ .property('checked', checked);
+ }
+ }
+
+ function updateFilterEventListeners() {
+ var context = this;
+
+ this.controls.filters.selects.on('change', function(d) {
+ var select = d3.select(this);
+ var selectedOptions = select.selectAll('option:checked').data();
+ updateSelectAll.call(context, d, selectedOptions);
+ context.draw();
+ });
+ }
+
+ function sortQueryRecencyOptions() {
+ this.controls.filters.selects
+ .filter(function(d) {
+ return d.value_col === 'queryrecency';
+ })
+ .selectAll('option')
+ .sort(function(a, b) {
+ var anum = parseFloat(a);
+ var bnum = parseFloat(b);
+ var diff = anum - bnum;
+ return diff ? diff : a < b ? -1 : a > b ? 1 : 0;
+ });
+ }
+
function setYAxisDomainLength() {
var context = this;
@@ -872,21 +1327,21 @@
function addResetButton() {
var _this = this;
- this.controls.wrap
+ this.resetButton = d3
+ .select(this.div)
.insert('button', ':first-child')
- .attr('id', 'reset-chart')
- .style({
- margin: '5px',
- padding: '5px',
- float: 'right'
- })
+ .classed('qo-button qo-button--reset-chart', true)
.text('Reset chart')
.on('click', function() {
- var element = clone(_this.div),
- settings = clone(_this.initialSettings),
- data = clone(_this.raw_data);
+ var element = _this.element;
+ var settings = clone(_this.initialSettings);
+ var data = clone(_this.raw_data);
_this.listing.destroy();
_this.destroy();
+ d3
+ .select(_this.element)
+ .selectAll('*')
+ .remove();
queryOverview(element, settings).init(data);
});
}
@@ -913,25 +1368,57 @@
//Reset listing.
context.listing.wrap.selectAll('*').remove();
- context.wrap.select('#listing-instruction').style('display', 'block');
context.listing.init(context.filtered_data);
});
}
- function addListingInstruction() {
- this.wrap
- .append('em')
- .attr('id', 'listing-instruction')
- .text('Click a bar to view its underlying data.');
+ function addFootnotes() {
+ this.footnotes = {
+ barClick: this.wrap
+ .append('div')
+ .classed('qo-footnote qo-footnote--bar-click', true)
+ .text('Click one or more bars to view the underlying data in the listing below.'),
+ deselectBars: this.wrap
+ .append('div')
+ .classed('qo-footnote qo-footnote--deselect-bars', true)
+ .text('Click in the white area to deselect all bars.')
+ };
}
function onLayout() {
+ //Class controls for unique selection.
+ classControls.call(this);
+
+ //Group controls logically.
+ groupControls.call(this);
+
+ //Add tooltips to controls.
+ addControlTooltips.call(this);
+
//Display group label rather than group column name in Group by control.
updateGroupByOptions.call(this);
+ //Highlight y-axis label when user hovers over Status Group control.
+ addGroupByHighlight.call(this);
+
+ //Check radio button of initial status group.
+ checkInitialStatusGroup.call(this);
+
+ //Highlight legend when user hovers over Status Group control.
+ addStatusGroupHighlight.call(this);
+
//Customize dropdowns with multiple options.
customizeMultiSelects.call(this);
+ //Add select all checkbox to filters.
+ addSelectAll.call(this);
+
+ //Update filter event listeners to toggle select all checkbox on change.
+ updateFilterEventListeners.call(this);
+
+ //Sort query recency categories numerically if possible.
+ sortQueryRecencyOptions.call(this);
+
//Handle y-domain length control
setYAxisDomainLength.call(this);
@@ -941,28 +1428,28 @@
//Clear listing when controls change.
clearListingOnChange.call(this);
- //Add listing instruction.
- addListingInstruction.call(this);
+ //Add chart footnotes.
+ addFootnotes.call(this);
}
function updateStratification() {
- var _this = this;
-
- var stratification = this.config.status_groups.find(function(status_group) {
- return status_group.value_col === _this.config.color_by;
+ var statusGroup = this.controls.wrap
+ .selectAll('.qo-radio--status-group')
+ .selectAll('.radio')
+ .filter(function() {
+ var label = d3.select(this);
+ var radio = label.select('.changer');
+ return radio.property('checked');
+ })
+ .text();
+ this.config.status_group = this.config.status_groups.find(function(status_group) {
+ return status_group.label === statusGroup;
});
- this.config.color_dom =
- stratification.order ||
- d3$1
- .set(
- this.raw_data.map(function(d) {
- return d[_this.config.color_by];
- })
- )
- .values()
- .sort();
- this.config.colors = stratification.colors;
- this.config.legend.label = stratification.label;
+ this.config.marks[0].split = this.config.status_group.value_col;
+ this.config.color_by = this.config.status_group.value_col;
+ this.config.color_dom = this.config.status_group.order;
+ this.config.colors = this.config.status_group.colors;
+ this.config.legend.label = this.config.status_group.label;
this.config.legend.order = this.config.color_dom.slice();
this.config.marks[0].tooltip = '[' + this.config.color_by + '] - $x queries';
}
@@ -990,9 +1477,9 @@
function updateRangeBand() {
if (this.config.marks[0].arrange === 'stacked') {
- this.config.range_band = 15;
+ this.config.range_band = this.initialSettings.range_band;
} else {
- this.config.range_band = 15 * this.config.color_dom.length;
+ this.config.range_band = this.initialSettings.range_band * this.config.color_dom.length;
}
}
@@ -1006,7 +1493,7 @@
function setLeftMargin() {
var fontSize = parseInt(this.wrap.style('font-size'));
- this.config.margin.left =
+ this.config.margin.left = Math.max(
Math.max(
7,
d3.max(this.y_dom, function(d) {
@@ -1015,8 +1502,14 @@
) *
fontSize *
0.5 +
- fontSize * 1.5 * 1.5 +
- 6;
+ fontSize * 1.5 * 1.5 +
+ 6,
+ 100
+ );
+ }
+
+ function setXDomain() {
+ if (this.filtered_data.length === 0) this.x_dom = [0, 0];
}
function setYDomain() {
@@ -1066,9 +1559,10 @@
function setChartHeight() {
//Match chart height to number of bars currently displayed.
- this.raw_height =
- (+this.config.range_band + this.config.range_band * this.config.padding) *
- this.y_dom.length;
+ this.raw_height = this.filtered_data.length
+ ? (+this.config.range_band + this.config.range_band * this.config.padding) *
+ this.y_dom.length
+ : 100;
}
function updateXAxisLabel() {
@@ -1089,28 +1583,34 @@
function onDraw() {
setLeftMargin.call(this);
+ setXDomain.call(this);
setYDomain.call(this);
setChartHeight.call(this);
updateXAxisLabel.call(this);
}
+ //TODO: modularize/refactor
function legendFilter() {
var _this = this;
var context = this;
+ //Alter layout of legend.
+ var legend = this.legend;
+ legend.style('margin-left', this.margin.left + 'px');
+ var legendTitle = legend.select('.legend-title');
+ legendTitle.attr(
+ 'title',
+ 'Add and remove queries by clicking the legend items to the left.'
+ );
+ legend.node().appendChild(legendTitle.node());
+
//Filter data by clicking on legend.
var statusFilter = this.filters.find(function(filter) {
return filter.col === _this.config.color_by;
});
var legendItems = this.wrap
.selectAll('.legend-item')
- .style({
- cursor: 'pointer',
- 'border-radius': '4px',
- padding: '5px',
- 'padding-left': '8px'
- })
.classed('selected', function(d) {
return statusFilter.val === 'All' || statusFilter.val.indexOf(d.label) > -1;
})
@@ -1119,17 +1619,15 @@
? 'lightgray'
: 'white';
});
- var statusOptions = this.controls.wrap
- .selectAll('.control-group')
- .filter(function(d) {
- return d.value_col === context.config.marks[0].split;
- })
- .selectAll('.changer option'); // status filter options
+ var statusControlGroup = this.controls.wrap.selectAll('.control-group').filter(function(d) {
+ return d.value_col === context.config.marks[0].split;
+ });
+ var statusOptions = statusControlGroup.selectAll('.changer option'); // status filter options
legendItems.selectAll('.legend-mark-text').remove(); // don't need 'em
+ legendItems.selectAll('.legend-color-block').attr('width', '8px');
legendItems.on('click', function(d) {
- var legendItem = d3.select(this),
- // clicked legend item
- selected = !legendItem.classed('selected'); // selected boolean
+ var legendItem = d3.select(this); // clicked legend item
+ var selected = !legendItem.classed('selected'); // selected boolean
legendItem.classed('selected', selected); // toggle selected class
var selectedLegendItems = legendItems
.filter(function() {
@@ -1148,6 +1646,7 @@
return selectedLegendItems.indexOf(d) > -1;
})
.property('selected', true); // set selected property of status options corresponding to selected statuses to true
+ updateSelectAll.call(context, statusControlGroup.datum(), selectedLegendItems);
var filtered_data = context.raw_data.filter(function(d) {
var filtered = selectedLegendItems.indexOf(d[context.config.marks[0].split]) === -1;
@@ -1182,9 +1681,8 @@
});
context.draw();
- //Remove listing and display listing instruction.
+ //Remove listing and display bar click footnote.
context.listing.wrap.selectAll('*').remove();
- context.wrap.select('#listing-instruction').style('display', 'block');
context.listing.init(filtered_data);
});
}
@@ -1237,39 +1735,92 @@
.style('fill', 'blue')
.style('text-decoration', 'underline');
yLabels.style('cursor', 'pointer').on('click', function(yLabel) {
- _this.controls.wrap
- .selectAll('.control-group')
- .filter(function(d) {
- return d.label === 'Group by';
+ //Update Group by control.
+ var groupByControl = _this.controls.otherControls.controlGroups.filter(function(d) {
+ return d.label === 'Group by';
+ });
+ groupByControl
+ .select('.wc-control-label')
+ .style({
+ 'font-weight': 'bold',
+ 'text-decoration': 'underline',
+ color: 'red'
})
- .selectAll('option')
- .property('selected', function(d) {
- return d === 'Form: Field';
+ .transition()
+ .delay(3000)
+ .style({
+ 'font-weight': 'normal',
+ 'text-decoration': 'none',
+ color: 'black'
});
+ groupByControl.selectAll('option').property('selected', function(d) {
+ return d === 'Form: Field';
+ });
+
+ //Update chart settings.
_this.config.y.column = 'Form: Field';
_this.config.y.label = 'Form: Field';
_this.config.marks[0].per[0] = 'Form: Field';
- _this.controls.wrap
- .selectAll('.control-group')
- .filter(function(d) {
- return d.label === 'Form';
+
+ //Update Form filter.
+ var formFilter = _this.controls.filters.controlGroups.filter(function(d) {
+ return d.label === 'Form';
+ });
+ formFilter
+ .select('.wc-control-label')
+ .style({
+ 'font-weight': 'bold',
+ 'text-decoration': 'underline',
+ color: 'red'
})
- .selectAll('option')
- .property('selected', function(d) {
- return d === yLabel;
+ .transition()
+ .delay(3000)
+ .style({
+ 'font-weight': 'normal',
+ 'text-decoration': 'none',
+ color: 'black'
});
- _this.filters.filter(function(filter) {
- return filter.col === _this.config.form_col;
- })[0].val = yLabel;
+ formFilter.selectAll('option').property('selected', function(d) {
+ return d === yLabel;
+ });
- _this.draw(
- _this.filtered_data.filter(function(d) {
- return d[_this.config.form_col] === yLabel;
- })
+ //Update Form filter object in `chart.filters`.
+ var filter = _this.filters.find(function(filter) {
+ return filter.col === _this.config.form_col;
+ });
+ filter.val = yLabel;
+ updateSelectAll.call(
+ _this,
+ _this.controls.filters.controlGroups
+ .filter(function(d) {
+ return d.value_col === _this.config.form_col;
+ })
+ .datum(),
+ [yLabel]
);
+
+ //Redraw chart and listing.
+
+ //Update Group by control.
+ _this.draw();
_this.listing.wrap.selectAll('*').remove();
- _this.wrap.select('listing-instruction').style('display', 'block');
_this.listing.init(_this.filtered_data);
+
+ //Highlight y-axis label.
+ _this.svg
+ .select('.y.axis .axis-title')
+ .style({
+ 'font-weight': 'bold',
+ 'text-decoration': 'underline',
+ fill: 'red'
+ })
+ .transition()
+ .delay(3000)
+ .style({
+ 'font-weight': 'normal',
+ 'text-decoration': 'none',
+ fill: 'black'
+ });
});
}
}
@@ -1375,85 +1926,148 @@
}
}
- // from https://gist.github.com/samgiles/762ee337dff48623e729
+ function mouseoverStyle(bar, selected) {
+ if (!selected)
+ bar.style({
+ 'stroke-width': '5px',
+ fill: 'black'
+ });
+ }
- Array.prototype.flatMap = function(lambda) {
- return Array.prototype.concat.apply([], this.map(lambda));
- };
+ function mouseoverAttrib(bar, selected) {
+ if (!selected)
+ bar.attr({
+ width: function width(d) {
+ return this.getBBox().width - 2.5;
+ },
+ x: function x(d) {
+ return this.getBBox().x + 2.5;
+ }
+ });
+ }
+
+ function mouseoutStyle(bar, selected) {
+ var _this = this;
+
+ var clear = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
+
+ if (!(selected || clear) || (selected && clear))
+ bar.style({
+ 'stroke-width': '1px',
+ fill: function fill(d) {
+ return _this.colorScale(d.key);
+ }
+ });
+ }
+
+ function mouseoutAttrib(bar, selected) {
+ var clear = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
+
+ if (!(selected || clear) || (selected && clear))
+ bar.attr({
+ width: function width(d) {
+ return this.getBBox().width + 2.5;
+ },
+ x: function x(d) {
+ return this.getBBox().x - 2.5;
+ }
+ });
+ }
+
+ function initListing() {
+ //Clear listing container.
+ this.listing.wrap.selectAll('*').remove();
+
+ //Capture data from selected bars.
+ var selectedData = d3
+ .selectAll('rect.selected')
+ .data()
+ .flatMap(function(d) {
+ return d.values.raw;
+ });
+
+ //Feed data from selected bars into listing.
+ if (selectedData.length > 0) this.listing.init(selectedData);
+ else this.listing.init(this.filtered_data);
+ }
function addBarClick() {
var context = this;
- var barGroups = this.svg.selectAll('.bar-group');
- var bars = this.svg.selectAll('.bar');
// will subtract and add to bar to offset increase in stroke-width and prevent bars
// from overlapping as much when neighbors are both selected.
- var mouseoverAttrib = {
- width: function width(d) {
- return this.getBBox().width - 2.5;
- },
- x: function x(d) {
- return this.getBBox().x + 2.5;
- }
- };
- var mouseoverStyle = {
- 'stroke-width': '5px',
- fill: 'black'
- };
- var mouseoutAttrib = {
- width: function width(d) {
- return this.getBBox().width + 2.5;
- },
- x: function x(d) {
- return this.getBBox().x - 2.5;
- }
- };
- var mouseoutStyle = {
- 'stroke-width': '1px',
- fill: function fill(d) {
- return context.colorScale(d.key);
- }
- };
- bars
+ this.bars = this.svg
+ .selectAll('.bar')
.style('cursor', 'pointer')
.on('mouseover', function() {
- if (!d3.select(this).classed('selected')) d3.select(this).style(mouseoverStyle);
- if (!d3.select(this).classed('selected')) d3.select(this).attr(mouseoverAttrib);
+ var bar = d3.select(this);
+ var selected = bar.classed('selected');
+
+ //Apply highlight attributes and styles to bar.
+ mouseoverStyle.call(context, bar, selected);
+ mouseoverAttrib.call(context, bar, selected);
+
//moveToFront causes an issue preventing onMouseout from firing in Internet Explorer so only call it in other browsers.
if (!/trident/i.test(navigator.userAgent)) d3.select(this).moveToFront();
})
.on('mouseout', function() {
- if (!d3.select(this).classed('selected')) d3.select(this).style(mouseoutStyle);
- if (!d3.select(this).classed('selected')) d3.select(this).attr(mouseoutAttrib);
- bars
- .filter(function() {
- return d3.select(this).classed('selected');
- })
- .moveToFront();
+ var bar = d3.select(this);
+ var selected = bar.classed('selected');
+
+ //Apply default attributes and styles to bar.
+ mouseoutStyle.call(context, bar, selected);
+ mouseoutAttrib.call(context, bar, selected);
+
+ //moveToFront causes an issue preventing onMouseout from firing in Internet Explorer so only call it in other browsers.
+ if (!/trident/i.test(navigator.userAgent))
+ context.bars
+ .filter(function() {
+ return d3.select(this).classed('selected');
+ })
+ .moveToFront();
})
.on('click', function(d) {
- // this doesn't need a style because mouseout isn't applied when the bar is selected
- d3
- .select(this)
- .classed('selected', d3.select(this).classed('selected') ? false : true);
- context.listing.wrap.selectAll('*').remove();
- // feed listing data for all selected bars
- context.listing.init(
- d3
- .selectAll('rect.selected')
- .data()
- .flatMap(function(d) {
- return d.values.raw;
- })
- );
- context.wrap.select('#listing-instruction').style('display', 'none'); // remove bar instructions
- // display filtered data if no bars are selected
- if (d3.selectAll('rect.selected')[0].length === 0) {
- context.listing.wrap.selectAll('*').remove();
- context.wrap.select('#listing-instruction').style('display', 'block');
- context.listing.init(context.filtered_data);
- }
+ var bar = d3.select(this);
+ var selected = bar.classed('selected');
+
+ //Update selected class of clicked bar.
+ bar.classed('selected', !selected);
+
+ //Re-initialize listing.
+ initListing.call(context);
+ });
+ }
+
+ function addBarDeselection() {
+ var _this = this;
+
+ var context = this;
+
+ this.overlay.on('click', function() {
+ _this.bars.each(function(d) {
+ var bar = d3.select(this);
+ var selected = bar.classed('selected');
+ mouseoutStyle.call(context, bar, selected, true);
+ mouseoutAttrib.call(context, bar, selected, true);
+ bar.classed('selected', false);
});
+ initListing.call(_this);
+ });
+ }
+
+ function addNoDataIndicator() {
+ this.svg.select('.qo-no-data').remove();
+
+ if (this.filtered_data.length === 0)
+ this.svg
+ .append('text')
+ .classed('qo-no-data', true)
+ .attr({
+ x: this.plot_width / 2,
+ y: this.plot_height / 2,
+ 'text-anchor': 'middle'
+ })
+ .text('No queries selected. Verify that no filter selections are in conflict.');
}
function onResize() {
@@ -1477,22 +2091,34 @@
//Add bar click-ability.
addBarClick.call(this);
+
+ //Add bar deselection.
+ addBarDeselection.call(this);
+
+ //Add informational text to the chart canvas when filters are in conflict.
+ addNoDataIndicator.call(this);
}
+ function onDestroy() {}
+
+ var chartCallbacks = {
+ onInit: onInit,
+ onLayout: onLayout,
+ onPreprocess: onPreprocess,
+ onDataTransform: onDataTransform,
+ onDraw: onDraw,
+ onResize: onResize,
+ onDestroy: onDestroy
+ };
+
function onInit$1() {}
- function resetListing() {
+ function addResetButton$1() {
var _this = this;
- this.wrap
+ this.resetButton = this.wrap
.insert('button', ':first-child')
- .attr('id', 'clear-listing')
- .style({
- margin: '5px',
- padding: '5px',
- float: 'left',
- display: 'block'
- })
+ .classed('qo-button qo-button--reset-listing', true)
.text('Reset listing')
.on('click', function() {
_this.wrap.selectAll('*').remove();
@@ -1515,7 +2141,6 @@
}
});
_this.chart.listing.init(_this.chart.filtered_data);
- _this.chart.wrap.select('#listing-instruction').style('display', 'block');
});
}
@@ -1525,10 +2150,10 @@
var table = this.table.node();
this.tableContainer = this.wrap
.append('div')
- .classed('query-table-container', true)
+ .classed('qo-table-container', true)
.node();
- this.wrap.select('table').classed('query-table', true); // I want to ensure that no other webcharts tables get flipped upside down
+ this.wrap.select('table').classed('qo-table', true); // I want to ensure that no other webcharts tables get flipped upside down
table.parentNode.insertBefore(this.tableContainer, table);
this.tableContainer.appendChild(table);
@@ -1536,12 +2161,14 @@
}
function onLayout$1() {
- resetListing.call(this);
+ addResetButton$1.call(this);
addTableContainer.call(this);
this.wrap.select('.sortable-container').classed('hidden', false);
this.table.style('width', '100%').style('display', 'table');
}
+ function onPreprocess$1() {}
+
function manualSort() {
var _this = this;
@@ -1590,7 +2217,7 @@
this.draw(this.data.manually_sorted);
}
- function onClick() {
+ function updateColumnSorting() {
var context = this;
this.thead_cells.on('click', function(d) {
@@ -1667,6 +2294,25 @@
});
}
+ function truncateCellText() {
+ var _this = this;
+
+ if (this.data.raw.length)
+ this.tbody
+ .selectAll('td')
+ .attr('title', function(d) {
+ return d.text;
+ })
+ .filter(function(d) {
+ return d.text.length > _this.chart.initialSettings.truncation_cutoff;
+ })
+ .text(function(d) {
+ return (
+ d.text.substring(0, _this.chart.initialSettings.truncation_cutoff) + '...'
+ );
+ });
+ }
+
function moveScrollBarLeft() {
var _this = this;
@@ -1680,7 +2326,11 @@
}
function onDraw$1() {
- onClick.call(this);
+ //Update default Webcharts column sorting.
+ updateColumnSorting.call(this);
+
+ //Truncate cells with length greater than `settings.truncation_cutoff`.
+ truncateCellText.call(this);
//Move table scrollbar all the way to the left.
moveScrollBarLeft.call(this);
@@ -1688,63 +2338,52 @@
function onDestroy$1() {}
- function defineStyles() {
- var styles = [
- '.query-table-container {' +
- ' overflow-x: auto;' +
- ' width : 100%;' +
- ' transform: rotate(180deg);' +
- ' -webkit-transform:rotate(180deg); ' +
- '}',
- '.query-table {' +
- ' transform: rotate(180deg);' +
- ' -webkit-transform:rotate(180deg); ' +
- '}'
- ];
-
- //Attach styles to DOM.
- this.style = document.createElement('style');
- this.style.type = 'text/css';
- this.style.innerHTML = styles.join('\n');
- document.getElementsByTagName('head')[0].appendChild(this.style);
- }
+ var listingCallbacks = {
+ onInit: onInit$1,
+ onLayout: onLayout$1,
+ onPreprocess: onPreprocess$1,
+ onDraw: onDraw$1,
+ onDestroy: onDestroy$1
+ };
function queryOverview$1(element, settings) {
+ //Settings
var mergedSettings = Object.assign({}, configuration.settings, settings);
var syncedSettings = configuration.syncSettings(mergedSettings);
var syncedControlInputs = configuration.syncControlInputs(
configuration.controlInputs,
syncedSettings
);
- var controls = webcharts.createControls(element, {
+
+ //Layout and styles
+ var containers = layout(element);
+ styles();
+
+ //Controls
+ var controls = webcharts.createControls(containers.controls.node(), {
location: 'top',
inputs: syncedControlInputs
});
- var chart = webcharts.createChart(element, syncedSettings, controls);
- var listing = webcharts.createTable(element, {
+ controls.element = element;
+
+ //Chart
+ var chart = webcharts.createChart(containers.chart.node(), syncedSettings, controls);
+ chart.element = element;
+ chart.initialSettings = clone(mergedSettings);
+ for (var callback in chartCallbacks) {
+ chart.on(callback.substring(2).toLowerCase(), chartCallbacks[callback]);
+ } //Listing
+ var listing = webcharts.createTable(containers.listing.node(), {
sortable: false,
exportable: syncedSettings.exportable
});
-
- chart.initialSettings = clone(mergedSettings);
- chart.on('init', onInit);
- chart.on('layout', onLayout);
- chart.on('preprocess', onPreprocess);
- chart.on('datatransform', onDataTransform);
- chart.on('draw', onDraw);
- chart.on('resize', onResize);
-
- listing.on('init', onInit$1);
- listing.on('layout', onLayout$1);
- listing.on('draw', onDraw$1);
- listing.on('destroy', onDestroy$1);
-
+ listing.element = element;
+ for (var _callback in listingCallbacks) {
+ listing.on(_callback.substring(2).toLowerCase(), listingCallbacks[_callback]);
+ } //Intertwine
chart.listing = listing;
listing.chart = chart;
- //add Table stylesheet
- defineStyles.call(listing);
-
return chart;
}
diff --git a/build/test-page/index.js b/build/test-page/index.js
deleted file mode 100644
index d7d9e76..0000000
--- a/build/test-page/index.js
+++ /dev/null
@@ -1,23 +0,0 @@
-//Load local build if in local environment.
-if (window.origin !== 'https://rhoinc.github.io') {
- var head = document.getElementsByTagName('head')[0];
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.src = '../queryOverview.js';
- head.appendChild(script);
-}
-
-d3.csv(
- 'https://rawgit.com/RhoInc/viz-library/master/data/queries/queries.csv',
- function(error,data) {
- if (error)
- console.log(error);
-
- var settings = {};
- var instance = queryOverview(
- '#container',
- settings
- );
- instance.init(data);
- }
-);
diff --git a/package-lock.json b/package-lock.json
index da16085..81d30ba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,17 +1,95 @@
{
"name": "query-overview",
- "version": "1.2.3",
+ "version": "2.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+ "@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "10.12.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.9.tgz",
+ "integrity": "sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA==",
+ "dev": true
+ },
"adler-32": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz",
"integrity": "sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=",
"optional": true,
"requires": {
- "exit-on-epipe": "1.0.1",
- "printj": "1.1.2"
+ "exit-on-epipe": "~1.0.1",
+ "printj": "~1.1.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ }
+ },
+ "babel-core": {
+ "version": "6.26.3",
+ "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
+ "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-generator": "^6.26.0",
+ "babel-helpers": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-register": "^6.26.0",
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "convert-source-map": "^1.5.1",
+ "debug": "^2.6.9",
+ "json5": "^0.5.1",
+ "lodash": "^4.17.4",
+ "minimatch": "^3.0.4",
+ "path-is-absolute": "^1.0.1",
+ "private": "^0.1.8",
+ "slash": "^1.0.0",
+ "source-map": "^0.5.7"
+ }
+ },
+ "babel-generator": {
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
+ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+ "dev": true,
+ "requires": {
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "detect-indent": "^4.0.0",
+ "jsesc": "^1.3.0",
+ "lodash": "^4.17.4",
+ "source-map": "^0.5.7",
+ "trim-right": "^1.0.1"
}
},
"babel-helper-builder-binary-assignment-operator-visitor": {
@@ -20,9 +98,9 @@
"integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=",
"dev": true,
"requires": {
- "babel-helper-explode-assignable-expression": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-explode-assignable-expression": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
},
"dependencies": {
"babel-runtime": {
@@ -31,8 +109,8 @@
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
- "core-js": "2.5.4",
- "regenerator-runtime": "0.11.1"
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
}
},
"babel-types": {
@@ -41,10 +119,10 @@
"integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "esutils": "2.0.2",
- "lodash": "4.17.5",
- "to-fast-properties": "1.0.3"
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
}
},
"core-js": {
@@ -85,9 +163,9 @@
"integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
},
"dependencies": {
"ansi-regex": {
@@ -108,9 +186,9 @@
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
"dev": true,
"requires": {
- "chalk": "1.1.3",
- "esutils": "2.0.2",
- "js-tokens": "3.0.2"
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
}
},
"babel-messages": {
@@ -119,7 +197,7 @@
"integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-runtime": {
@@ -128,8 +206,8 @@
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
- "core-js": "2.5.4",
- "regenerator-runtime": "0.11.1"
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
}
},
"babel-traverse": {
@@ -138,15 +216,15 @@
"integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
"dev": true,
"requires": {
- "babel-code-frame": "6.26.0",
- "babel-messages": "6.23.0",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0",
- "babylon": "6.18.0",
- "debug": "2.6.9",
- "globals": "9.18.0",
- "invariant": "2.2.4",
- "lodash": "4.17.5"
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
}
},
"babel-types": {
@@ -155,10 +233,10 @@
"integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "esutils": "2.0.2",
- "lodash": "4.17.5",
- "to-fast-properties": "1.0.3"
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
}
},
"babylon": {
@@ -173,11 +251,11 @@
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
}
},
"core-js": {
@@ -219,7 +297,7 @@
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"dev": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
}
},
"invariant": {
@@ -228,7 +306,7 @@
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dev": true,
"requires": {
- "loose-envify": "1.3.1"
+ "loose-envify": "^1.0.0"
}
},
"js-tokens": {
@@ -249,7 +327,7 @@
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
"dev": true,
"requires": {
- "js-tokens": "3.0.2"
+ "js-tokens": "^3.0.0"
}
},
"ms": {
@@ -270,7 +348,7 @@
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
}
},
"supports-color": {
@@ -293,11 +371,11 @@
"integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=",
"dev": true,
"requires": {
- "babel-helper-function-name": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
},
"dependencies": {
"ansi-regex": {
@@ -318,9 +396,9 @@
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
"dev": true,
"requires": {
- "chalk": "1.1.3",
- "esutils": "2.0.2",
- "js-tokens": "3.0.2"
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
}
},
"babel-helper-function-name": {
@@ -329,11 +407,11 @@
"integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
"dev": true,
"requires": {
- "babel-helper-get-function-arity": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
}
},
"babel-helper-get-function-arity": {
@@ -342,8 +420,8 @@
"integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-messages": {
@@ -352,7 +430,7 @@
"integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-runtime": {
@@ -361,8 +439,8 @@
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
- "core-js": "2.5.4",
- "regenerator-runtime": "0.11.1"
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
}
},
"babel-template": {
@@ -371,11 +449,11 @@
"integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0",
- "babylon": "6.18.0",
- "lodash": "4.17.5"
+ "babel-runtime": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "lodash": "^4.17.4"
}
},
"babel-traverse": {
@@ -384,15 +462,15 @@
"integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
"dev": true,
"requires": {
- "babel-code-frame": "6.26.0",
- "babel-messages": "6.23.0",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0",
- "babylon": "6.18.0",
- "debug": "2.6.9",
- "globals": "9.18.0",
- "invariant": "2.2.4",
- "lodash": "4.17.5"
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
}
},
"babel-types": {
@@ -401,10 +479,10 @@
"integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "esutils": "2.0.2",
- "lodash": "4.17.5",
- "to-fast-properties": "1.0.3"
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
}
},
"babylon": {
@@ -419,11 +497,11 @@
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
}
},
"core-js": {
@@ -465,7 +543,7 @@
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"dev": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
}
},
"invariant": {
@@ -474,7 +552,7 @@
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dev": true,
"requires": {
- "loose-envify": "1.3.1"
+ "loose-envify": "^1.0.0"
}
},
"js-tokens": {
@@ -495,7 +573,7 @@
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
"dev": true,
"requires": {
- "js-tokens": "3.0.2"
+ "js-tokens": "^3.0.0"
}
},
"ms": {
@@ -516,7 +594,7 @@
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
}
},
"supports-color": {
@@ -533,13 +611,32 @@
}
}
},
+ "babel-helpers": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
+ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
"babel-plugin-external-helpers": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-external-helpers/-/babel-plugin-external-helpers-6.22.0.tgz",
"integrity": "sha1-IoX0iwK9Xe3oUXXK+MYuhq3M76E=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
},
"dependencies": {
"babel-runtime": {
@@ -548,8 +645,8 @@
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
- "core-js": "2.5.4",
- "regenerator-runtime": "0.11.1"
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
}
},
"core-js": {
@@ -590,9 +687,9 @@
"integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=",
"dev": true,
"requires": {
- "babel-helper-remap-async-to-generator": "6.24.1",
- "babel-plugin-syntax-async-functions": "6.13.0",
- "babel-runtime": "6.26.0"
+ "babel-helper-remap-async-to-generator": "^6.24.1",
+ "babel-plugin-syntax-async-functions": "^6.8.0",
+ "babel-runtime": "^6.22.0"
},
"dependencies": {
"babel-runtime": {
@@ -601,8 +698,8 @@
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
- "core-js": "2.5.4",
- "regenerator-runtime": "0.11.1"
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
}
},
"core-js": {
@@ -625,9 +722,9 @@
"integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=",
"dev": true,
"requires": {
- "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1",
- "babel-plugin-syntax-exponentiation-operator": "6.13.0",
- "babel-runtime": "6.26.0"
+ "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1",
+ "babel-plugin-syntax-exponentiation-operator": "^6.8.0",
+ "babel-runtime": "^6.22.0"
},
"dependencies": {
"babel-runtime": {
@@ -636,8 +733,8 @@
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
- "core-js": "2.5.4",
- "regenerator-runtime": "0.11.1"
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
}
},
"core-js": {
@@ -660,36 +757,36 @@
"integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==",
"dev": true,
"requires": {
- "babel-plugin-check-es2015-constants": "6.22.0",
- "babel-plugin-syntax-trailing-function-commas": "6.22.0",
- "babel-plugin-transform-async-to-generator": "6.24.1",
- "babel-plugin-transform-es2015-arrow-functions": "6.22.0",
- "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0",
- "babel-plugin-transform-es2015-block-scoping": "6.26.0",
- "babel-plugin-transform-es2015-classes": "6.24.1",
- "babel-plugin-transform-es2015-computed-properties": "6.24.1",
- "babel-plugin-transform-es2015-destructuring": "6.23.0",
- "babel-plugin-transform-es2015-duplicate-keys": "6.24.1",
- "babel-plugin-transform-es2015-for-of": "6.23.0",
- "babel-plugin-transform-es2015-function-name": "6.24.1",
- "babel-plugin-transform-es2015-literals": "6.22.0",
- "babel-plugin-transform-es2015-modules-amd": "6.24.1",
- "babel-plugin-transform-es2015-modules-commonjs": "6.26.0",
- "babel-plugin-transform-es2015-modules-systemjs": "6.24.1",
- "babel-plugin-transform-es2015-modules-umd": "6.24.1",
- "babel-plugin-transform-es2015-object-super": "6.24.1",
- "babel-plugin-transform-es2015-parameters": "6.24.1",
- "babel-plugin-transform-es2015-shorthand-properties": "6.24.1",
- "babel-plugin-transform-es2015-spread": "6.22.0",
- "babel-plugin-transform-es2015-sticky-regex": "6.24.1",
- "babel-plugin-transform-es2015-template-literals": "6.22.0",
- "babel-plugin-transform-es2015-typeof-symbol": "6.23.0",
- "babel-plugin-transform-es2015-unicode-regex": "6.24.1",
- "babel-plugin-transform-exponentiation-operator": "6.24.1",
- "babel-plugin-transform-regenerator": "6.26.0",
- "browserslist": "2.11.3",
- "invariant": "2.2.4",
- "semver": "5.5.0"
+ "babel-plugin-check-es2015-constants": "^6.22.0",
+ "babel-plugin-syntax-trailing-function-commas": "^6.22.0",
+ "babel-plugin-transform-async-to-generator": "^6.22.0",
+ "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
+ "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0",
+ "babel-plugin-transform-es2015-block-scoping": "^6.23.0",
+ "babel-plugin-transform-es2015-classes": "^6.23.0",
+ "babel-plugin-transform-es2015-computed-properties": "^6.22.0",
+ "babel-plugin-transform-es2015-destructuring": "^6.23.0",
+ "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0",
+ "babel-plugin-transform-es2015-for-of": "^6.23.0",
+ "babel-plugin-transform-es2015-function-name": "^6.22.0",
+ "babel-plugin-transform-es2015-literals": "^6.22.0",
+ "babel-plugin-transform-es2015-modules-amd": "^6.22.0",
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0",
+ "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0",
+ "babel-plugin-transform-es2015-modules-umd": "^6.23.0",
+ "babel-plugin-transform-es2015-object-super": "^6.22.0",
+ "babel-plugin-transform-es2015-parameters": "^6.23.0",
+ "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0",
+ "babel-plugin-transform-es2015-spread": "^6.22.0",
+ "babel-plugin-transform-es2015-sticky-regex": "^6.22.0",
+ "babel-plugin-transform-es2015-template-literals": "^6.22.0",
+ "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0",
+ "babel-plugin-transform-es2015-unicode-regex": "^6.22.0",
+ "babel-plugin-transform-exponentiation-operator": "^6.22.0",
+ "babel-plugin-transform-regenerator": "^6.22.0",
+ "browserslist": "^2.1.2",
+ "invariant": "^2.2.2",
+ "semver": "^5.3.0"
},
"dependencies": {
"ansi-regex": {
@@ -710,9 +807,9 @@
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
"dev": true,
"requires": {
- "chalk": "1.1.3",
- "esutils": "2.0.2",
- "js-tokens": "3.0.2"
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
}
},
"babel-helper-call-delegate": {
@@ -721,10 +818,10 @@
"integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
"dev": true,
"requires": {
- "babel-helper-hoist-variables": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-hoist-variables": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
}
},
"babel-helper-define-map": {
@@ -733,10 +830,10 @@
"integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
"dev": true,
"requires": {
- "babel-helper-function-name": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0",
- "lodash": "4.17.5"
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
}
},
"babel-helper-function-name": {
@@ -745,11 +842,11 @@
"integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
"dev": true,
"requires": {
- "babel-helper-get-function-arity": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
}
},
"babel-helper-get-function-arity": {
@@ -758,8 +855,8 @@
"integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-helper-hoist-variables": {
@@ -768,8 +865,8 @@
"integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-helper-optimise-call-expression": {
@@ -778,8 +875,8 @@
"integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-helper-regex": {
@@ -788,9 +885,9 @@
"integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0",
- "lodash": "4.17.5"
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
}
},
"babel-helper-replace-supers": {
@@ -799,12 +896,12 @@
"integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
"dev": true,
"requires": {
- "babel-helper-optimise-call-expression": "6.24.1",
- "babel-messages": "6.23.0",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-optimise-call-expression": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
}
},
"babel-messages": {
@@ -813,7 +910,7 @@
"integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-check-es2015-constants": {
@@ -822,7 +919,7 @@
"integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-arrow-functions": {
@@ -831,7 +928,7 @@
"integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-block-scoped-functions": {
@@ -840,7 +937,7 @@
"integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-block-scoping": {
@@ -849,11 +946,11 @@
"integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0",
- "lodash": "4.17.5"
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
}
},
"babel-plugin-transform-es2015-classes": {
@@ -862,15 +959,15 @@
"integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
"dev": true,
"requires": {
- "babel-helper-define-map": "6.26.0",
- "babel-helper-function-name": "6.24.1",
- "babel-helper-optimise-call-expression": "6.24.1",
- "babel-helper-replace-supers": "6.24.1",
- "babel-messages": "6.23.0",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-define-map": "^6.24.1",
+ "babel-helper-function-name": "^6.24.1",
+ "babel-helper-optimise-call-expression": "^6.24.1",
+ "babel-helper-replace-supers": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-computed-properties": {
@@ -879,8 +976,8 @@
"integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-destructuring": {
@@ -889,7 +986,7 @@
"integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-duplicate-keys": {
@@ -898,8 +995,8 @@
"integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-for-of": {
@@ -908,7 +1005,7 @@
"integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-function-name": {
@@ -917,9 +1014,9 @@
"integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
"dev": true,
"requires": {
- "babel-helper-function-name": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-literals": {
@@ -928,7 +1025,7 @@
"integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-modules-amd": {
@@ -937,9 +1034,9 @@
"integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
"dev": true,
"requires": {
- "babel-plugin-transform-es2015-modules-commonjs": "6.26.0",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0"
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-modules-commonjs": {
@@ -948,10 +1045,10 @@
"integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=",
"dev": true,
"requires": {
- "babel-plugin-transform-strict-mode": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-plugin-transform-strict-mode": "^6.24.1",
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-types": "^6.26.0"
}
},
"babel-plugin-transform-es2015-modules-systemjs": {
@@ -960,9 +1057,9 @@
"integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
"dev": true,
"requires": {
- "babel-helper-hoist-variables": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0"
+ "babel-helper-hoist-variables": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-modules-umd": {
@@ -971,9 +1068,9 @@
"integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
"dev": true,
"requires": {
- "babel-plugin-transform-es2015-modules-amd": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0"
+ "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-object-super": {
@@ -982,8 +1079,8 @@
"integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
"dev": true,
"requires": {
- "babel-helper-replace-supers": "6.24.1",
- "babel-runtime": "6.26.0"
+ "babel-helper-replace-supers": "^6.24.1",
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-parameters": {
@@ -992,12 +1089,12 @@
"integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
"dev": true,
"requires": {
- "babel-helper-call-delegate": "6.24.1",
- "babel-helper-get-function-arity": "6.24.1",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-call-delegate": "^6.24.1",
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-shorthand-properties": {
@@ -1006,8 +1103,8 @@
"integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-spread": {
@@ -1016,7 +1113,7 @@
"integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-sticky-regex": {
@@ -1025,9 +1122,9 @@
"integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
"dev": true,
"requires": {
- "babel-helper-regex": "6.26.0",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-helper-regex": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-template-literals": {
@@ -1036,7 +1133,7 @@
"integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-typeof-symbol": {
@@ -1045,7 +1142,7 @@
"integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0"
+ "babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-unicode-regex": {
@@ -1054,9 +1151,9 @@
"integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
"dev": true,
"requires": {
- "babel-helper-regex": "6.26.0",
- "babel-runtime": "6.26.0",
- "regexpu-core": "2.0.0"
+ "babel-helper-regex": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "regexpu-core": "^2.0.0"
}
},
"babel-plugin-transform-regenerator": {
@@ -1065,7 +1162,7 @@
"integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=",
"dev": true,
"requires": {
- "regenerator-transform": "0.10.1"
+ "regenerator-transform": "^0.10.0"
}
},
"babel-plugin-transform-strict-mode": {
@@ -1074,8 +1171,8 @@
"integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0"
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
}
},
"babel-runtime": {
@@ -1084,8 +1181,8 @@
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"dev": true,
"requires": {
- "core-js": "2.5.4",
- "regenerator-runtime": "0.11.1"
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
}
},
"babel-template": {
@@ -1094,11 +1191,11 @@
"integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0",
- "babylon": "6.18.0",
- "lodash": "4.17.5"
+ "babel-runtime": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "lodash": "^4.17.4"
}
},
"babel-traverse": {
@@ -1107,15 +1204,15 @@
"integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
"dev": true,
"requires": {
- "babel-code-frame": "6.26.0",
- "babel-messages": "6.23.0",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0",
- "babylon": "6.18.0",
- "debug": "2.6.9",
- "globals": "9.18.0",
- "invariant": "2.2.4",
- "lodash": "4.17.5"
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
}
},
"babel-types": {
@@ -1124,10 +1221,10 @@
"integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "esutils": "2.0.2",
- "lodash": "4.17.5",
- "to-fast-properties": "1.0.3"
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
}
},
"babylon": {
@@ -1142,11 +1239,11 @@
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
}
},
"core-js": {
@@ -1188,7 +1285,7 @@
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"dev": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
}
},
"invariant": {
@@ -1197,7 +1294,7 @@
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dev": true,
"requires": {
- "loose-envify": "1.3.1"
+ "loose-envify": "^1.0.0"
}
},
"js-tokens": {
@@ -1224,7 +1321,7 @@
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
"dev": true,
"requires": {
- "js-tokens": "3.0.2"
+ "js-tokens": "^3.0.0"
}
},
"ms": {
@@ -1257,9 +1354,9 @@
"integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
"dev": true,
"requires": {
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0",
- "private": "0.1.8"
+ "babel-runtime": "^6.18.0",
+ "babel-types": "^6.19.0",
+ "private": "^0.1.6"
}
},
"regexpu-core": {
@@ -1268,9 +1365,9 @@
"integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=",
"dev": true,
"requires": {
- "regenerate": "1.3.3",
- "regjsgen": "0.2.0",
- "regjsparser": "0.1.5"
+ "regenerate": "^1.2.1",
+ "regjsgen": "^0.2.0",
+ "regjsparser": "^0.1.4"
}
},
"regjsgen": {
@@ -1285,7 +1382,7 @@
"integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
"dev": true,
"requires": {
- "jsesc": "0.5.0"
+ "jsesc": "~0.5.0"
}
},
"strip-ansi": {
@@ -1294,7 +1391,7 @@
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
- "ansi-regex": "2.1.1"
+ "ansi-regex": "^2.0.0"
}
},
"supports-color": {
@@ -1317,576 +1414,263 @@
"integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
"dev": true,
"requires": {
- "babel-core": "6.26.0",
- "babel-runtime": "6.26.0",
- "core-js": "2.5.4",
- "home-or-tmp": "2.0.0",
- "lodash": "4.17.5",
- "mkdirp": "0.5.1",
- "source-map-support": "0.4.18"
+ "babel-core": "^6.26.0",
+ "babel-runtime": "^6.26.0",
+ "core-js": "^2.5.0",
+ "home-or-tmp": "^2.0.0",
+ "lodash": "^4.17.4",
+ "mkdirp": "^0.5.1",
+ "source-map-support": "^0.4.15"
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "dev": true,
+ "requires": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ }
+ },
+ "babel-template": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
+ }
+ },
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "browserslist": {
+ "version": "2.11.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz",
+ "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30000792",
+ "electron-to-chromium": "^1.3.30"
+ }
+ },
+ "buffer-from": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
+ "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
+ "optional": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30000821",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000821.tgz",
+ "integrity": "sha512-qyYay02wr/5k7PO86W+LKFaEUZfWIvT65PaXuPP16jkSpgZGIsSstHKiYAPVLjTj98j2WnWwZg8CjXPx7UIPYg==",
+ "dev": true
+ },
+ "cfb": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.0.5.tgz",
+ "integrity": "sha512-z1BN+JkopTE4vYu0sx25da2ZFurcN8gUKcBpT2ThCDlNFtWBozId8AHMs4OS7jTPLCJYK30Ud7QgcioyGkkkbg==",
+ "optional": true,
+ "requires": {
+ "commander": "^2.14.1",
+ "printj": "~1.1.2"
},
"dependencies": {
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
- },
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
- "dev": true
- },
- "babel-code-frame": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
- "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
- "dev": true,
- "requires": {
- "chalk": "1.1.3",
- "esutils": "2.0.2",
- "js-tokens": "3.0.2"
- }
- },
- "babel-core": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz",
- "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=",
- "dev": true,
- "requires": {
- "babel-code-frame": "6.26.0",
- "babel-generator": "6.26.1",
- "babel-helpers": "6.24.1",
- "babel-messages": "6.23.0",
- "babel-register": "6.26.0",
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0",
- "babylon": "6.18.0",
- "convert-source-map": "1.5.1",
- "debug": "2.6.9",
- "json5": "0.5.1",
- "lodash": "4.17.5",
- "minimatch": "3.0.4",
- "path-is-absolute": "1.0.1",
- "private": "0.1.8",
- "slash": "1.0.0",
- "source-map": "0.5.7"
- }
- },
- "babel-generator": {
- "version": "6.26.1",
- "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
- "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
- "dev": true,
- "requires": {
- "babel-messages": "6.23.0",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0",
- "detect-indent": "4.0.0",
- "jsesc": "1.3.0",
- "lodash": "4.17.5",
- "source-map": "0.5.7",
- "trim-right": "1.0.1"
- }
- },
- "babel-helpers": {
- "version": "6.24.1",
- "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
- "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
- "dev": true,
- "requires": {
- "babel-runtime": "6.26.0",
- "babel-template": "6.26.0"
- }
- },
- "babel-messages": {
- "version": "6.23.0",
- "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
- "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
- "dev": true,
- "requires": {
- "babel-runtime": "6.26.0"
- }
- },
- "babel-runtime": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
- "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
- "dev": true,
- "requires": {
- "core-js": "2.5.4",
- "regenerator-runtime": "0.11.1"
- }
- },
- "babel-template": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
- "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
- "dev": true,
- "requires": {
- "babel-runtime": "6.26.0",
- "babel-traverse": "6.26.0",
- "babel-types": "6.26.0",
- "babylon": "6.18.0",
- "lodash": "4.17.5"
- }
- },
- "babel-traverse": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
- "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
- "dev": true,
- "requires": {
- "babel-code-frame": "6.26.0",
- "babel-messages": "6.23.0",
- "babel-runtime": "6.26.0",
- "babel-types": "6.26.0",
- "babylon": "6.18.0",
- "debug": "2.6.9",
- "globals": "9.18.0",
- "invariant": "2.2.4",
- "lodash": "4.17.5"
- }
- },
- "babel-types": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
- "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
- "dev": true,
- "requires": {
- "babel-runtime": "6.26.0",
- "esutils": "2.0.2",
- "lodash": "4.17.5",
- "to-fast-properties": "1.0.3"
- }
+ "commander": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+ "optional": true
+ }
+ }
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "codepage": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.3.8.tgz",
+ "integrity": "sha1-Ty5dfAl13ij4hJgFjcta/KtqX3E=",
+ "optional": true,
+ "requires": {
+ "commander": "^2.15.1",
+ "concat-stream": "^1.6.2",
+ "voc": "^1.1.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+ "optional": true
+ }
+ }
+ },
+ "colors": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
+ "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
+ "optional": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "optional": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ },
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "optional": true
},
- "babylon": {
- "version": "6.18.0",
- "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
- "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
- "dev": true
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
- "balanced-match": {
+ "isarray": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "dev": true
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "optional": true
},
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "requires": {
- "balanced-match": "1.0.0",
- "concat-map": "0.0.1"
- }
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "optional": true
},
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
+ "readable-stream": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
+ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
+ "optional": true,
"requires": {
- "ansi-styles": "2.2.1",
- "escape-string-regexp": "1.0.5",
- "has-ansi": "2.0.0",
- "strip-ansi": "3.0.1",
- "supports-color": "2.0.0"
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.0.3",
+ "util-deprecate": "~1.0.1"
}
},
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
- },
- "convert-source-map": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
- "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=",
- "dev": true
- },
- "core-js": {
- "version": "2.5.4",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.4.tgz",
- "integrity": "sha1-8si/GB8qgLkvNgEhQpzmOi8K6uA=",
- "dev": true
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "optional": true,
"requires": {
- "ms": "2.0.0"
+ "safe-buffer": "~5.1.0"
}
},
- "detect-indent": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
- "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
- "dev": true,
- "requires": {
- "repeating": "2.0.1"
- }
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "esutils": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
- "dev": true
- },
- "globals": {
- "version": "9.18.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
- "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
- "dev": true
- },
- "has-ansi": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
- "dev": true,
- "requires": {
- "ansi-regex": "2.1.1"
- }
- },
- "home-or-tmp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
- "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
- "dev": true,
- "requires": {
- "os-homedir": "1.0.2",
- "os-tmpdir": "1.0.2"
- }
- },
- "invariant": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
- "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
- "dev": true,
- "requires": {
- "loose-envify": "1.3.1"
- }
- },
- "is-finite": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
- "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
- "dev": true,
- "requires": {
- "number-is-nan": "1.0.1"
- }
- },
- "js-tokens": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
- "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
- "dev": true
- },
- "jsesc": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
- "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
- "dev": true
- },
- "json5": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
- "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
- "dev": true
- },
- "lodash": {
- "version": "4.17.5",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
- "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==",
- "dev": true
- },
- "loose-envify": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
- "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
- "dev": true,
- "requires": {
- "js-tokens": "3.0.2"
- }
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "dev": true,
- "requires": {
- "brace-expansion": "1.1.11"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- },
- "number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
- "dev": true
- },
- "os-homedir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
- "dev": true
- },
- "os-tmpdir": {
+ "util-deprecate": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
- "dev": true
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
- },
- "private": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
- "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
- "dev": true
- },
- "regenerator-runtime": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
- "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
- "dev": true
- },
- "repeating": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
- "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
- "dev": true,
- "requires": {
- "is-finite": "1.0.2"
- }
- },
- "slash": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
- "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
- "dev": true
- },
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
- },
- "source-map-support": {
- "version": "0.4.18",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
- "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
- "dev": true,
- "requires": {
- "source-map": "0.5.7"
- }
- },
- "strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
- "requires": {
- "ansi-regex": "2.1.1"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
- },
- "to-fast-properties": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
- "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
- "dev": true
- },
- "trim-right": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
- "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
- "dev": true
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "optional": true
}
}
},
- "browserslist": {
- "version": "2.11.3",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz",
- "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==",
+ "convert-source-map": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
+ "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
"dev": true,
"requires": {
- "caniuse-lite": "1.0.30000821",
- "electron-to-chromium": "1.3.41"
+ "safe-buffer": "~5.1.1"
}
},
- "buffer-from": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
- "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==",
- "optional": true
- },
- "caniuse-lite": {
- "version": "1.0.30000821",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000821.tgz",
- "integrity": "sha512-qyYay02wr/5k7PO86W+LKFaEUZfWIvT65PaXuPP16jkSpgZGIsSstHKiYAPVLjTj98j2WnWwZg8CjXPx7UIPYg==",
+ "core-js": {
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
+ "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==",
"dev": true
},
- "cfb": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.0.5.tgz",
- "integrity": "sha512-z1BN+JkopTE4vYu0sx25da2ZFurcN8gUKcBpT2ThCDlNFtWBozId8AHMs4OS7jTPLCJYK30Ud7QgcioyGkkkbg==",
- "optional": true,
- "requires": {
- "commander": "2.15.1",
- "printj": "1.1.2"
- },
- "dependencies": {
- "commander": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
- "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
- "optional": true
- }
- }
- },
- "codepage": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.3.8.tgz",
- "integrity": "sha1-Ty5dfAl13ij4hJgFjcta/KtqX3E=",
- "optional": true,
- "requires": {
- "commander": "2.15.1",
- "concat-stream": "1.6.2",
- "voc": "1.1.0"
- },
- "dependencies": {
- "commander": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
- "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
- "optional": true
- }
- }
- },
- "colors": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
- "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
- "optional": true
- },
- "concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "optional": true,
- "requires": {
- "buffer-from": "1.0.0",
- "inherits": "2.0.3",
- "readable-stream": "2.3.5",
- "typedarray": "0.0.6"
- },
- "dependencies": {
- "core-util-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
- "optional": true
- },
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "optional": true
- },
- "process-nextick-args": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
- "optional": true
- },
- "readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
- "optional": true,
- "requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "2.0.0",
- "safe-buffer": "5.1.1",
- "string_decoder": "1.0.3",
- "util-deprecate": "1.0.2"
- }
- },
- "string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
- "optional": true,
- "requires": {
- "safe-buffer": "5.1.1"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
- "optional": true
- }
- }
- },
"crc-32": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
"integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
"optional": true,
"requires": {
- "exit-on-epipe": "1.0.1",
- "printj": "1.1.2"
+ "exit-on-epipe": "~1.0.1",
+ "printj": "~1.1.0"
}
},
"d3": {
@@ -1894,12 +1678,48 @@
"resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
"integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g="
},
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "detect-indent": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
"electron-to-chromium": {
"version": "1.3.41",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.41.tgz",
"integrity": "sha1-fjNkPgDNhe39F+BBlPbQDnNzcjU=",
"dev": true
},
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.2.1.tgz",
+ "integrity": "sha1-va/oCVOD2EFNXcLs9MkXO225QS4=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
"exit-on-epipe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
@@ -1911,19 +1731,68 @@
"integrity": "sha1-V3Z3t/3L5vr3xGHxgB00E3zaQ1Q=",
"optional": true
},
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+ "dev": true
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "home-or-tmp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
+ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
+ "dev": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.1"
+ }
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "is-finite": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+ "dev": true
+ },
"js-xlsx": {
"version": "0.8.22",
"resolved": "https://registry.npmjs.org/js-xlsx/-/js-xlsx-0.8.22.tgz",
"integrity": "sha512-3N4a9RBHTr777rxxlvwJVpC+er/neRC+40sm2M/g3RIpWiCJG0iyaGJa8Za1K3NvjhZcKn9Sz5n36TY9ti5RMQ==",
"optional": true,
"requires": {
- "adler-32": "1.2.0",
- "cfb": "1.0.5",
- "codepage": "1.3.8",
- "commander": "2.15.1",
- "crc-32": "1.2.0",
+ "adler-32": "^1.2.0",
+ "cfb": ">=0.10.0",
+ "codepage": "~1.3.6",
+ "commander": "^2.15.1",
+ "crc-32": "^1.2.0",
"jszip": "2.4.0",
- "ssf": "0.8.2"
+ "ssf": "~0.8.1"
},
"dependencies": {
"commander": {
@@ -1934,21 +1803,102 @@
}
}
},
+ "jsesc": {
+ "version": "1.3.0",
+ "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+ "dev": true
+ },
+ "json5": {
+ "version": "0.5.1",
+ "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+ "dev": true
+ },
"jszip": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-2.4.0.tgz",
"integrity": "sha1-SHqTt2w7/6bLCFzWHrk06r4tKU8=",
"optional": true,
"requires": {
- "pako": "0.2.9"
+ "pako": "~0.2.5"
+ }
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
}
},
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "dev": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
"pako": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
"integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
"optional": true
},
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
"prettier": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.11.1.tgz",
@@ -1960,584 +1910,54 @@
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="
},
+ "private": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+ "dev": true
+ },
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+ "dev": true
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true,
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
"rollup": {
- "version": "0.50.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.50.1.tgz",
- "integrity": "sha512-XwrnqjSTk+yR8GbP6hiJuVe83MVmBw/gm4P3qP34A10fRXvv6ppl0ZUg1+Pj1tIZSR/aw5ZaILLEiVxwXIAdAw==",
- "dev": true
+ "version": "0.66.6",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.66.6.tgz",
+ "integrity": "sha512-J7/SWanrcb83vfIHqa8+aVVGzy457GcjA6GVZEnD0x2u4OnOd0Q1pCrEoNe8yLwM6z6LZP02zBT2uW0yh5TqOw==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "0.0.39",
+ "@types/node": "*"
+ }
},
"rollup-plugin-babel": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-3.0.3.tgz",
- "integrity": "sha512-5kzM/Rr4jQSRPLc2eN5NuD+CI/6AAy7S1O18Ogu4U3nq1Q42VJn0C9EMtqnvxtfwf1XrezOtdA9ro1VZI5B0mA==",
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-3.0.7.tgz",
+ "integrity": "sha512-bVe2y0z/V5Ax1qU8NX/0idmzIwJPdUGu8Xx3vXH73h0yGjxfv2gkFI82MBVg49SlsFlLTBadBHb67zy4TWM3hA==",
"dev": true,
"requires": {
- "rollup-pluginutils": "1.5.2"
- },
- "dependencies": {
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "dev": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "requires": {
- "balanced-match": "1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
- },
- "estree-walker": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.2.1.tgz",
- "integrity": "sha1-va/oCVOD2EFNXcLs9MkXO225QS4=",
- "dev": true
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "dev": true,
- "requires": {
- "brace-expansion": "1.1.11"
- }
- },
- "rollup-pluginutils": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz",
- "integrity": "sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=",
- "dev": true,
- "requires": {
- "estree-walker": "0.2.1",
- "minimatch": "3.0.4"
- }
- }
+ "rollup-pluginutils": "^1.5.0"
}
},
- "rollup-watch": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/rollup-watch/-/rollup-watch-4.3.1.tgz",
- "integrity": "sha512-6yjnIwfjpSrqA8IafyIu7fsEyeImNR4aDjA1bQ7KWeVuiA+Clfsx8+PGQkyABWIQzmauQ//tIJ5wAxLXsXs8qQ==",
+ "rollup-pluginutils": {
+ "version": "1.5.2",
+ "resolved": "http://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz",
+ "integrity": "sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg=",
"dev": true,
"requires": {
- "chokidar": "1.7.0",
- "require-relative": "0.8.7",
- "rollup-pluginutils": "2.0.1"
- },
- "dependencies": {
- "anymatch": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
- "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
- "dev": true,
- "requires": {
- "micromatch": "2.3.11",
- "normalize-path": "2.1.1"
- }
- },
- "arr-diff": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
- "dev": true,
- "requires": {
- "arr-flatten": "1.1.0"
- }
- },
- "arr-flatten": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
- "dev": true
- },
- "array-unique": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
- "dev": true
- },
- "async-each": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
- "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
- "dev": true
- },
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "dev": true
- },
- "binary-extensions": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
- "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
- "dev": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "requires": {
- "balanced-match": "1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "braces": {
- "version": "1.8.5",
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
- "dev": true,
- "requires": {
- "expand-range": "1.8.2",
- "preserve": "0.2.0",
- "repeat-element": "1.1.2"
- }
- },
- "chokidar": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
- "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
- "dev": true,
- "requires": {
- "anymatch": "1.3.2",
- "async-each": "1.0.1",
- "glob-parent": "2.0.0",
- "inherits": "2.0.3",
- "is-binary-path": "1.0.1",
- "is-glob": "2.0.1",
- "path-is-absolute": "1.0.1",
- "readdirp": "2.1.0"
- }
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
- "dev": true
- },
- "estree-walker": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz",
- "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=",
- "dev": true
- },
- "expand-brackets": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
- "dev": true,
- "requires": {
- "is-posix-bracket": "0.1.1"
- }
- },
- "expand-range": {
- "version": "1.8.2",
- "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
- "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
- "dev": true,
- "requires": {
- "fill-range": "2.2.3"
- }
- },
- "extglob": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
- "dev": true,
- "requires": {
- "is-extglob": "1.0.0"
- }
- },
- "filename-regex": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
- "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=",
- "dev": true
- },
- "fill-range": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz",
- "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=",
- "dev": true,
- "requires": {
- "is-number": "2.1.0",
- "isobject": "2.1.0",
- "randomatic": "1.1.7",
- "repeat-element": "1.1.2",
- "repeat-string": "1.6.1"
- }
- },
- "for-in": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
- "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
- "dev": true
- },
- "for-own": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
- "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
- "dev": true,
- "requires": {
- "for-in": "1.0.2"
- }
- },
- "glob-base": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
- "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
- "dev": true,
- "requires": {
- "glob-parent": "2.0.0",
- "is-glob": "2.0.1"
- }
- },
- "glob-parent": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
- "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
- "dev": true,
- "requires": {
- "is-glob": "2.0.1"
- }
- },
- "graceful-fs": {
- "version": "4.1.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
- "dev": true
- },
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
- "dev": true
- },
- "is-binary-path": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
- "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
- "dev": true,
- "requires": {
- "binary-extensions": "1.11.0"
- }
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
- "dev": true
- },
- "is-dotfile": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
- "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
- "dev": true
- },
- "is-equal-shallow": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
- "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
- "dev": true,
- "requires": {
- "is-primitive": "2.0.0"
- }
- },
- "is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
- "dev": true
- },
- "is-extglob": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
- "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
- "dev": true
- },
- "is-glob": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
- "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
- "dev": true,
- "requires": {
- "is-extglob": "1.0.0"
- }
- },
- "is-number": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
- "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
- "dev": true,
- "requires": {
- "kind-of": "3.2.2"
- }
- },
- "is-posix-bracket": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
- "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=",
- "dev": true
- },
- "is-primitive": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
- "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
- "dev": true
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
- "dev": true
- },
- "isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
- "dev": true,
- "requires": {
- "isarray": "1.0.0"
- }
- },
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "1.1.6"
- }
- },
- "micromatch": {
- "version": "2.3.11",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
- "dev": true,
- "requires": {
- "arr-diff": "2.0.0",
- "array-unique": "0.2.1",
- "braces": "1.8.5",
- "expand-brackets": "0.1.5",
- "extglob": "0.3.2",
- "filename-regex": "2.0.1",
- "is-extglob": "1.0.0",
- "is-glob": "2.0.1",
- "kind-of": "3.2.2",
- "normalize-path": "2.1.1",
- "object.omit": "2.0.1",
- "parse-glob": "3.0.4",
- "regex-cache": "0.4.4"
- }
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "dev": true,
- "requires": {
- "brace-expansion": "1.1.11"
- }
- },
- "normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
- "dev": true,
- "requires": {
- "remove-trailing-separator": "1.1.0"
- }
- },
- "object.omit": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
- "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
- "dev": true,
- "requires": {
- "for-own": "0.1.5",
- "is-extendable": "0.1.1"
- }
- },
- "parse-glob": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
- "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
- "dev": true,
- "requires": {
- "glob-base": "0.3.0",
- "is-dotfile": "1.0.3",
- "is-extglob": "1.0.0",
- "is-glob": "2.0.1"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
- },
- "preserve": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
- "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=",
- "dev": true
- },
- "process-nextick-args": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
- "dev": true
- },
- "randomatic": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
- "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==",
- "dev": true,
- "requires": {
- "is-number": "3.0.0",
- "kind-of": "4.0.0"
- },
- "dependencies": {
- "is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
- "dev": true,
- "requires": {
- "kind-of": "3.2.2"
- },
- "dependencies": {
- "kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
- "dev": true,
- "requires": {
- "is-buffer": "1.1.6"
- }
- }
- }
- },
- "kind-of": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
- "dev": true,
- "requires": {
- "is-buffer": "1.1.6"
- }
- }
- }
- },
- "readable-stream": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
- "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
- "dev": true,
- "requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "2.0.0",
- "safe-buffer": "5.1.1",
- "string_decoder": "1.0.3",
- "util-deprecate": "1.0.2"
- }
- },
- "readdirp": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz",
- "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=",
- "dev": true,
- "requires": {
- "graceful-fs": "4.1.11",
- "minimatch": "3.0.4",
- "readable-stream": "2.3.5",
- "set-immediate-shim": "1.0.1"
- }
- },
- "regex-cache": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
- "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
- "dev": true,
- "requires": {
- "is-equal-shallow": "0.1.3"
- }
- },
- "remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
- "dev": true
- },
- "repeat-element": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
- "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
- "dev": true
- },
- "repeat-string": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
- "dev": true
- },
- "require-relative": {
- "version": "0.8.7",
- "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
- "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
- "dev": true
- },
- "rollup-pluginutils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz",
- "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=",
- "dev": true,
- "requires": {
- "estree-walker": "0.3.1",
- "micromatch": "2.3.11"
- }
- },
- "set-immediate-shim": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
- "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
- "dev": true
- },
- "string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
- "dev": true,
- "requires": {
- "safe-buffer": "5.1.1"
- }
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
- "dev": true
- }
+ "estree-walker": "^0.2.1",
+ "minimatch": "^3.0.2"
}
},
"safe-buffer": {
@@ -2551,6 +1971,27 @@
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true
},
+ "slash": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.6"
+ }
+ },
"ssf": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.8.2.tgz",
@@ -2559,9 +2000,36 @@
"requires": {
"colors": "0.6.2",
"frac": "0.3.1",
- "voc": "1.1.0"
+ "voc": "^1.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
}
},
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+ "dev": true
+ },
+ "trim-right": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
+ "dev": true
+ },
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -2578,8 +2046,8 @@
"resolved": "https://registry.npmjs.org/webcharts/-/webcharts-1.10.0.tgz",
"integrity": "sha512-YeHYeYcsWNa2SO0BWSiUl4xGDQB4i3zyIdZTFeAGbxK8mcObCdKudOj9umA7IgSIRM2yUp7XEWj2x17zPgKB7w==",
"requires": {
- "d3": "3.5.17",
- "js-xlsx": "0.8.22"
+ "d3": "^3",
+ "js-xlsx": "^0.8.22"
}
}
}
diff --git a/package.json b/package.json
index c91f342..2be6937 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,9 @@
{
"name": "query-overview",
- "version": "1.2.3",
+ "version": "2.0.0",
"description": "Interactive bar chart for exploration of query data from clinical trials",
+ "module": "./src/index.js",
"main": "./build/queryOverview.js",
- "module": "./src/wrapper.js",
"author": "Rho, Inc.",
"license": "MIT",
"dependencies": {
@@ -11,22 +11,25 @@
"webcharts": "~1"
},
"scripts": {
- "build": "npm run bundle && npm run format && npm run build-md && npm run test-page",
- "build-md": "node ./scripts/configuration-markdown.js",
+ "build": "npm audit fix && npm run bundle && npm run format && npm run build-wiki && npm run check-settings-schema",
+ "build-wiki": "npm run build-configuration-wiki && npm run build-data-guidelines-wiki",
+ "build-configuration-wiki": "node ./scripts/build-configuration-wiki.js",
+ "build-data-guidelines-wiki": "node ./scripts/build-data-guidelines-wiki.js",
"bundle": "rollup -c",
+ "check-settings-schema": "node ./scripts/check-settings-schema.js",
"format": "npm run format-src && npm run format-bundle",
- "format-src": "prettier --print-width=100 --tab-width=4 --single-quote --write \"./src/**/*.js\"",
+ "format-src": "prettier --print-width=100 --tab-width=4 --single-quote --write \"./src/**/!(*styles).js\"",
"format-bundle": "prettier --print-width=100 --tab-width=4 --single-quote --write ./build/*.js",
- "test-page": "start chrome ./build/test-page/index.html && start firefox ./build/test-page/index.html && start iexplore file://%CD%/build/test-page/index.html",
+ "test-page": "start chrome ./test-page/index.html && start firefox ./test-page/index.html && start iexplore file://%CD%/test-page/index.html",
"watch": "rollup -c -w"
},
"devDependencies": {
+ "babel-core": "^6.26.3",
"babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.6.0",
"babel-register": "^6.26.0",
"prettier": "^1.7.4",
- "rollup": "^0.50.0",
- "rollup-plugin-babel": "^3.0.2",
- "rollup-watch": "^4.3.1"
+ "rollup": "^0.66.6",
+ "rollup-plugin-babel": "^3.0.2"
}
}
diff --git a/scripts/configuration-markdown.js b/scripts/build-configuration-wiki.js
similarity index 73%
rename from scripts/configuration-markdown.js
rename to scripts/build-configuration-wiki.js
index b96f82d..e974dbf 100644
--- a/scripts/configuration-markdown.js
+++ b/scripts/build-configuration-wiki.js
@@ -1,8 +1,12 @@
-var pkg = require('../package'),
- schema = require('../settings-schema'),
- properties = schema.properties,
- markdown = [],
- fs = require('fs');
+require('babel-register');
+const fs = require('fs');
+const pkg = require('../package');
+const schema = require('../settings-schema');
+const properties = schema.properties;
+const settings = require('../src/configuration/rendererSettings.js').default;
+const chartSettings = require('../src/configuration/chartSettings.js').default;
+const listingSettings = require('../src/configuration/listingSettings.js').default;
+const markdown = [];
function setDefault(setting) {
let settingDefault = '**default:** ';
@@ -51,7 +55,7 @@ function setDefault(setting) {
markdown.push(`## settings.${property}`);
markdown.push(`\`${setting.type}\``);
markdown.push(``);
- markdown.push(`${setting.description}`);
+ markdown.push(`${setting.description || setting.title}`);
if (setting.type !== 'object') {
markdown.push(``);
@@ -64,7 +68,7 @@ function setDefault(setting) {
markdown.push(`### settings.${property}.${subProperty}`);
markdown.push(`\`${subSetting.type}\``);
markdown.push(``);
- markdown.push(`${subSetting.title}`);
+ markdown.push(`${subSetting.description || subSetting.title}`);
markdown.push(``);
markdown.push(setDefault(subSetting));
});
@@ -78,7 +82,7 @@ function setDefault(setting) {
markdown.push(`### settings.${property}[].${subProperty}`);
markdown.push(`\`${subSetting.type}\``);
markdown.push(``);
- markdown.push(`${subSetting.title}`);
+ markdown.push(`${subSetting.description || subSetting.title}`);
markdown.push(``);
markdown.push(setDefault(subSetting));
});
@@ -95,29 +99,33 @@ function setDefault(setting) {
Webcharts settings
\------------------------------------------------------------------------------------------------*/
- var webchartsSettingsFlag = 0,
- webchartsSettings = fs.readFileSync('./src/configuration/webchartsSettings.js', 'utf8')
- .split('\n')
- webchartsSettings.splice(0,1,'{\r');
- webchartsSettings.push('}');
-
markdown.push(``);
markdown.push(`# Webcharts settings`);
- markdown.push(`The object below contains each Webcharts setting as of version ${schema.version}.`);
- markdown.push(``);
- markdown.push('```');
- markdown.push(webchartsSettings.join(''));
- markdown.push('```');
+ markdown.push(`The objects below contain Webcharts settings for each display as of version ${schema.version} of the ${pkg.name.split('-').map(str => str.substring(0,1).toUpperCase() + str.substring(1).toLowerCase()).join(' ')}.`);
+
+ [
+ chartSettings,
+ listingSettings,
+ ].forEach(settingsFx => {
+ const settings = JSON.stringify(settingsFx(), null, 4);
+ const display = settingsFx.name.replace('Settings', '');
+
+ markdown.push(``);
+ markdown.push(`## ${display.substring(0,1).toUpperCase()}${display.substring(1)}`);
+ markdown.push('```');
+ markdown.push(settings);
+ markdown.push('```');
+ });
/*------------------------------------------------------------------------------------------------\
Configuration markdown
\------------------------------------------------------------------------------------------------*/
fs.writeFile(
- './scripts/configuration.md',
+ './scripts/configuration-wiki.md',
markdown.join('\n'),
(err) => {
if (err)
console.log(err);
- console.log('The configuration markdown file was built!');
+ console.log('The configuration wiki markdown file was built!');
});
diff --git a/scripts/build-data-guidelines-wiki.js b/scripts/build-data-guidelines-wiki.js
new file mode 100644
index 0000000..e81263a
--- /dev/null
+++ b/scripts/build-data-guidelines-wiki.js
@@ -0,0 +1,80 @@
+require('babel-register');
+const fs = require('fs');
+const pkg = require('../package');
+const schema = require('../settings-schema');
+
+//Create markdown array, one item per line.
+const markdown = [
+ schema['data-guidelines'],
+ '',
+ '## Data structure',
+ schema['data-structure'],
+ '',
+ '## Data specification',
+ 'required and optional variables:',
+ '',
+ '| Setting | Default | Data Type | Description | Required? |',
+ '|:--------|:--------|:----------|:------------|:---------:|',
+];
+
+//Add variable table to markdown array.
+const properties = schema.properties;
+const settings = Object.keys(properties);
+const variables = settings
+ .filter(setting => properties[setting]['data-mapping'])
+ .map(setting => {
+ const property = properties[setting];
+ property.setting = setting;
+
+ return property;
+ });
+variables.forEach(variable => {
+ if (['string', 'number'].indexOf(variable.type) > -1)
+ markdown.push(
+ `|\`${
+ variable.setting}\`|${
+ variable.default ? `_${variable.default}_` : ''
+ }|**${
+ variable['data-type']}**|${
+ variable.description.replace(/name of variable that (captures )?/, '')}|${
+ variable.required ? '**Yes**' : ''
+ }|`
+ );
+ else if (variable.type === 'array') {
+ if (variable.defaults)
+ variable.defaults.forEach((item,i) => {
+ markdown.push(
+ `|\`${
+ variable.setting}[${i}]\`|_${
+ item}_|**${
+ variable['data-type']}**|${
+ variable.descriptions[item]}|${
+ variable.required ? '**Yes**' : ''
+ }|`
+ )
+ });
+ else
+ markdown.push(
+ `|\`${
+ variable.setting}[]\`||**${
+ variable['data-type']}**|${
+ variable.description}|${
+ variable.required ? '**Yes**' : ''
+ }|`
+ );
+ } else
+ console.warn(`This wiki can't handle ${variable.type}s! Get outta here!`);
+});
+
+/*------------------------------------------------------------------------------------------------\
+ Configuration markdown
+\------------------------------------------------------------------------------------------------*/
+
+ fs.writeFile(
+ './scripts/data-guidelines-wiki.md',
+ markdown.join('\n'),
+ (err) => {
+ if (err)
+ console.log(err);
+ console.log('The data guidelines wiki markdown file was built!');
+ });
diff --git a/scripts/check-settings-schema.js b/scripts/check-settings-schema.js
new file mode 100644
index 0000000..4b72155
--- /dev/null
+++ b/scripts/check-settings-schema.js
@@ -0,0 +1,26 @@
+require('babel-register');
+const fs = require('fs');
+const pkg = require('../package');
+
+//settings schema
+const schema = require('../settings-schema');
+const properties = schema.properties;
+const expectedSettings = Object.keys(properties);
+
+//renderer settings
+const settings = require('../src/configuration/rendererSettings.js').default();
+const actualSettings = Object.keys(settings);
+
+//differences
+const missingExpectedSettings = actualSettings
+ .filter(setting => expectedSettings.indexOf(setting) < 0);
+if (missingExpectedSettings.length > 0) {
+ console.log('\x1b[31m%s\x1b[0m', 'These settings are missing from the settings schema:\n');
+ console.log('\x1b[31m%s\x1b[0m', `${JSON.stringify(missingExpectedSettings, null, 4)}\n`);
+}
+const missingActualSettings = expectedSettings
+ .filter(setting => actualSettings.indexOf(setting) < 0);
+if (missingActualSettings.length > 0) {
+ console.log('\x1b[31m%s\x1b[0m', 'These settings are missing from the renderer settings:\n');
+ console.log('\x1b[31m%s\x1b[0m', `${JSON.stringify(missingActualSettings, null, 4)}\n`);
+}
diff --git a/scripts/configuration.md b/scripts/configuration-wiki.md
similarity index 50%
rename from scripts/configuration.md
rename to scripts/configuration-wiki.md
index 422d741..5bcd89b 100644
--- a/scripts/configuration.md
+++ b/scripts/configuration-wiki.md
@@ -3,7 +3,7 @@ The most straightforward way to customize query-overview is by using a configura
In addition to the standard Webcharts settings several custom settings not available in the base Webcharts library have been added to query-overview to facilitate data mapping and other custom functionality. These custom settings are described in detail below. All defaults can be overwritten by users.
# Renderer-specific settings
-The sections below describe each query-overview setting as of version 1.2.3.
+The sections below describe each query-overview setting as of version 2.0.0.
## settings.form_col
`string`
@@ -37,58 +37,55 @@ field variable name
field description variable name
-**default:** `"fieldname"`
+**default:** `"null"`
-## settings.marking_group_col
+## settings.site_col
`string`
-query origin variable name
+site variable name
-**default:** `"markinggroup"`
+**default:** `"sitename"`
-## settings.visit_col
+## settings.marking_group_col
`string`
-visit/folder variable name
+query origin variable name
-**default:** `"folderoid"`
+**default:** `"markinggroup"`
-## settings.open_col
+## settings.visit_col
`string`
-variable name for how long a query has been open
+visit/folder variable name
-**default:** `"open_time"`
+**default:** `"folderoid"`
-## settings.open_category_col
+## settings.color_by_col
`string`
-Categorical version of query open time variable
+coloring variable name of query categorization: query age, query status, or any custom categorization; note that _queryage_ is derived by the renderer
-**default:** `"Query Open Time Category"`
+**default:** `"queryage"`
-## settings.open_category_order
+## settings.age_statuses
`array`
-an array of query open time that dictates how they are ordered in the chart
+an array of query statuses for which query age will be derived
**default:**
```
[
- "0-7 days",
- "8-14 days",
- "15-30 days",
- ">30 days"
+ "Open"
]
```
@@ -97,42 +94,46 @@ an array of query open time that dictates how they are ordered in the chart
## settings.age_col
`string`
-query age variable name
+query age variable measured in days between query open date and data snapshot date, query response date, or query resolution date for open, answered, and resolved queries, respectively
**default:** `"qdays"`
-## settings.age_category_col
-`string`
-
-Categorical version of query age variable
-
-**default:** `"Query Age Category"`
-
-
-
-## settings.age_category_order
+## settings.age_cutoffs
`array`
-an array of query age categories that dictates how they are ordered in the legend and chart
+an array of query age cutoffs for which query age range will be derived
-**default:** none
+**default:**
+```
+[
+ 14,
+ 28,
+ 56,
+ 112
+]
+```
-## settings.age_category_colors
+## settings.age_range_colors
`array`
-an array of colors that determines the colors for query age categories
+an array of colors with which to color query age range categories; only as many colors as there are query age range categories will be used, from darkest to lightest
**default:**
```
[
- "#fcae91",
- "#fb6a4a",
- "#de2d26",
- "#a50f15"
+ "#ffffcc",
+ "#ffeda0",
+ "#fed976",
+ "#feb24c",
+ "#fd8d3c",
+ "#fc4e2a",
+ "#e31a1c",
+ "#bd0026",
+ "#800026"
]
```
@@ -172,10 +173,44 @@ an array of colors that determines the colors for query statuses
**default:**
```
[
- "#fb9a99",
- "#fdbf6f",
- "#1f78b4",
- "gray"
+ "#fd8d3c",
+ "#4daf4a",
+ "#377eb8",
+ "#999999"
+]
+```
+
+
+
+## settings.recency_category_col
+`string`
+
+query recency category variable name; overrides `recency_col`
+
+**default:** `"open_time"`
+
+
+
+## settings.recency_col
+`string`
+
+query recency variable measured in days between query open date and data snapshot date, regardless of query status
+
+**default:** `"odays"`
+
+
+
+## settings.recency_cutoffs
+`array`
+
+an array of query recency cutoffs for which query recency range will be derived
+
+**default:**
+```
+[
+ 7,
+ 14,
+ 30
]
```
@@ -184,21 +219,21 @@ an array of colors that determines the colors for query statuses
## settings.groups
`array`
-an array of group-by variables by which to group queries on the y-axis
+an array of variables by which to count queries; each value of the variable is plotted on the y-axis
**default:** none
### settings.groups[].value_col
`string`
-Group-by Variable
+group-by variable name
**default:** none
### settings.groups[].label
`string`
-Group-by Label
+group-by variable label
**default:** none
@@ -207,101 +242,101 @@ Group-by Label
## settings.status_groups
`array`
-an array of Stratification variables by which to stratify the stacked bars
+an array of variables with which to stratify each group-by value, each value of which plots as a component of a stacked bar when Bar Arrangement is set to _Stacked_ or as individual bars when Bar Arrangement is set to _grouped_
**default:** none
### settings.status_groups[].value_col
`string`
-Stratification Variable
+Stratification variable name
**default:** none
### settings.status_groups[].label
`string`
-Stratification Label
+Stratification variable label
**default:** none
### settings.status_groups[].order
`array`
-Stratification Order
+Stratification variable order
**default:** none
### settings.status_groups[].colors
`array`
-Stratification Colors
+Stratification variable colors
**default:** none
-## settings.site_col
-`string`
-
-site variable name
-
-**default:** `"sitename"`
-
-
-
## settings.filters
`array`
-an array of filter variables with which to filter the data
+an array of variables with which to filter the data
**default:** none
### settings.filters[].value_col
`string`
-Filter Variable
+filter variable name
**default:** none
### settings.filters[].label
`string`
-Filter Label
+filter variable label
**default:** none
+## settings.dropdown_size
+`number`
+
+controls the maximum number of options that appear in the multi-select dropdowns before a scrollbar appears
+
+**default:** `6`
+
+
+
## settings.details
`array`
-an array of detail variables which will print in the detail listing
+an array of variables which will print in the listing; if unspecified all variables in data will appear in listing
**default:** none
### settings.details[].value_col
`string`
-Detail Listing Variable
+detail listing variable name
**default:** none
### settings.details[].label
`string`
-Detail Listing Column Header
+detail listing column header
**default:** none
-## settings.dropdown_size
-`number`
+## settings.bar_arrangement
+`string`
-controls the maximum number of options that appear in the multi-select dropdowns before a scrollbar appears
+controls arrangement of bars, either stacked or grouped side-by-side
-**default:** `6`
+**default:** `"stacked"`
@@ -323,25 +358,69 @@ sort groups on the y-axis alphanumerically; by default groups are sorted by desc
-## settings.exportable
+## settings.truncate_listing_cells
`boolean`
-allow the export of data to .csv via a button beneath the detail listing
+optionally truncate cell text past a certain number of characters
**default:** `true`
-## settings.nRowsPerPage
+## settings.truncation_cutoff
`number`
-the number of rows displayed per page.
+cell text past this cutoff will be truncated and the full text will be captured in a tooltip
-**default:** `10`
+**default:** `100`
# Webcharts settings
-The object below contains each Webcharts setting as of version 1.2.3.
+The objects below contain Webcharts settings for each display as of version 2.0.0 of the Query Overview.
+
+## Chart
+```
+{
+ "x": {
+ "label": "# of Queries",
+ "column": null,
+ "behavior": "flex"
+ },
+ "y": {
+ "type": "ordinal",
+ "column": null,
+ "label": "Form",
+ "sort": null
+ },
+ "marks": [
+ {
+ "type": "bar",
+ "per": [
+ null
+ ],
+ "split": null,
+ "arrange": null,
+ "summarizeX": "count",
+ "tooltip": null
+ }
+ ],
+ "color_by": null,
+ "color_dom": null,
+ "legend": {
+ "location": "top",
+ "label": null,
+ "order": null
+ },
+ "margin": {
+ "right": "50"
+ },
+ "range_band": 25
+}
+```
+## Listing
```
-{
x: {
label: '# of Queries',
behavior: 'flex'
},
y: {
type: 'ordinal',
column: null, // set in syncSettings()
label: 'Form',
sort: 'total-descending'
},
marks: [
{
type: 'bar',
per: [null], // set in syncSettings()
split: null, // set in syncSettings()
arrange: 'stacked',
summarizeX: 'count',
tooltip: null // set in syncSettings()
}
],
color_by: null, // set in syncSettings()
color_dom: null, // set in syncSettings()
legend: {
location: 'top',
// label: 'Query Status',
label: null,
order: null // set in syncSettings()
},
range_band: 15,
margin: { right: '50' } // room for count annotation
};
}
+{
+ "nRowsPerPage": 25,
+ "exportable": true
+}
```
\ No newline at end of file
diff --git a/scripts/data-guidelines-wiki.md b/scripts/data-guidelines-wiki.md
new file mode 100644
index 0000000..1eab96f
--- /dev/null
+++ b/scripts/data-guidelines-wiki.md
@@ -0,0 +1,25 @@
+The Query Overview accepts [JSON](https://en.wikipedia.org/wiki/JSON) data of the format returned by [`d3.csv()`](https://github.com/d3/d3-3.x-api-reference/blob/master/CSV.md). The renderer visualizes clinical query data with **one row per query** plus the required variables specified below.
+
+## Data structure
+one record per query
+
+## Data specification
+required and optional variables:
+
+| Setting | Default | Data Type | Description | Required? |
+|:--------|:--------|:----------|:------------|:---------:|
+|`form_col`|_formoid_|**character**|form variable name|**Yes**|
+|`formDescription_col`|_ecrfpagename_|**character**|form description variable name|**Yes**|
+|`field_col`|_fieldname_|**character**|field variable name|**Yes**|
+|`fieldDescription_col`||**character**|field description variable name||
+|`site_col`|_sitename_|**character**|site variable name|**Yes**|
+|`marking_group_col`|_markinggroup_|**character**|query origin variable name|**Yes**|
+|`visit_col`|_folderoid_|**character**|visit/folder variable name|**Yes**|
+|`age_col`|_qdays_|**numeric**|query age variable measured in days between query open date and data snapshot date, query response date, or query resolution date for open, answered, and resolved queries, respectively|**Yes**|
+|`status_col`|_querystatus_|**character**|query status variable name|**Yes**|
+|`recency_category_col`|_open_time_|**character**|query recency category variable name; overrides `recency_col`||
+|`recency_col`|_odays_|**numeric**|query recency variable measured in days between query open date and data snapshot date, regardless of query status||
+|`groups[]`||**either**|an array of variables by which to count queries; each value of the variable is plotted on the y-axis||
+|`status_groups[]`||**either**|an array of variables with which to stratify each group-by value, each value of which plots as a component of a stacked bar when Bar Arrangement is set to _Stacked_ or as individual bars when Bar Arrangement is set to _grouped_||
+|`filters[]`||**either**|an array of variables with which to filter the data||
+|`details[]`||**either**|an array of variables which will print in the listing; if unspecified all variables in data will appear in listing||
\ No newline at end of file
diff --git a/settings-schema.json b/settings-schema.json
index 1ac0af7..db2a069 100644
--- a/settings-schema.json
+++ b/settings-schema.json
@@ -2,116 +2,151 @@
"title": "settings",
"description": "JSON schema for the configuration of query-overview",
"overview": "The most straightforward way to customize query-overview is by using a configuration object whose properties describe the behavior and appearance of the chart. Since query-overview is a Webcharts `chart` object, many default Webcharts settings are set in the [defaultSettings.js file](https://github.com/RhoInc/query-overview/blob/master/src/defaultSettings.js) as [described below](#webcharts-settings). Refer to the [Webcharts documentation](https://github.com/RhoInc/Webcharts/wiki/Chart-Configuration) for more details on these settings.\nIn addition to the standard Webcharts settings several custom settings not available in the base Webcharts library have been added to query-overview to facilitate data mapping and other custom functionality. These custom settings are described in detail below. All defaults can be overwritten by users.",
- "version": "1.2.3",
+ "version": "2.0.0",
"type": "object",
+ "data-guidelines": "The Query Overview accepts [JSON](https://en.wikipedia.org/wiki/JSON) data of the format returned by [`d3.csv()`](https://github.com/d3/d3-3.x-api-reference/blob/master/CSV.md). The renderer visualizes clinical query data with **one row per query** plus the required variables specified below.",
+ "data-structure": "one record per query",
"properties": {
"form_col": {
"title": "Form Variable",
"description": "form variable name",
"type": "string",
- "default": "formoid"
+ "default": "formoid",
+ "data-mapping": true,
+ "data-type": "character",
+ "required": true
},
"formDescription_col": {
"title": "Form Description Variable",
"description": "form description variable name",
"type": "string",
- "default": "ecrfpagename"
+ "default": "ecrfpagename",
+ "data-mapping": true,
+ "data-type": "character",
+ "required": true
},
"field_col": {
"title": "Field Variable",
"description": "field variable name",
"type": "string",
- "default": "fieldname"
+ "default": "fieldname",
+ "data-mapping": true,
+ "data-type": "character",
+ "required": true
},
"fieldDescription_col": {
"title": "Field Description Variable",
"description": "field description variable name",
"type": "string",
- "default": "fieldname"
+ "default": null,
+ "data-mapping": true,
+ "data-type": "character",
+ "required": false
+ },
+ "site_col": {
+ "title": "Site Variable",
+ "description": "site variable name",
+ "type": "string",
+ "default": "sitename",
+ "data-mapping": true,
+ "data-type": "character",
+ "required": true
},
"marking_group_col": {
"title": "Marking Group Variable",
"description": "query origin variable name",
"type": "string",
- "default": "markinggroup"
+ "default": "markinggroup",
+ "data-mapping": true,
+ "data-type": "character",
+ "required": true
},
"visit_col": {
"title": "Visit/Folder Variable",
"description": "visit/folder variable name",
"type": "string",
- "default": "folderoid"
- },
- "open_col": {
- "title": "Query Open Time Variable",
- "description": "variable name for how long a query has been open",
- "type": "string",
- "default": "open_time"
+ "default": "folderoid",
+ "data-mapping": true,
+ "data-type": "character",
+ "required": true
},
- "open_category_col": {
- "title": "Query Open Time Category Variable",
- "description": "Categorical version of query open time variable",
+ "color_by_col": {
+ "title": "Status Group Variable",
+ "description": "coloring variable name of query categorization: query age, query status, or any custom categorization; note that _queryage_ is derived by the renderer",
"type": "string",
- "default": "Query Open Time Category"
+ "default": "queryage",
+ "enum": [
+ "queryage",
+ "querystatus"
+ ]
},
- "open_category_order": {
- "title": "Query Open Time Order",
- "description": "an array of query open time that dictates how they are ordered in the chart",
+ "age_statuses": {
+ "title": "Query Statuses Applicable to Query Age",
+ "description": "an array of query statuses for which query age will be derived",
"type": "array",
"items": {
- "title": "Query Open Time Category",
- "description": "query open time category value",
+ "title": "Query Status",
+ "description": "query status value",
"type": "string"
},
"defaultObject": [
- "0-7 days",
- "8-14 days",
- "15-30 days",
- ">30 days"
+ "Open"
]
},
"age_col": {
- "title": "Query Age Variable",
- "description": "query age variable name",
+ "title": "Query Age",
+ "description": "query age variable measured in days between query open date and data snapshot date, query response date, or query resolution date for open, answered, and resolved queries, respectively",
"type": "string",
- "default": "qdays"
+ "default": "qdays",
+ "data-mapping": true,
+ "data-type": "numeric",
+ "required": true
},
- "age_category_col": {
- "title": "Query Age Category Variable",
- "description": "Categorical version of query age variable",
- "type": "string",
- "default": "Query Age Category"
- },
- "age_category_order": {
- "title": "Query Age Category Order",
- "description": "an array of query age categories that dictates how they are ordered in the legend and chart",
+ "age_cutoffs": {
+ "title": "Query Age Cutoffs",
+ "description": "an array of query age cutoffs for which query age range will be derived",
"type": "array",
"items": {
- "title": "Query Age Category",
- "description": "query age category value",
- "type": "string"
- }
+ "title": "Query Age",
+ "description": "query age measured in days between query open date and data snapshot date",
+ "type": "number"
+ },
+ "defaultObject": [
+ 14,
+ 28,
+ 56,
+ 112
+ ]
},
- "age_category_colors": {
- "title": "Query Age Category Color",
- "description": "an array of colors that determines the colors for query age categories",
+ "age_range_colors": {
+ "title": "Query Age Range Colors",
+ "description": "an array of colors with which to color query age range categories; only as many colors as there are query age range categories will be used, from darkest to lightest",
"type": "array",
"items": {
- "title": "Query Age Category",
- "description": "query age category value",
+ "title": "Query Age Range Color",
+ "description": "query age range color",
"type": "string"
},
"defaultObject": [
- "#fcae91",
- "#fb6a4a",
- "#de2d26",
- "#a50f15"
+ "#ffffcc",
+ "#ffeda0",
+ "#fed976",
+ "#feb24c",
+ "#fd8d3c",
+ "#fc4e2a",
+ "#e31a1c",
+ "#bd0026",
+ "#800026"
]
},
"status_col": {
"title": "Query Status Variable",
"description": "query status variable name",
"type": "string",
- "default": "querystatus"
+ "default": "querystatus",
+ "data-mapping": true,
+ "data-type": "character",
+ "required": true
},
"status_order": {
"title": "Query Status Order",
@@ -139,15 +174,48 @@
"type": "string"
},
"defaultObject": [
- "#fb9a99",
- "#fdbf6f",
- "#1f78b4",
- "gray"
+ "#fd8d3c",
+ "#4daf4a",
+ "#377eb8",
+ "#999999"
+ ]
+ },
+ "recency_category_col": {
+ "title": "Query Recency Category",
+ "description": "query recency category variable name; overrides `recency_col`",
+ "type": "string",
+ "default": "open_time",
+ "data-mapping": true,
+ "data-type": "character",
+ "required": false
+ },
+ "recency_col": {
+ "title": "Query Recency",
+ "description": "query recency variable measured in days between query open date and data snapshot date, regardless of query status",
+ "type": "string",
+ "default": "odays",
+ "data-mapping": true,
+ "data-type": "numeric",
+ "required": false
+ },
+ "recency_cutoffs": {
+ "title": "Query Recency Cutoffs",
+ "description": "an array of query recency cutoffs for which query recency range will be derived",
+ "type": "array",
+ "items": {
+ "title": "Query Recency",
+ "description": "query recency measured in days between query open date and data snapshot date or today...?",
+ "type": "number"
+ },
+ "defaultObject": [
+ 7,
+ 14,
+ 30
]
},
"groups": {
"title": "Group-by Variables",
- "description": "an array of group-by variables by which to group queries on the y-axis",
+ "description": "an array of variables by which to count queries; each value of the variable is plotted on the y-axis",
"type": "array",
"items": {
"type": "object",
@@ -162,12 +230,15 @@
"description": "group-by variable label",
"type": "string"
}
- }
- }
+ }
+ },
+ "data-mapping": true,
+ "data-type": "either",
+ "required": false
},
"status_groups": {
"title": "Stratification Variables",
- "description": "an array of Stratification variables by which to stratify the stacked bars",
+ "description": "an array of variables with which to stratify each group-by value, each value of which plots as a component of a stacked bar when Bar Arrangement is set to _Stacked_ or as individual bars when Bar Arrangement is set to _grouped_",
"type": "array",
"items": {
"type": "object",
@@ -193,17 +264,14 @@
"type": "array"
}
}
- }
- },
- "site_col": {
- "title": "Site Variable",
- "description": "site variable name",
- "type": "string",
- "default": "sitename"
+ },
+ "data-mapping": true,
+ "data-type": "either",
+ "required": false
},
"filters": {
"title": "Filter Variables",
- "description": "an array of filter variables with which to filter the data",
+ "description": "an array of variables with which to filter the data",
"type": "array",
"items": {
"type": "object",
@@ -219,11 +287,20 @@
"type": "string"
}
}
- }
+ },
+ "data-mapping": true,
+ "data-type": "either",
+ "required": false
+ },
+ "dropdown_size": {
+ "title": "Number of Options Display in Multi-select Dropdowns",
+ "description": "controls the maximum number of options that appear in the multi-select dropdowns before a scrollbar appears",
+ "type": "number",
+ "default": 6
},
"details": {
"title": "Detail Listing Variables",
- "description": "an array of detail variables which will print in the detail listing",
+ "description": "an array of variables which will print in the listing; if unspecified all variables in data will appear in listing",
"type": "array",
"items": {
"type": "object",
@@ -239,13 +316,20 @@
"type": "string"
}
}
- }
+ },
+ "data-mapping": true,
+ "data-type": "either",
+ "required": false
},
- "dropdown_size": {
- "title": "Number of Options Display in Multi-select Dropdowns",
- "description": "controls the maximum number of options that appear in the multi-select dropdowns before a scrollbar appears",
- "type": "number",
- "default": 6
+ "bar_arrangement": {
+ "title": "Arrangement of Bars",
+ "description": "controls arrangement of bars, either stacked or grouped side-by-side",
+ "type": "string",
+ "default": "stacked",
+ "enum": [
+ "stacked",
+ "grouped"
+ ]
},
"cutoff": {
"title": "Number of Groups Displayed Initially",
@@ -259,17 +343,17 @@
"type": "boolean",
"default": true
},
- "exportable": {
- "title": "Allow Listing Export?",
- "description": "allow the export of data to .csv via a button beneath the detail listing",
+ "truncate_listing_cells": {
+ "title": "Truncate Listing Cell Text?",
+ "description": "optionally truncate cell text past a certain number of characters",
"type": "boolean",
"default": true
},
- "nRowsPerPage": {
- "title": "Number of rows displayed per page.",
- "description": "the number of rows displayed per page.",
+ "truncation_cutoff": {
+ "title": "Maximum Length of Cell Text",
+ "description": "cell text past this cutoff will be truncated and the full text will be captured in a tooltip",
"type": "number",
- "default": 10
+ "default": 100
}
}
}
diff --git a/src/chart/index.js b/src/chart/index.js
new file mode 100644
index 0000000..d0a6a34
--- /dev/null
+++ b/src/chart/index.js
@@ -0,0 +1,17 @@
+import onInit from './onInit';
+import onLayout from './onLayout';
+import onPreprocess from './onPreprocess';
+import onDataTransform from './onDataTransform';
+import onDraw from './onDraw';
+import onResize from './onResize';
+import onDestroy from './onDestroy';
+
+export default {
+ onInit,
+ onLayout,
+ onPreprocess,
+ onDataTransform,
+ onDraw,
+ onResize,
+ onDestroy
+};
diff --git a/src/chart/onDraw.js b/src/chart/onDraw.js
index 2dd8554..51ed83c 100644
--- a/src/chart/onDraw.js
+++ b/src/chart/onDraw.js
@@ -1,10 +1,12 @@
import setLeftMargin from './onDraw/setLeftMargin';
+import setXDomain from './onDraw/setXDomain';
import setYDomain from './onDraw/setYDomain';
import setChartHeight from './onDraw/setChartHeight';
import updateXAxisLabel from './onDraw/updateXAxisLabel';
export default function onDraw() {
setLeftMargin.call(this);
+ setXDomain.call(this);
setYDomain.call(this);
setChartHeight.call(this);
updateXAxisLabel.call(this);
diff --git a/src/chart/onDraw/setChartHeight.js b/src/chart/onDraw/setChartHeight.js
index feb9245..594b895 100644
--- a/src/chart/onDraw/setChartHeight.js
+++ b/src/chart/onDraw/setChartHeight.js
@@ -1,6 +1,7 @@
export default function setChartHeight() {
//Match chart height to number of bars currently displayed.
- this.raw_height =
- (+this.config.range_band + this.config.range_band * this.config.padding) *
- this.y_dom.length;
+ this.raw_height = this.filtered_data.length
+ ? (+this.config.range_band + this.config.range_band * this.config.padding) *
+ this.y_dom.length
+ : 100;
}
diff --git a/src/chart/onDraw/setLeftMargin.js b/src/chart/onDraw/setLeftMargin.js
index a376ee1..6d10d98 100644
--- a/src/chart/onDraw/setLeftMargin.js
+++ b/src/chart/onDraw/setLeftMargin.js
@@ -1,5 +1,7 @@
export default function setLeftMargin() {
const fontSize = parseInt(this.wrap.style('font-size'));
- this.config.margin.left =
- Math.max(7, d3.max(this.y_dom, d => d.length)) * fontSize * 0.5 + fontSize * 1.5 * 1.5 + 6;
+ this.config.margin.left = Math.max(
+ Math.max(7, d3.max(this.y_dom, d => d.length)) * fontSize * 0.5 + fontSize * 1.5 * 1.5 + 6,
+ 100
+ );
}
diff --git a/src/chart/onDraw/setXDomain.js b/src/chart/onDraw/setXDomain.js
new file mode 100644
index 0000000..de30f1c
--- /dev/null
+++ b/src/chart/onDraw/setXDomain.js
@@ -0,0 +1,3 @@
+export default function setXDomain() {
+ if (this.filtered_data.length === 0) this.x_dom = [0, 0];
+}
diff --git a/src/chart/onInit.js b/src/chart/onInit.js
index 0a86af9..85af7a9 100644
--- a/src/chart/onInit.js
+++ b/src/chart/onInit.js
@@ -1,7 +1,7 @@
import defineListingSettings from './onInit/defineListingSettings';
import defineNewVariables from './onInit/defineNewVariables';
-import defineQueryStatuses from './onInit/defineQueryStatuses';
-import defineQueryAgeCategories from './onInit/defineQueryAgeCategories';
+import defineQueryStatusSet from './onInit/defineQueryStatusSet';
+import defineQueryRecencySet from './onInit/defineQueryRecencySet';
import removeInvalidControls from './onInit/removeInvalidControls';
export default function onInit() {
@@ -9,10 +9,10 @@ export default function onInit() {
defineNewVariables.call(this);
//Define query statuses.
- defineQueryStatuses.call(this);
+ defineQueryStatusSet.call(this);
- //Define query age categories.
- defineQueryAgeCategories.call(this);
+ //Define query recency categories.
+ defineQueryRecencySet.call(this);
//Define detail listing settings.
defineListingSettings.call(this);
diff --git a/src/chart/onInit/defineNewVariables.js b/src/chart/onInit/defineNewVariables.js
index b74f703..b0b0e6d 100644
--- a/src/chart/onInit/defineNewVariables.js
+++ b/src/chart/onInit/defineNewVariables.js
@@ -1,51 +1,42 @@
export default function defineNewVariables() {
+ const queryAgeCol = this.config.status_groups.find(
+ status_group => status_group.label === 'Query Age'
+ ).value_col;
+ const queryRecencyCol = this.config.filters.find(filter => filter.label === 'Query Recency')
+ .value_col;
+
this.raw_data.forEach(d => {
+ //Concatenate form and field to avoid duplicates across forms.
d['Form: Field'] = d[this.config.form_col] + ': ' + d[this.config.field_col];
- //Define query age category.
- if (!this.config.age_category_order) {
- const queryAge =
- /^ *\d+ *$/.test(d[this.config.age_col]) &&
- ['Closed', 'Cancelled'].indexOf(d[this.config.status_col]) < 0
- ? +d[this.config.age_col]
- : NaN;
- switch (true) {
- case queryAge <= 14:
- d['Query Age Category'] = '0-2 weeks';
- break;
- case queryAge <= 28:
- d['Query Age Category'] = '2-4 weeks';
- break;
- case queryAge <= 56:
- d['Query Age Category'] = '4-8 weeks';
- break;
- case queryAge <= 112:
- d['Query Age Category'] = '8-16 weeks';
- break;
- case queryAge > 112:
- d['Query Age Category'] = '>16 weeks';
- break;
- default:
- d['Query Age Category'] = d[this.config.status_col];
- break;
- }
+ //Define query age.
+ if (this.config.age_statuses.indexOf(d[this.config.status_col]) < 0)
+ d[queryAgeCol] = d[this.config.status_col];
+ else {
+ const age = +d[this.config.age_col];
+ this.config.ageRanges.forEach((ageRange, i) => {
+ if (i === 0 && ageRange[0] <= age && age <= ageRange[1])
+ d[queryAgeCol] = this.config.ageRangeCategories[i];
+ else if (i === this.config.ageRanges.length - 1 && ageRange[0] < age)
+ d[queryAgeCol] = this.config.ageRangeCategories[i];
+ else if (ageRange[0] < age && age <= ageRange[1])
+ d[queryAgeCol] = this.config.ageRangeCategories[i];
+ });
}
- //Define query open time category.
- const openTime = /^ *\d+ *$/.test(d[this.config.open_col]) ? +d[this.config.open_col] : NaN;
- switch (true) {
- case openTime <= 7:
- d['Query Open Time Category'] = '0-7 days';
- break;
- case openTime <= 14:
- d['Query Open Time Category'] = '8-14 days';
- break;
- case openTime <= 30:
- d['Query Open Time Category'] = '15-30 days';
- break;
- default:
- d['Query Open Time Category'] = '>30 days';
- break;
+ //Define query recency.
+ if (d.hasOwnProperty(this.config.recency_category_col)) {
+ d[queryRecencyCol] = d[this.config.recency_category_col] || 'N/A';
+ } else if (d.hasOwnProperty(this.config.recency_col)) {
+ const recency = +d[this.config.recency_col];
+ this.config.recencyRanges.forEach((recencyRange, i) => {
+ if (i === 0 && recencyRange[0] <= recency && recency <= recencyRange[1])
+ d[queryRecencyCol] = this.config.recencyRangeCategories[i];
+ else if (i === this.config.recencyRanges.length - 1 && recencyRange[0] < recency)
+ d[queryRecencyCol] = this.config.recencyRangeCategories[i];
+ else if (recencyRange[0] < recency && recency <= recencyRange[1])
+ d[queryRecencyCol] = this.config.recencyRangeCategories[i];
+ });
}
});
}
diff --git a/src/chart/onInit/defineQueryAgeCategories.js b/src/chart/onInit/defineQueryAgeCategories.js
deleted file mode 100644
index 49a904f..0000000
--- a/src/chart/onInit/defineQueryAgeCategories.js
+++ /dev/null
@@ -1,31 +0,0 @@
-export default function defineQueryAgeCategories() {
- const queryAgeCategoryInput = this.controls.config.inputs.find(
- input => input.value_col === this.config.age_category_col
- );
- const queryAgeCategoryGroup = this.config.status_groups.find(
- age_category_group => age_category_group.value_col === this.config.age_category_col
- );
- const queryStatusOrder = this.config.status_groups.find(
- status_group => status_group.value_col === this.config.status_col
- ).order;
- const queryAgeCategoryOrder = Array.isArray(queryAgeCategoryGroup.order)
- ? queryAgeCategoryGroup.order.concat(
- d3
- .set(this.raw_data.map(d => d[this.config.age_category_col]))
- .values()
- .filter(value => queryAgeCategoryGroup.order.indexOf(value) < 0)
- .sort()
- )
- : d3
- .set(this.raw_data.map(d => d[this.config.age_category_col]))
- .values()
- .sort((a, b) => {
- const aIndex = queryStatusOrder.indexOf(a);
- const bIndex = queryStatusOrder.indexOf(b);
- const diff = aIndex - bIndex;
-
- return diff ? diff : a < b ? -1 : 1;
- });
- queryAgeCategoryInput.order = queryAgeCategoryOrder;
- queryAgeCategoryGroup.order = queryAgeCategoryOrder;
-}
diff --git a/src/chart/onInit/defineQueryRecencySet.js b/src/chart/onInit/defineQueryRecencySet.js
new file mode 100644
index 0000000..538f9b2
--- /dev/null
+++ b/src/chart/onInit/defineQueryRecencySet.js
@@ -0,0 +1,23 @@
+export default function defineQueryRecencySet() {
+ const queryRecencyInput = this.controls.config.inputs.find(
+ input => input.value_col === 'queryrecency'
+ );
+
+ if (this.raw_data[0].hasOwnProperty(this.config.recency_category_col)) {
+ queryRecencyInput.values = d3
+ .set(this.raw_data.map(d => d.queryrecency))
+ .values()
+ .sort((a, b) => {
+ const anum = parseFloat(a);
+ const bnum = parseFloat(b);
+ const diff = anum - bnum;
+ return diff ? diff : a < b ? -1 : a > b ? 1 : 0;
+ });
+ } else if (this.raw_data[0].hasOwnProperty(this.config.recency_col))
+ queryRecencyInput.values = this.config.recencyRangeCategories;
+ else
+ this.controls.config.inputs.splice(
+ this.controls.config.inputs.findIndex(input => input.value_col === 'queryrecency'),
+ 1
+ );
+}
diff --git a/src/chart/onInit/defineQueryStatusSet.js b/src/chart/onInit/defineQueryStatusSet.js
new file mode 100644
index 0000000..29bfdcd
--- /dev/null
+++ b/src/chart/onInit/defineQueryStatusSet.js
@@ -0,0 +1,23 @@
+export default function defineQueryStatusSet() {
+ const queryStatusInput = this.controls.config.inputs.find(
+ input => input.value_col === this.config.status_col
+ );
+ const queryStatusGroup = this.config.status_groups.find(
+ status_group => status_group.value_col === this.config.status_col
+ );
+ const queryStatusOrder =
+ Array.isArray(queryStatusGroup.order) && queryStatusGroup.order.length
+ ? queryStatusGroup.order.concat(
+ d3
+ .set(this.raw_data.map(d => d[this.config.status_col]))
+ .values()
+ .filter(value => queryStatusGroup.order.indexOf(value) < 0)
+ .sort()
+ )
+ : d3
+ .set(this.raw_data.map(d => d[this.config.status_col]))
+ .values()
+ .sort();
+ queryStatusInput.order = queryStatusOrder;
+ queryStatusGroup.order = queryStatusOrder;
+}
diff --git a/src/chart/onInit/defineQueryStatuses.js b/src/chart/onInit/defineQueryStatuses.js
deleted file mode 100644
index 9755b20..0000000
--- a/src/chart/onInit/defineQueryStatuses.js
+++ /dev/null
@@ -1,22 +0,0 @@
-export default function defineQueryStatuses() {
- const queryStatusInput = this.controls.config.inputs.find(
- input => input.value_col === this.config.status_col
- );
- const queryStatusGroup = this.config.status_groups.find(
- status_group => status_group.value_col === this.config.status_col
- );
- const queryStatusOrder = Array.isArray(queryStatusGroup.order)
- ? queryStatusGroup.order.concat(
- d3
- .set(this.raw_data.map(d => d[this.config.status_col]))
- .values()
- .filter(value => queryStatusGroup.order.indexOf(value) < 0)
- .sort()
- )
- : d3
- .set(this.raw_data.map(d => d[this.config.status_col]))
- .values()
- .sort();
- queryStatusInput.order = queryStatusOrder;
- queryStatusGroup.order = queryStatusOrder;
-}
diff --git a/src/chart/onLayout.js b/src/chart/onLayout.js
index a6b8bef..c331710 100644
--- a/src/chart/onLayout.js
+++ b/src/chart/onLayout.js
@@ -1,17 +1,53 @@
+import classControls from './onLayout/classControls';
+import groupControls from './onLayout/groupControls';
+import addControlTooltips from './onLayout/addControlTooltips';
import updateGroupByOptions from './onLayout/updateGroupByOptions';
+import addGroupByHighlight from './onLayout/addGroupByHighlight';
+import checkInitialStatusGroup from './onLayout/checkInitialStatusGroup';
+import addStatusGroupHighlight from './onLayout/addStatusGroupHighlight';
import customizeMultiSelects from './onLayout/customizeMultiSelects';
+import addSelectAll from './onLayout/addSelectAll';
+import updateFilterEventListeners from './onLayout/updateFilterEventListeners';
+import sortQueryRecencyOptions from './onLayout/sortQueryRecencyOptions';
import setYAxisDomainLength from './onLayout/setYAxisDomainLength';
import addResetButton from './onLayout/addResetButton';
import clearListingOnChange from './onLayout/clearListingOnChange';
-import addListingInstruction from './onLayout/addListingInstruction';
+import addFootnotes from './onLayout/addFootnotes';
export default function onLayout() {
+ //Class controls for unique selection.
+ classControls.call(this);
+
+ //Group controls logically.
+ groupControls.call(this);
+
+ //Add tooltips to controls.
+ addControlTooltips.call(this);
+
//Display group label rather than group column name in Group by control.
updateGroupByOptions.call(this);
+ //Highlight y-axis label when user hovers over Status Group control.
+ addGroupByHighlight.call(this);
+
+ //Check radio button of initial status group.
+ checkInitialStatusGroup.call(this);
+
+ //Highlight legend when user hovers over Status Group control.
+ addStatusGroupHighlight.call(this);
+
//Customize dropdowns with multiple options.
customizeMultiSelects.call(this);
+ //Add select all checkbox to filters.
+ addSelectAll.call(this);
+
+ //Update filter event listeners to toggle select all checkbox on change.
+ updateFilterEventListeners.call(this);
+
+ //Sort query recency categories numerically if possible.
+ sortQueryRecencyOptions.call(this);
+
//Handle y-domain length control
setYAxisDomainLength.call(this);
@@ -21,6 +57,6 @@ export default function onLayout() {
//Clear listing when controls change.
clearListingOnChange.call(this);
- //Add listing instruction.
- addListingInstruction.call(this);
+ //Add chart footnotes.
+ addFootnotes.call(this);
}
diff --git a/src/chart/onLayout/addControlTooltips.js b/src/chart/onLayout/addControlTooltips.js
new file mode 100644
index 0000000..94e0d9d
--- /dev/null
+++ b/src/chart/onLayout/addControlTooltips.js
@@ -0,0 +1,42 @@
+export default function addControlTooltips() {
+ const tooltips = {
+ //other controls
+ 'Group by':
+ 'Controls the variable with which the queries are grouped; each group is plotted along the vertical axis of the chart.',
+ 'Status Group': 'Controls the variable with which the bars are subdivided.',
+ 'Bar Arrangement':
+ 'Controls the layout of the status groups.\n- stacked: status groups are plotted side-by-side horizontally\n- grouped: status groups are plotted side-by-side vertically',
+ 'Show First N Groups': 'Controls the number of groups displayed on the vertical axis.',
+ 'Order Groups Alphabetically?':
+ 'Controls the order of the groups; uncheck to sort groups by magnitude (largest to smallest number of queries) instead of alphabetically.',
+
+ //filters
+ 'Query Age':
+ 'Open queries are broken down into how long they have been open. All other queries are classified by status (answered, closed, cancelled).',
+ 'Query Status':
+ 'Open: site has not responded to the issue\nAnswered: site has responded to issue; DM needs to review\nClosed: issue resolved\nCancelled: query cancelled by DM',
+ 'Query Recency':
+ 'Number of days a query has been open, regardless of its current status (applies only to queries opened in the past 30 days)',
+ Form:
+ 'CRF page abbreviation; hover over the abbreviation in the chart to see its full name.',
+ Site: 'Name or ID of site',
+ 'Marking Group': 'Entity that opened the query',
+ 'Visit/Folder':
+ 'Visit/folder abbreviation; hover over the visit/folder abbreviation in the chart to see the full name.'
+ };
+ this.controls.controlGroups.each(function(d) {
+ const tooltip =
+ tooltips[d.label] ||
+ `This ${d.type} controls ${d.value_col || d.option || d.options.join(', ')}.`;
+ if (tooltips[d.label] === undefined)
+ console.warn(
+ `The control labeled ${
+ d.label
+ } does not have a curated tooltip. Defaulting to ${tooltip}.`
+ );
+ d3
+ .select(this)
+ .selectAll('.wc-control-label')
+ .attr('title', tooltip);
+ });
+}
diff --git a/src/chart/onLayout/addFootnotes.js b/src/chart/onLayout/addFootnotes.js
new file mode 100644
index 0000000..5d2f2f0
--- /dev/null
+++ b/src/chart/onLayout/addFootnotes.js
@@ -0,0 +1,12 @@
+export default function addFootnotes() {
+ this.footnotes = {
+ barClick: this.wrap
+ .append('div')
+ .classed('qo-footnote qo-footnote--bar-click', true)
+ .text('Click one or more bars to view the underlying data in the listing below.'),
+ deselectBars: this.wrap
+ .append('div')
+ .classed('qo-footnote qo-footnote--deselect-bars', true)
+ .text('Click in the white area to deselect all bars.')
+ };
+}
diff --git a/src/chart/onLayout/addGroupByHighlight.js b/src/chart/onLayout/addGroupByHighlight.js
new file mode 100644
index 0000000..f278063
--- /dev/null
+++ b/src/chart/onLayout/addGroupByHighlight.js
@@ -0,0 +1,18 @@
+export default function addGroupByHighlight() {
+ this.controls.otherControls.controlGroups
+ .filter(d => d.label === 'Group by')
+ .on('mouseover', () => {
+ this.svg.selectAll('.y.axis .axis-title').style({
+ 'font-weight': 'bold',
+ 'text-decoration': 'underline',
+ fill: 'red'
+ });
+ })
+ .on('mouseout', () => {
+ this.svg.selectAll('.y.axis .axis-title').style({
+ 'font-weight': 'normal',
+ 'text-decoration': 'none',
+ fill: 'black'
+ });
+ });
+}
diff --git a/src/chart/onLayout/addListingInstruction.js b/src/chart/onLayout/addListingInstruction.js
deleted file mode 100644
index 2df201d..0000000
--- a/src/chart/onLayout/addListingInstruction.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export default function addListingInstruction() {
- this.wrap
- .append('em')
- .attr('id', 'listing-instruction')
- .text('Click a bar to view its underlying data.');
-}
diff --git a/src/chart/onLayout/addResetButton.js b/src/chart/onLayout/addResetButton.js
index 9851e7d..528274a 100644
--- a/src/chart/onLayout/addResetButton.js
+++ b/src/chart/onLayout/addResetButton.js
@@ -1,21 +1,21 @@
import clone from '../../util/clone';
export default function addResetButton() {
- this.controls.wrap
+ this.resetButton = d3
+ .select(this.div)
.insert('button', ':first-child')
- .attr('id', 'reset-chart')
- .style({
- margin: '5px',
- padding: '5px',
- float: 'right'
- })
+ .classed('qo-button qo-button--reset-chart', true)
.text('Reset chart')
.on('click', () => {
- const element = clone(this.div),
- settings = clone(this.initialSettings),
- data = clone(this.raw_data);
+ const element = this.element;
+ const settings = clone(this.initialSettings);
+ const data = clone(this.raw_data);
this.listing.destroy();
this.destroy();
+ d3
+ .select(this.element)
+ .selectAll('*')
+ .remove();
queryOverview(element, settings).init(data);
});
}
diff --git a/src/chart/onLayout/addSelectAll.js b/src/chart/onLayout/addSelectAll.js
new file mode 100644
index 0000000..c105ff1
--- /dev/null
+++ b/src/chart/onLayout/addSelectAll.js
@@ -0,0 +1,33 @@
+export default function addSelectAll() {
+ const context = this;
+
+ this.controls.filters.labels.filter(d => d.multiple).each(function(d) {
+ const label = d3
+ .select(this)
+ .html(`${d.label} `);
+ const checkbox = label
+ .select('input')
+ .datum(d)
+ .attr('title', `Deselect all ${d.label} options`)
+ .property('checked', true)
+ .on('click', function(di) {
+ const checkbox = d3.select(this);
+ const checked = this.checked;
+
+ //Update checkbox tooltip.
+ checkbox.attr(
+ 'title',
+ checked ? `Deselect all ${di.label} options` : `Select all ${di.label} options`
+ );
+
+ //Update filter object.
+ const filter = context.filters.find(filter => filter.col === di.value_col);
+ if (checked) filter.val = filter.choices;
+ else filter.val = [];
+
+ //Redraw.
+ context.draw();
+ });
+ });
+ this.controls.filters.checkboxes = this.controls.filters.labels.selectAll('.qo-select-all');
+}
diff --git a/src/chart/onLayout/addStatusGroupHighlight.js b/src/chart/onLayout/addStatusGroupHighlight.js
new file mode 100644
index 0000000..af2ee40
--- /dev/null
+++ b/src/chart/onLayout/addStatusGroupHighlight.js
@@ -0,0 +1,16 @@
+export default function addStatusGroupHighlight() {
+ this.controls.otherControls.controlGroups
+ .filter(d => d.label === 'Status Group')
+ .on('mouseover', () => {
+ this.legend.select('.legend-title').style({
+ 'text-decoration': 'underline',
+ color: 'red'
+ });
+ })
+ .on('mouseout', () => {
+ this.legend.select('.legend-title').style({
+ 'text-decoration': 'none',
+ color: 'black'
+ });
+ });
+}
diff --git a/src/chart/onLayout/checkInitialStatusGroup.js b/src/chart/onLayout/checkInitialStatusGroup.js
new file mode 100644
index 0000000..cd51d7a
--- /dev/null
+++ b/src/chart/onLayout/checkInitialStatusGroup.js
@@ -0,0 +1,7 @@
+export default function checkInitialStatusGroup() {
+ this.controls.otherControls.controlGroups
+ .filter(d => d.label === 'Status Group')
+ .selectAll('.radio')
+ .selectAll('.changer')
+ .property('checked', d => d === this.config.legend.label);
+}
diff --git a/src/chart/onLayout/classControls.js b/src/chart/onLayout/classControls.js
new file mode 100644
index 0000000..7200541
--- /dev/null
+++ b/src/chart/onLayout/classControls.js
@@ -0,0 +1,11 @@
+export default function classControls() {
+ this.controls.controlGroups = this.controls.wrap
+ .selectAll('.control-group')
+ .attr(
+ 'class',
+ d =>
+ `control-group qo-${d.type} qo-${d.type}--${d.label
+ .toLowerCase()
+ .replace(/[^_a-zA-Z-]/g, '-')}`
+ );
+}
diff --git a/src/chart/onLayout/clearListingOnChange.js b/src/chart/onLayout/clearListingOnChange.js
index fc9d879..9ab2e11 100644
--- a/src/chart/onLayout/clearListingOnChange.js
+++ b/src/chart/onLayout/clearListingOnChange.js
@@ -16,7 +16,6 @@ export default function clearListingOnChange() {
//Reset listing.
context.listing.wrap.selectAll('*').remove();
- context.wrap.select('#listing-instruction').style('display', 'block');
context.listing.init(context.filtered_data);
});
}
diff --git a/src/chart/onLayout/groupControls.js b/src/chart/onLayout/groupControls.js
new file mode 100644
index 0000000..de59ad7
--- /dev/null
+++ b/src/chart/onLayout/groupControls.js
@@ -0,0 +1,50 @@
+export default function groupControls() {
+ const context = this;
+
+ //Group filters.
+ this.controls.filters = {
+ container: this.controls.wrap
+ .insert('div', '.qo-subsetter')
+ .classed('qo-control-grouping qo-control-grouping--filters', true)
+ };
+ this.controls.filters.container
+ .append('div')
+ .classed('qo-control-grouping--label', true)
+ .attr(
+ 'title',
+ 'Filters subset the data underlying the chart and listing.\nHover over filter labels to view more information about them.'
+ )
+ .text('Filters');
+ this.controls.filters.controlGroups = this.controls.wrap.selectAll('.qo-subsetter');
+ this.controls.filters.labels = this.controls.filters.controlGroups.selectAll(
+ '.wc-control-label'
+ );
+ this.controls.filters.selects = this.controls.filters.controlGroups.selectAll('.changer');
+ this.controls.filters.controlGroups.each(function(d) {
+ context.controls.filters.container.node().appendChild(this);
+ });
+
+ //Group other controls.
+ this.controls.otherControls = {
+ container: this.controls.wrap
+ .insert('div', ':first-child')
+ .classed('qo-control-grouping qo-control-grouping--other-controls', true)
+ };
+ this.controls.otherControls.label = this.controls.otherControls.container
+ .append('div')
+ .classed('qo-control-grouping--label', true)
+ .attr(
+ 'title',
+ 'Controls alter the display of the chart.\nHover over control labels to view more information about them.'
+ )
+ .text('Controls');
+ this.controls.otherControls.controlGroups = this.controls.wrap.selectAll(
+ '.control-group:not(.qo-subsetter)'
+ );
+ this.controls.otherControls.labels = this.controls.otherControls.controlGroups.selectAll(
+ '.wc-control-label'
+ );
+ this.controls.otherControls.controlGroups.each(function(d) {
+ context.controls.otherControls.container.node().appendChild(this);
+ });
+}
diff --git a/src/chart/onLayout/sortQueryRecencyOptions.js b/src/chart/onLayout/sortQueryRecencyOptions.js
new file mode 100644
index 0000000..27d12b2
--- /dev/null
+++ b/src/chart/onLayout/sortQueryRecencyOptions.js
@@ -0,0 +1,11 @@
+export default function sortQueryRecencyOptions() {
+ this.controls.filters.selects
+ .filter(d => d.value_col === 'queryrecency')
+ .selectAll('option')
+ .sort((a, b) => {
+ const anum = parseFloat(a);
+ const bnum = parseFloat(b);
+ const diff = anum - bnum;
+ return diff ? diff : a < b ? -1 : a > b ? 1 : 0;
+ });
+}
diff --git a/src/chart/onLayout/tweakMultiSelects.js b/src/chart/onLayout/tweakMultiSelects.js
deleted file mode 100644
index 94c67fc..0000000
--- a/src/chart/onLayout/tweakMultiSelects.js
+++ /dev/null
@@ -1,19 +0,0 @@
-export default function tweakMultiSelects() {
- const context = this;
-
- this.controls.wrap
- .selectAll('.control-group')
- .filter(d => d.type === 'subsetter' && d.multiple)
- .each(function(d) {
- d3
- .select(this)
- .select('select')
- .attr(
- 'size',
- context.filters.find(filter => filter.col === d.value_col).choices.length
- )
- .attr('title', 'Hold the CTRL key to select or deselect a single option.')
- .selectAll('option')
- .property('selected', true);
- });
-}
diff --git a/src/chart/onLayout/updateFilterEventListeners.js b/src/chart/onLayout/updateFilterEventListeners.js
new file mode 100644
index 0000000..83397db
--- /dev/null
+++ b/src/chart/onLayout/updateFilterEventListeners.js
@@ -0,0 +1,12 @@
+import updateSelectAll from './updateFilterEventListeners/updateSelectAll';
+
+export default function updateFilterEventListeners() {
+ const context = this;
+
+ this.controls.filters.selects.on('change', function(d) {
+ const select = d3.select(this);
+ const selectedOptions = select.selectAll('option:checked').data();
+ updateSelectAll.call(context, d, selectedOptions);
+ context.draw();
+ });
+}
diff --git a/src/chart/onLayout/updateFilterEventListeners/updateSelectAll.js b/src/chart/onLayout/updateFilterEventListeners/updateSelectAll.js
new file mode 100644
index 0000000..878a205
--- /dev/null
+++ b/src/chart/onLayout/updateFilterEventListeners/updateSelectAll.js
@@ -0,0 +1,17 @@
+export default function updateSelectAll(d, selectedOptions) {
+ //Update filter object.
+ const filter = this.filters.find(filter => filter.col === d.value_col);
+ filter.val = d.multiple ? selectedOptions : selectedOptions.pop();
+
+ //Update checkbox.
+ if (d.multiple) {
+ const checked = filter.val.length === filter.choices.length;
+ const checkbox = this.controls.filters.checkboxes
+ .filter(di => di.value_col === d.value_col)
+ .attr(
+ 'title',
+ checked ? `Deselect all ${d.label} options` : `Select all ${d.label} options`
+ )
+ .property('checked', checked);
+ }
+}
diff --git a/src/chart/onLayout/updateGroupByOptions.js b/src/chart/onLayout/updateGroupByOptions.js
index 61382d8..8fd9c5c 100644
--- a/src/chart/onLayout/updateGroupByOptions.js
+++ b/src/chart/onLayout/updateGroupByOptions.js
@@ -1,7 +1,7 @@
export default function updateGroupByOptions() {
const context = this;
- const groupByControl = this.controls.wrap
+ this.controls.wrap
.selectAll('.control-group select')
.filter(d => d.label === 'Group by')
.on('change', function() {
diff --git a/src/chart/onPreprocess/updateRangeBand.js b/src/chart/onPreprocess/updateRangeBand.js
index bb03ae0..0624fba 100644
--- a/src/chart/onPreprocess/updateRangeBand.js
+++ b/src/chart/onPreprocess/updateRangeBand.js
@@ -1,7 +1,7 @@
export default function updateRangeBand() {
if (this.config.marks[0].arrange === 'stacked') {
- this.config.range_band = 15;
+ this.config.range_band = this.initialSettings.range_band;
} else {
- this.config.range_band = 15 * this.config.color_dom.length;
+ this.config.range_band = this.initialSettings.range_band * this.config.color_dom.length;
}
}
diff --git a/src/chart/onPreprocess/updateStratification.js b/src/chart/onPreprocess/updateStratification.js
index f7b818f..05c34b1 100644
--- a/src/chart/onPreprocess/updateStratification.js
+++ b/src/chart/onPreprocess/updateStratification.js
@@ -1,16 +1,21 @@
-import { set } from 'd3';
-
export default function updateStratification() {
- const stratification = this.config.status_groups.find(
- status_group => status_group.value_col === this.config.color_by
+ const statusGroup = this.controls.wrap
+ .selectAll('.qo-radio--status-group')
+ .selectAll('.radio')
+ .filter(function() {
+ const label = d3.select(this);
+ const radio = label.select('.changer');
+ return radio.property('checked');
+ })
+ .text();
+ this.config.status_group = this.config.status_groups.find(
+ status_group => status_group.label === statusGroup
);
- this.config.color_dom =
- stratification.order ||
- set(this.raw_data.map(d => d[this.config.color_by]))
- .values()
- .sort();
- this.config.colors = stratification.colors;
- this.config.legend.label = stratification.label;
+ this.config.marks[0].split = this.config.status_group.value_col;
+ this.config.color_by = this.config.status_group.value_col;
+ this.config.color_dom = this.config.status_group.order;
+ this.config.colors = this.config.status_group.colors;
+ this.config.legend.label = this.config.status_group.label;
this.config.legend.order = this.config.color_dom.slice();
this.config.marks[0].tooltip = `[${this.config.color_by}] - $x queries`;
}
diff --git a/src/chart/onResize.js b/src/chart/onResize.js
index 6820139..1a2f752 100644
--- a/src/chart/onResize.js
+++ b/src/chart/onResize.js
@@ -5,6 +5,8 @@ import annotateYAxisInfo from './onResize/annotateYAxisInfo';
import hideBars from './onResize/hideBars';
import annotateNumberOfQueries from './onResize/annotateNumberOfQueries';
import addBarClick from './onResize/addBarClick';
+import addBarDeselection from './onResize/addBarDeselection';
+import addNoDataIndicator from './onResize/addNoDataIndicator';
export default function onResize() {
//Add filter functionality to legend.
@@ -27,4 +29,10 @@ export default function onResize() {
//Add bar click-ability.
addBarClick.call(this);
+
+ //Add bar deselection.
+ addBarDeselection.call(this);
+
+ //Add informational text to the chart canvas when filters are in conflict.
+ addNoDataIndicator.call(this);
}
diff --git a/src/chart/onResize/addBarClick.js b/src/chart/onResize/addBarClick.js
index 196eb7c..66ebd1b 100644
--- a/src/chart/onResize/addBarClick.js
+++ b/src/chart/onResize/addBarClick.js
@@ -1,70 +1,52 @@
-import flatMap from '../../util/flatMap';
+import mouseoverStyle from './addBarClick/mouseoverStyle';
+import mouseoverAttrib from './addBarClick/mouseoverAttrib';
+import mouseoutStyle from './addBarClick/mouseoutStyle';
+import mouseoutAttrib from './addBarClick/mouseoutAttrib';
+import initListing from './addBarClick/initListing';
export default function addBarClick() {
const context = this;
- const barGroups = this.svg.selectAll('.bar-group');
- const bars = this.svg.selectAll('.bar');
// will subtract and add to bar to offset increase in stroke-width and prevent bars
// from overlapping as much when neighbors are both selected.
- const mouseoverAttrib = {
- width: function(d) {
- return this.getBBox().width - 2.5;
- },
- x: function(d) {
- return this.getBBox().x + 2.5;
- }
- };
- const mouseoverStyle = {
- 'stroke-width': '5px',
- fill: 'black'
- };
- const mouseoutAttrib = {
- width: function(d) {
- return this.getBBox().width + 2.5;
- },
- x: function(d) {
- return this.getBBox().x - 2.5;
- }
- };
- const mouseoutStyle = {
- 'stroke-width': '1px',
- fill: d => context.colorScale(d.key)
- };
- bars
+ this.bars = this.svg
+ .selectAll('.bar')
.style('cursor', 'pointer')
.on('mouseover', function() {
- if (!d3.select(this).classed('selected')) d3.select(this).style(mouseoverStyle);
- if (!d3.select(this).classed('selected')) d3.select(this).attr(mouseoverAttrib);
+ const bar = d3.select(this);
+ const selected = bar.classed('selected');
+
+ //Apply highlight attributes and styles to bar.
+ mouseoverStyle.call(context, bar, selected);
+ mouseoverAttrib.call(context, bar, selected);
+
//moveToFront causes an issue preventing onMouseout from firing in Internet Explorer so only call it in other browsers.
if (!/trident/i.test(navigator.userAgent)) d3.select(this).moveToFront();
})
.on('mouseout', function() {
- if (!d3.select(this).classed('selected')) d3.select(this).style(mouseoutStyle);
- if (!d3.select(this).classed('selected')) d3.select(this).attr(mouseoutAttrib);
- bars
- .filter(function() {
- return d3.select(this).classed('selected');
- })
- .moveToFront();
+ const bar = d3.select(this);
+ const selected = bar.classed('selected');
+
+ //Apply default attributes and styles to bar.
+ mouseoutStyle.call(context, bar, selected);
+ mouseoutAttrib.call(context, bar, selected);
+
+ //moveToFront causes an issue preventing onMouseout from firing in Internet Explorer so only call it in other browsers.
+ if (!/trident/i.test(navigator.userAgent))
+ context.bars
+ .filter(function() {
+ return d3.select(this).classed('selected');
+ })
+ .moveToFront();
})
.on('click', function(d) {
- // this doesn't need a style because mouseout isn't applied when the bar is selected
- d3.select(this).classed('selected', d3.select(this).classed('selected') ? false : true);
- context.listing.wrap.selectAll('*').remove();
- // feed listing data for all selected bars
- context.listing.init(
- d3
- .selectAll('rect.selected')
- .data()
- .flatMap(d => d.values.raw)
- );
- context.wrap.select('#listing-instruction').style('display', 'none'); // remove bar instructions
- // display filtered data if no bars are selected
- if (d3.selectAll('rect.selected')[0].length === 0) {
- context.listing.wrap.selectAll('*').remove();
- context.wrap.select('#listing-instruction').style('display', 'block');
- context.listing.init(context.filtered_data);
- }
+ const bar = d3.select(this);
+ const selected = bar.classed('selected');
+
+ //Update selected class of clicked bar.
+ bar.classed('selected', !selected);
+
+ //Re-initialize listing.
+ initListing.call(context);
});
}
diff --git a/src/chart/onResize/addBarClick/initListing.js b/src/chart/onResize/addBarClick/initListing.js
new file mode 100644
index 0000000..1609e2f
--- /dev/null
+++ b/src/chart/onResize/addBarClick/initListing.js
@@ -0,0 +1,14 @@
+export default function initListing() {
+ //Clear listing container.
+ this.listing.wrap.selectAll('*').remove();
+
+ //Capture data from selected bars.
+ const selectedData = d3
+ .selectAll('rect.selected')
+ .data()
+ .flatMap(d => d.values.raw);
+
+ //Feed data from selected bars into listing.
+ if (selectedData.length > 0) this.listing.init(selectedData);
+ else this.listing.init(this.filtered_data);
+}
diff --git a/src/chart/onResize/addBarClick/mouseoutAttrib.js b/src/chart/onResize/addBarClick/mouseoutAttrib.js
new file mode 100644
index 0000000..8d5ec89
--- /dev/null
+++ b/src/chart/onResize/addBarClick/mouseoutAttrib.js
@@ -0,0 +1,11 @@
+export default function mouseoutAttrib(bar, selected, clear = false) {
+ if (!(selected || clear) || (selected && clear))
+ bar.attr({
+ width: function(d) {
+ return this.getBBox().width + 2.5;
+ },
+ x: function(d) {
+ return this.getBBox().x - 2.5;
+ }
+ });
+}
diff --git a/src/chart/onResize/addBarClick/mouseoutStyle.js b/src/chart/onResize/addBarClick/mouseoutStyle.js
new file mode 100644
index 0000000..909d9e8
--- /dev/null
+++ b/src/chart/onResize/addBarClick/mouseoutStyle.js
@@ -0,0 +1,7 @@
+export default function mouseoutStyle(bar, selected, clear = false) {
+ if (!(selected || clear) || (selected && clear))
+ bar.style({
+ 'stroke-width': '1px',
+ fill: d => this.colorScale(d.key)
+ });
+}
diff --git a/src/chart/onResize/addBarClick/mouseoverAttrib.js b/src/chart/onResize/addBarClick/mouseoverAttrib.js
new file mode 100644
index 0000000..cbc86ef
--- /dev/null
+++ b/src/chart/onResize/addBarClick/mouseoverAttrib.js
@@ -0,0 +1,11 @@
+export default function mouseoverAttrib(bar, selected) {
+ if (!selected)
+ bar.attr({
+ width: function(d) {
+ return this.getBBox().width - 2.5;
+ },
+ x: function(d) {
+ return this.getBBox().x + 2.5;
+ }
+ });
+}
diff --git a/src/chart/onResize/addBarClick/mouseoverStyle.js b/src/chart/onResize/addBarClick/mouseoverStyle.js
new file mode 100644
index 0000000..58ccdda
--- /dev/null
+++ b/src/chart/onResize/addBarClick/mouseoverStyle.js
@@ -0,0 +1,7 @@
+export default function mouseoverStyle(bar, selected) {
+ if (!selected)
+ bar.style({
+ 'stroke-width': '5px',
+ fill: 'black'
+ });
+}
diff --git a/src/chart/onResize/addBarDeselection.js b/src/chart/onResize/addBarDeselection.js
new file mode 100644
index 0000000..9d5a19d
--- /dev/null
+++ b/src/chart/onResize/addBarDeselection.js
@@ -0,0 +1,18 @@
+import mouseoutStyle from './addBarClick/mouseoutStyle';
+import mouseoutAttrib from './addBarClick/mouseoutAttrib';
+import initListing from './addBarClick/initListing';
+
+export default function addBarDeselection() {
+ const context = this;
+
+ this.overlay.on('click', () => {
+ this.bars.each(function(d) {
+ const bar = d3.select(this);
+ const selected = bar.classed('selected');
+ mouseoutStyle.call(context, bar, selected, true);
+ mouseoutAttrib.call(context, bar, selected, true);
+ bar.classed('selected', false);
+ });
+ initListing.call(this);
+ });
+}
diff --git a/src/chart/onResize/addNoDataIndicator.js b/src/chart/onResize/addNoDataIndicator.js
new file mode 100644
index 0000000..141df5e
--- /dev/null
+++ b/src/chart/onResize/addNoDataIndicator.js
@@ -0,0 +1,14 @@
+export default function addNoDataIndicator() {
+ this.svg.select('.qo-no-data').remove();
+
+ if (this.filtered_data.length === 0)
+ this.svg
+ .append('text')
+ .classed('qo-no-data', true)
+ .attr({
+ x: this.plot_width / 2,
+ y: this.plot_height / 2,
+ 'text-anchor': 'middle'
+ })
+ .text('No queries selected. Verify that no filter selections are in conflict.');
+}
diff --git a/src/chart/onResize/addYAxisTickClick.js b/src/chart/onResize/addYAxisTickClick.js
index 61a2fcb..de40755 100644
--- a/src/chart/onResize/addYAxisTickClick.js
+++ b/src/chart/onResize/addYAxisTickClick.js
@@ -1,3 +1,5 @@
+import updateSelectAll from '../onLayout/updateFilterEventListeners/updateSelectAll';
+
export default function addYAxisTickClick() {
if (this.config.y.column === this.config.form_col) {
const yLabels = this.svg
@@ -5,27 +7,84 @@ export default function addYAxisTickClick() {
.style('fill', 'blue')
.style('text-decoration', 'underline');
yLabels.style('cursor', 'pointer').on('click', yLabel => {
- this.controls.wrap
- .selectAll('.control-group')
- .filter(d => d.label === 'Group by')
- .selectAll('option')
- .property('selected', d => {
- return d === 'Form: Field';
+ //Update Group by control.
+ const groupByControl = this.controls.otherControls.controlGroups.filter(
+ d => d.label === 'Group by'
+ );
+ groupByControl
+ .select('.wc-control-label')
+ .style({
+ 'font-weight': 'bold',
+ 'text-decoration': 'underline',
+ color: 'red'
+ })
+ .transition()
+ .delay(3000)
+ .style({
+ 'font-weight': 'normal',
+ 'text-decoration': 'none',
+ color: 'black'
});
+ groupByControl.selectAll('option').property('selected', d => {
+ return d === 'Form: Field';
+ });
+
+ //Update chart settings.
this.config.y.column = 'Form: Field';
this.config.y.label = 'Form: Field';
this.config.marks[0].per[0] = 'Form: Field';
- this.controls.wrap
- .selectAll('.control-group')
- .filter(d => d.label === 'Form')
- .selectAll('option')
- .property('selected', d => d === yLabel);
- this.filters.filter(filter => filter.col === this.config.form_col)[0].val = yLabel;
- this.draw(this.filtered_data.filter(d => d[this.config.form_col] === yLabel));
+ //Update Form filter.
+ const formFilter = this.controls.filters.controlGroups.filter(d => d.label === 'Form');
+ formFilter
+ .select('.wc-control-label')
+ .style({
+ 'font-weight': 'bold',
+ 'text-decoration': 'underline',
+ color: 'red'
+ })
+ .transition()
+ .delay(3000)
+ .style({
+ 'font-weight': 'normal',
+ 'text-decoration': 'none',
+ color: 'black'
+ });
+ formFilter.selectAll('option').property('selected', d => d === yLabel);
+
+ //Update Form filter object in `chart.filters`.
+ const filter = this.filters.find(filter => filter.col === this.config.form_col);
+ filter.val = yLabel;
+ updateSelectAll.call(
+ this,
+ this.controls.filters.controlGroups
+ .filter(d => d.value_col === this.config.form_col)
+ .datum(),
+ [yLabel]
+ );
+
+ //Redraw chart and listing.
+
+ //Update Group by control.
+ this.draw();
this.listing.wrap.selectAll('*').remove();
- this.wrap.select('listing-instruction').style('display', 'block');
this.listing.init(this.filtered_data);
+
+ //Highlight y-axis label.
+ this.svg
+ .select('.y.axis .axis-title')
+ .style({
+ 'font-weight': 'bold',
+ 'text-decoration': 'underline',
+ fill: 'red'
+ })
+ .transition()
+ .delay(3000)
+ .style({
+ 'font-weight': 'normal',
+ 'text-decoration': 'none',
+ fill: 'black'
+ });
});
}
}
diff --git a/src/chart/onResize/legendFilter.js b/src/chart/onResize/legendFilter.js
index 88c8bb3..c59bfc8 100644
--- a/src/chart/onResize/legendFilter.js
+++ b/src/chart/onResize/legendFilter.js
@@ -1,16 +1,20 @@
+import updateSelectAll from '../onLayout/updateFilterEventListeners/updateSelectAll';
+
+//TODO: modularize/refactor
export default function legendFilter() {
const context = this;
+ //Alter layout of legend.
+ const legend = this.legend;
+ legend.style('margin-left', `${this.margin.left}px`);
+ const legendTitle = legend.select('.legend-title');
+ legendTitle.attr('title', 'Add and remove queries by clicking the legend items to the left.');
+ legend.node().appendChild(legendTitle.node());
+
//Filter data by clicking on legend.
const statusFilter = this.filters.find(filter => filter.col === this.config.color_by);
const legendItems = this.wrap
.selectAll('.legend-item')
- .style({
- cursor: 'pointer',
- 'border-radius': '4px',
- padding: '5px',
- 'padding-left': '8px'
- })
.classed(
'selected',
d => statusFilter.val === 'All' || statusFilter.val.indexOf(d.label) > -1
@@ -22,14 +26,15 @@ export default function legendFilter() {
? 'lightgray'
: 'white'
);
- const statusOptions = this.controls.wrap
+ const statusControlGroup = this.controls.wrap
.selectAll('.control-group')
- .filter(d => d.value_col === context.config.marks[0].split)
- .selectAll('.changer option'); // status filter options
+ .filter(d => d.value_col === context.config.marks[0].split);
+ const statusOptions = statusControlGroup.selectAll('.changer option'); // status filter options
legendItems.selectAll('.legend-mark-text').remove(); // don't need 'em
+ legendItems.selectAll('.legend-color-block').attr('width', '8px');
legendItems.on('click', function(d) {
- const legendItem = d3.select(this), // clicked legend item
- selected = !legendItem.classed('selected'); // selected boolean
+ const legendItem = d3.select(this); // clicked legend item
+ const selected = !legendItem.classed('selected'); // selected boolean
legendItem.classed('selected', selected); // toggle selected class
const selectedLegendItems = legendItems
.filter(function() {
@@ -44,6 +49,7 @@ export default function legendFilter() {
.property('selected', false)
.filter(d => selectedLegendItems.indexOf(d) > -1)
.property('selected', true); // set selected property of status options corresponding to selected statuses to true
+ updateSelectAll.call(context, statusControlGroup.datum(), selectedLegendItems);
const filtered_data = context.raw_data.filter(d => {
let filtered = selectedLegendItems.indexOf(d[context.config.marks[0].split]) === -1;
@@ -74,9 +80,8 @@ export default function legendFilter() {
});
context.draw();
- //Remove listing and display listing instruction.
+ //Remove listing and display bar click footnote.
context.listing.wrap.selectAll('*').remove();
- context.wrap.select('#listing-instruction').style('display', 'block');
context.listing.init(filtered_data);
});
}
diff --git a/src/configuration/chartSettings.js b/src/configuration/chartSettings.js
new file mode 100644
index 0000000..f2becb9
--- /dev/null
+++ b/src/configuration/chartSettings.js
@@ -0,0 +1,36 @@
+export default function chartSettings() {
+ return {
+ x: {
+ label: '# of Queries',
+ column: null,
+ behavior: 'flex'
+ },
+ y: {
+ type: 'ordinal',
+ column: null, // set in syncSettings()
+ label: 'Form',
+ sort: null // set in syncSettings()
+ },
+ marks: [
+ {
+ type: 'bar',
+ per: [null], // set in syncSettings()
+ split: null, // set in syncSettings()
+ arrange: null, // set in syncSettings()
+ summarizeX: 'count',
+ tooltip: null // set in syncSettings()
+ }
+ ],
+ color_by: null, // set in syncSettings()
+ color_dom: null, // set in syncSettings()
+ legend: {
+ location: 'top',
+ label: null, // set in syncSettings()
+ order: null // set in syncSettings()
+ },
+ margin: {
+ right: '50' // room for count annotation
+ },
+ range_band: 25
+ };
+}
diff --git a/src/configuration/controlInputs.js b/src/configuration/controlInputs.js
index 04af664..118b050 100644
--- a/src/configuration/controlInputs.js
+++ b/src/configuration/controlInputs.js
@@ -3,16 +3,14 @@ export default [
type: 'dropdown',
option: 'y.label',
label: 'Group by',
- description: 'variable toggle',
start: null, // set in syncControlInputs()
values: null, // set in syncControlInputs()
require: true
},
{
- type: 'dropdown',
+ type: 'radio',
label: 'Status Group',
- description: 'stratification',
- options: ['marks.0.split', 'color_by'], // will want to change tooltip too
+ option: 'color_by_col',
start: null, // set in syncControlInputs()
values: null, // set in syncControlInputs()
require: true
@@ -32,6 +30,6 @@ export default [
{
type: 'checkbox',
option: 'alphabetize',
- label: 'Alphabetical?'
+ label: 'Order Groups Alphabetically?'
}
];
diff --git a/src/configuration/functions/arrayOfVariablesCheck.js b/src/configuration/functions/arrayOfVariablesCheck.js
deleted file mode 100644
index eb33038..0000000
--- a/src/configuration/functions/arrayOfVariablesCheck.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import { merge } from 'd3';
-
-export default function arrayOfVariablesCheck(defaultVariables, userDefinedVariables) {
- const validSetting =
- userDefinedVariables instanceof Array && userDefinedVariables.length
- ? merge([
- defaultVariables,
- userDefinedVariables.filter(
- item =>
- !(item instanceof Object && item.hasOwnProperty('value_col') === false) &&
- defaultVariables.map(d => d.value_col).indexOf(item.value_col || item) ===
- -1
- )
- ]).map(item => {
- const itemObject = {};
-
- itemObject.value_col = item instanceof Object ? item.value_col : item;
- itemObject.label =
- item instanceof Object
- ? item.label || itemObject.value_col
- : itemObject.value_col;
-
- return itemObject;
- })
- : defaultVariables;
-
- return validSetting;
-}
diff --git a/src/configuration/index.js b/src/configuration/index.js
index eeeb641..0e4733c 100644
--- a/src/configuration/index.js
+++ b/src/configuration/index.js
@@ -1,13 +1,15 @@
import rendererSettings from './rendererSettings';
-import webchartsSettings from './webchartsSettings';
+import chartSettings from './chartSettings';
+import listingSettings from './listingSettings';
import syncSettings from './syncSettings';
import controlInputs from './controlInputs';
import syncControlInputs from './syncControlInputs';
export default {
rendererSettings,
- webchartsSettings,
- settings: Object.assign({}, rendererSettings, webchartsSettings),
+ chartSettings,
+ listingSettings,
+ settings: Object.assign({}, rendererSettings(), chartSettings(), listingSettings()),
syncSettings,
controlInputs,
syncControlInputs
diff --git a/src/configuration/listingSettings.js b/src/configuration/listingSettings.js
new file mode 100644
index 0000000..3cb3df9
--- /dev/null
+++ b/src/configuration/listingSettings.js
@@ -0,0 +1,6 @@
+export default function listingSettings() {
+ return {
+ nRowsPerPage: 25,
+ exportable: true
+ };
+}
diff --git a/src/configuration/rendererSettings.js b/src/configuration/rendererSettings.js
index 37ad869..275b3d7 100644
--- a/src/configuration/rendererSettings.js
+++ b/src/configuration/rendererSettings.js
@@ -1,36 +1,51 @@
-export default {
- //query variables
- form_col: 'formoid',
- formDescription_col: 'ecrfpagename',
- field_col: 'fieldname',
- fieldDescription_col: 'fieldname', //there is not a dscriptive column in the test data prescribed by heather
- marking_group_col: 'markinggroup',
- visit_col: 'folderoid',
+export default function rendererSettings() {
+ return {
+ //query variables
+ form_col: 'formoid',
+ formDescription_col: 'ecrfpagename',
+ field_col: 'fieldname',
+ fieldDescription_col: null,
+ site_col: 'sitename',
+ marking_group_col: 'markinggroup',
+ visit_col: 'folderoid',
+ color_by_col: 'queryage', // options: [ 'queryage' , 'querystatus' ] or any of status_groups[].value_col
- //query open time settings
- open_col: 'open_time',
- open_category_col: 'Query Open Time Category',
- open_category_order: ['0-7 days', '8-14 days', '15-30 days', '>30 days'],
+ //query age
+ age_statuses: ['Open'],
+ age_col: 'qdays',
+ age_cutoffs: [14, 28, 56, 112],
+ age_range_colors: [
+ '#ffffcc',
+ '#ffeda0',
+ '#fed976',
+ '#feb24c',
+ '#fd8d3c',
+ '#fc4e2a',
+ '#e31a1c',
+ '#bd0026',
+ '#800026'
+ ],
- //query age settings
- age_col: 'qdays',
- age_category_col: 'Query Age Category',
- age_category_order: null,
- age_category_colors: ['#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#800026', '#1f78b4', 'gray'],
+ //query status
+ status_col: 'querystatus',
+ status_order: ['Open', 'Answered', 'Closed', 'Cancelled'],
+ status_colors: ['#fd8d3c', '#4daf4a', '#377eb8', '#999999'],
- //query status settings
- status_col: 'querystatus',
- status_order: ['Open', 'Answered', 'Closed', 'Cancelled'],
- status_colors: ['#fb9a99', '#fdbf6f', '#1f78b4', 'gray'],
+ //query recency
+ recency_category_col: 'open_time',
+ recency_col: 'odays',
+ recency_cutoffs: [7, 14, 30],
- groups: null,
- status_groups: null,
- site_col: 'sitename',
- filters: null,
- details: null,
- dropdown_size: 6,
- cutoff: 'All',
- alphabetize: true,
- exportable: true,
- nRowsPerPage: 10
-};
+ //miscellany
+ groups: null,
+ status_groups: null,
+ filters: null,
+ dropdown_size: 6,
+ details: null,
+ bar_arrangement: 'stacked',
+ cutoff: 'All',
+ alphabetize: true,
+ truncate_listing_cells: true,
+ truncation_cutoff: 100
+ };
+}
diff --git a/src/configuration/syncControlInputs.js b/src/configuration/syncControlInputs.js
index a7bca1a..2e92273 100644
--- a/src/configuration/syncControlInputs.js
+++ b/src/configuration/syncControlInputs.js
@@ -1,35 +1,14 @@
-import clone from '../util/clone';
+import syncGroupBy from './syncControlInputs/syncGroupBy';
+import syncStatusGroup from './syncControlInputs/syncStatusGroup';
+import syncFilters from './syncControlInputs/syncFilters';
+import syncShowFirstNGroups from './syncControlInputs/syncShowFirstNGroups';
export default function syncControlInputs(controlInputs, settings) {
- const syncedControlInputs = clone(controlInputs);
-
- //Group by
- const groupByControl = syncedControlInputs.find(
- controlInput => controlInput.label === 'Group by'
- );
- groupByControl.values = settings.groups.map(group => group.label);
-
- //Status Group
- const statusGroupControl = syncedControlInputs.find(
- controlInput => controlInput.label === 'Status Group'
- );
- statusGroupControl.values = settings.status_groups.map(status_group => status_group.value_col);
-
- //filters
- settings.filters.forEach((filter, i) => {
- filter.type = 'subsetter';
- filter.description = 'filter';
- syncedControlInputs.splice(2 + i, 0, filter);
- });
-
- //Show First N Groups
- const nGroupsControl = syncedControlInputs.find(
- controlInput => controlInput.label === 'Show First N Groups'
- );
- if (nGroupsControl.values.indexOf(settings.cutoff.toString()) === -1) {
- nGroupsControl.values.push(settings.cutoff.toString());
- nGroupsControl.values.sort((a, b) => (a === 'All' ? 1 : b === 'All' ? -1 : +a - +b));
- } else settings.cutoff = settings.cutoff.toString() || nGroupsControl.values[0];
+ const syncedControlInputs = settings.clone(controlInputs);
+ syncGroupBy(syncedControlInputs, settings);
+ syncStatusGroup(syncedControlInputs, settings);
+ syncFilters(syncedControlInputs, settings);
+ syncShowFirstNGroups(syncedControlInputs, settings);
return syncedControlInputs;
}
diff --git a/src/configuration/syncControlInputs/syncFilters.js b/src/configuration/syncControlInputs/syncFilters.js
new file mode 100644
index 0000000..ae02e3b
--- /dev/null
+++ b/src/configuration/syncControlInputs/syncFilters.js
@@ -0,0 +1,6 @@
+export default function syncFilters(controlInputs, settings) {
+ settings.filters.forEach((filter, i) => {
+ filter.type = 'subsetter';
+ controlInputs.splice(2 + i, 0, filter);
+ });
+}
diff --git a/src/configuration/syncControlInputs/syncGroupBy.js b/src/configuration/syncControlInputs/syncGroupBy.js
new file mode 100644
index 0000000..44b74a5
--- /dev/null
+++ b/src/configuration/syncControlInputs/syncGroupBy.js
@@ -0,0 +1,4 @@
+export default function syncGroupBy(controlInputs, settings) {
+ const groupByControl = controlInputs.find(controlInput => controlInput.label === 'Group by');
+ groupByControl.values = settings.groups.map(group => group.label);
+}
diff --git a/src/configuration/syncControlInputs/syncShowFirstNGroups.js b/src/configuration/syncControlInputs/syncShowFirstNGroups.js
new file mode 100644
index 0000000..4066bd7
--- /dev/null
+++ b/src/configuration/syncControlInputs/syncShowFirstNGroups.js
@@ -0,0 +1,10 @@
+export default function syncShowFirstNGroups(controlInputs, settings) {
+ const nGroupsControl = controlInputs.find(
+ controlInput => controlInput.label === 'Show First N Groups'
+ );
+ if (nGroupsControl.values.indexOf(settings.cutoff.toString()) === -1) {
+ settings.cutoff = settings.cutoff.toString();
+ nGroupsControl.values.push(settings.cutoff.toString());
+ nGroupsControl.values.sort((a, b) => (a === 'All' ? 1 : b === 'All' ? -1 : +a - +b));
+ } else settings.cutoff = settings.cutoff.toString() || nGroupsControl.values[0];
+}
diff --git a/src/configuration/syncControlInputs/syncStatusGroup.js b/src/configuration/syncControlInputs/syncStatusGroup.js
new file mode 100644
index 0000000..5ed0ce0
--- /dev/null
+++ b/src/configuration/syncControlInputs/syncStatusGroup.js
@@ -0,0 +1,7 @@
+export default function syncStatusGroup(controlInputs, settings) {
+ const statusGroupControl = controlInputs.find(
+ controlInput => controlInput.label === 'Status Group'
+ );
+ statusGroupControl.start = settings.color_by;
+ statusGroupControl.values = settings.status_groups.map(status_group => status_group.label);
+}
diff --git a/src/configuration/syncSettings.js b/src/configuration/syncSettings.js
index 62091d3..0b2b4ae 100644
--- a/src/configuration/syncSettings.js
+++ b/src/configuration/syncSettings.js
@@ -1,86 +1,20 @@
import clone from '../util/clone';
-import arrayOfVariablesCheck from './functions/arrayOfVariablesCheck';
+import arrayOfVariablesCheck from './syncSettings/arrayOfVariablesCheck';
+import syncGroups from './syncSettings/syncGroups';
+import syncStatusGroups from './syncSettings/syncStatusGroups';
+import syncWebchartsSettings from './syncSettings/syncWebchartsSettings';
+import syncFilters from './syncSettings/syncFilters';
+import syncCutoff from './syncSettings/syncCutoff';
+import syncDetails from './syncSettings/syncCutoff';
export default function syncSettings(settings) {
- const syncedSettings = clone(settings);
+ const syncedSettings = Object.assign({}, clone(settings), { clone, arrayOfVariablesCheck });
+ syncGroups(syncedSettings);
+ syncStatusGroups(syncedSettings);
+ syncWebchartsSettings(syncedSettings);
+ syncFilters(syncedSettings);
+ syncCutoff(syncedSettings);
+ syncDetails(syncedSettings);
- //groups
- const defaultGroups = [
- { value_col: syncedSettings.form_col, label: 'Form' },
- { value_col: 'Form: Field', label: 'Form: Field' },
- { value_col: syncedSettings.site_col, label: 'Site' },
- { value_col: syncedSettings.marking_group_col, label: 'Marking Group' },
- { value_col: syncedSettings.visit_col, label: 'Visit/Folder' }
- ];
- syncedSettings.groups = arrayOfVariablesCheck(defaultGroups, settings.groups);
-
- //status_groups
- const defaultStatusGroups = [
- {
- value_col: syncedSettings.age_category_col,
- label: 'Query Age Category',
- order: syncedSettings.age_category_order,
- colors: syncedSettings.age_category_colors,
- derive_source: syncedSettings.age_col
- },
- {
- value_col: syncedSettings.status_col,
- label: 'Query Status',
- order: syncedSettings.status_order,
- colors: syncedSettings.status_colors
- }
- ];
- syncedSettings.status_groups = arrayOfVariablesCheck(
- defaultStatusGroups,
- settings.status_groups
- );
-
- //y-axis
- syncedSettings.y.column = syncedSettings.form_col;
-
- //stratification
- syncedSettings.color_by = syncedSettings.status_groups[0].value_col;
- syncedSettings.color_dom = syncedSettings.status_groups[0].order
- ? syncedSettings.status_groups[0].order.slice()
- : null;
- syncedSettings.colors = syncedSettings.status_groups[0].colors;
- syncedSettings.legend.label = syncedSettings.status_groups[0].label;
- syncedSettings.legend.order = syncedSettings.status_groups[0].order
- ? syncedSettings.status_groups[0].order.slice()
- : null;
-
- //mark settings
- syncedSettings.marks[0].per[0] = syncedSettings.form_col;
- syncedSettings.marks[0].split = syncedSettings.color_by;
- syncedSettings.marks[0].tooltip = `[${syncedSettings.color_by}] - $x queries`;
-
- //filters
- const defaultFilters = [
- {
- value_col: syncedSettings.open_category_col,
- derive_source: syncedSettings.open_col,
- label: 'Query Open Time',
- multiple: true,
- order: syncedSettings.open_category_order
- },
- { value_col: syncedSettings.form_col, label: 'Form', multiple: true },
- { value_col: syncedSettings.site_col, label: 'Site', multiple: true },
- { value_col: syncedSettings.marking_group_col, label: 'Marking Group', multiple: true },
- { value_col: syncedSettings.visit_col, label: 'Visit/Folder', multiple: true }
- ];
-
- syncedSettings.status_groups.reverse().forEach(status_group => {
- status_group.multiple = true;
- defaultFilters.unshift(clone(status_group));
- });
- syncedSettings.filters = arrayOfVariablesCheck(defaultFilters, settings.filters);
-
- //cutoff
- if (!(+syncedSettings.cutoff > 0 || syncedSettings.cutoff === 'All'))
- syncedSettings.cutoff = 10;
-
- //details
- syncedSettings.details = arrayOfVariablesCheck([], settings.details);
- if (syncedSettings.details.length === 0) delete syncedSettings.details;
return syncedSettings;
}
diff --git a/src/configuration/syncSettings/arrayOfVariablesCheck.js b/src/configuration/syncSettings/arrayOfVariablesCheck.js
new file mode 100644
index 0000000..9484485
--- /dev/null
+++ b/src/configuration/syncSettings/arrayOfVariablesCheck.js
@@ -0,0 +1,32 @@
+export default function arrayOfVariablesCheck(defaultVariables, userDefinedVariables) {
+ const validSetting =
+ userDefinedVariables instanceof Array && userDefinedVariables.length
+ ? d3
+ .merge([
+ defaultVariables,
+ userDefinedVariables.filter(
+ item =>
+ !(
+ item instanceof Object &&
+ item.hasOwnProperty('value_col') === false
+ ) &&
+ defaultVariables
+ .map(d => d.value_col)
+ .indexOf(item.value_col || item) === -1
+ )
+ ])
+ .map(item => {
+ const itemObject = item instanceof Object ? Object.assign({}, item) : {};
+
+ itemObject.value_col = item instanceof Object ? item.value_col : item;
+ itemObject.label =
+ item instanceof Object
+ ? item.label || itemObject.value_col
+ : itemObject.value_col;
+
+ return itemObject;
+ })
+ : defaultVariables;
+
+ return validSetting;
+}
diff --git a/src/configuration/syncSettings/syncCutoff.js b/src/configuration/syncSettings/syncCutoff.js
new file mode 100644
index 0000000..8fbc677
--- /dev/null
+++ b/src/configuration/syncSettings/syncCutoff.js
@@ -0,0 +1,3 @@
+export default function syncCutoff(settings) {
+ if (!(+settings.cutoff > 0 || settings.cutoff === 'All')) settings.cutoff = 10;
+}
diff --git a/src/configuration/syncSettings/syncDetails.js b/src/configuration/syncSettings/syncDetails.js
new file mode 100644
index 0000000..1c7a1bb
--- /dev/null
+++ b/src/configuration/syncSettings/syncDetails.js
@@ -0,0 +1,4 @@
+export default function syncDetails(settings) {
+ settings.details = arrayOfVariablesCheck([], settings.details);
+ if (settings.details.length === 0) delete settings.details;
+}
diff --git a/src/configuration/syncSettings/syncFilters.js b/src/configuration/syncSettings/syncFilters.js
new file mode 100644
index 0000000..6734fa0
--- /dev/null
+++ b/src/configuration/syncSettings/syncFilters.js
@@ -0,0 +1,68 @@
+export default function syncFilters(settings) {
+ //recency ranges
+ settings.recencyRanges = settings.recency_cutoffs.map(
+ (d, i) => (i > 0 ? [settings.recency_cutoffs[i - 1], d] : [0, d])
+ );
+ settings.recencyRanges.push([
+ settings.recency_cutoffs[settings.recency_cutoffs.length - 1],
+ null
+ ]);
+
+ //recency range categories
+ settings.recencyRangeCategories = settings.recency_cutoffs.every(
+ recency_range => recency_range % 7 === 0
+ )
+ ? settings.recencyRanges.map(
+ (recencyRange, i) =>
+ i < settings.recencyRanges.length - 1
+ ? `${recencyRange.map(days => days / 7).join('-')} weeks`
+ : `>${recencyRange[0] / 7} weeks`
+ )
+ : settings.recencyRanges.map(
+ (recencyRange, i) =>
+ i < settings.recencyRanges.length - 1
+ ? `${recencyRange.join('-')} days`
+ : `>${recencyRange[0]} days`
+ );
+
+ //default filters
+ const defaultFilters = [
+ {
+ value_col: 'queryrecency',
+ label: 'Query Recency',
+ multiple: true
+ },
+ {
+ value_col: settings.form_col,
+ label: 'Form',
+ multiple: true
+ },
+ {
+ value_col: settings.site_col,
+ label: 'Site',
+ multiple: true
+ },
+ {
+ value_col: settings.marking_group_col,
+ label: 'Marking Group',
+ multiple: true
+ },
+ {
+ value_col: settings.visit_col,
+ label: 'Visit/Folder',
+ multiple: true
+ }
+ ];
+
+ //add status group variables to list of filters
+ settings.status_groups
+ .slice()
+ .reverse()
+ .forEach(status_group => {
+ status_group.multiple = true;
+ defaultFilters.unshift(settings.clone(status_group));
+ });
+
+ //add custom filters
+ settings.filters = settings.arrayOfVariablesCheck(defaultFilters, settings.filters);
+}
diff --git a/src/configuration/syncSettings/syncGroups.js b/src/configuration/syncSettings/syncGroups.js
new file mode 100644
index 0000000..3d804c8
--- /dev/null
+++ b/src/configuration/syncSettings/syncGroups.js
@@ -0,0 +1,10 @@
+export default function syncGroups(settings) {
+ const defaultGroups = [
+ { value_col: settings.form_col, label: 'Form' },
+ { value_col: 'Form: Field', label: 'Form: Field' },
+ { value_col: settings.site_col, label: 'Site' },
+ { value_col: settings.marking_group_col, label: 'Marking Group' },
+ { value_col: settings.visit_col, label: 'Visit/Folder' }
+ ];
+ settings.groups = settings.arrayOfVariablesCheck(defaultGroups, settings.groups);
+}
diff --git a/src/configuration/syncSettings/syncStatusGroups.js b/src/configuration/syncSettings/syncStatusGroups.js
new file mode 100644
index 0000000..dd2c205
--- /dev/null
+++ b/src/configuration/syncSettings/syncStatusGroups.js
@@ -0,0 +1,65 @@
+export default function syncStatusGroups(settings) {
+ //age ranges
+ settings.ageRanges = settings.age_cutoffs.map(
+ (d, i) => (i > 0 ? [settings.age_cutoffs[i - 1], d] : [0, d])
+ );
+ settings.ageRanges.push([settings.age_cutoffs[settings.age_cutoffs.length - 1], null]);
+
+ //age range categories
+ settings.ageRangeCategories = settings.age_cutoffs.every(age_range => age_range % 7 === 0)
+ ? settings.ageRanges.map(
+ (ageRange, i) =>
+ i < settings.ageRanges.length - 1
+ ? `${ageRange.map(days => days / 7).join('-')} wks`
+ : `>${ageRange[0] / 7} wks`
+ )
+ : settings.ageRanges.map(
+ (ageRange, i) =>
+ i < settings.ageRanges.length - 1
+ ? `${ageRange.join('-')} days`
+ : `>${ageRange[0]} days`
+ );
+
+ //age range colors
+ let ageRangeColors = settings.age_range_colors.slice(
+ settings.age_range_colors.length - settings.ageRanges.length
+ );
+ settings.status_order.forEach((status, i) => {
+ if (settings.age_statuses.indexOf(status) < 0)
+ ageRangeColors.push(settings.status_colors[i]);
+ });
+
+ //reconcile settings.status_order with settings.status_colors to ensure equal length
+ if (settings.status_order.length !== settings.status_colors.length) {
+ console.warn('The number of query statuses does not match the number of query colors:');
+ console.log(settings.status_order);
+ console.log(settings.status_colors);
+ }
+
+ //default status groups
+ const defaultStatusGroups = [
+ {
+ value_col: 'queryage', // derived in ../chart/onInit/defineNewVariables
+ label: 'Query Age',
+ order: settings.ageRangeCategories.concat(
+ settings.status_order.filter(status => settings.age_statuses.indexOf(status) < 0)
+ ),
+ colors: ageRangeColors
+ },
+ {
+ value_col: settings.status_col,
+ label: 'Query Status',
+ order: settings.status_order,
+ colors: settings.status_colors
+ }
+ ];
+
+ //add custom status groups
+ settings.status_groups = settings.arrayOfVariablesCheck(
+ defaultStatusGroups,
+ settings.status_groups
+ );
+ settings.status_group = settings.status_groups.find(
+ status_group => status_group.value_col === settings.color_by_col
+ );
+}
diff --git a/src/configuration/syncSettings/syncWebchartsSettings.js b/src/configuration/syncSettings/syncWebchartsSettings.js
new file mode 100644
index 0000000..261bc83
--- /dev/null
+++ b/src/configuration/syncSettings/syncWebchartsSettings.js
@@ -0,0 +1,23 @@
+export default function syncWebchartsSettings(settings) {
+ //y-axis
+ settings.y.column = settings.form_col;
+ settings.y.sort = settings.alphabetize ? 'alphabetical-ascending' : 'total-descending';
+ settings.y.range_band = settings.range_band || 25;
+
+ //mark settings
+ settings.marks[0].per[0] = settings.form_col;
+ settings.marks[0].split = settings.status_group.value_col;
+ settings.marks[0].arrange = settings.bar_arrangement;
+ settings.marks[0].tooltip = `[${settings.status_group.value_col}] - $x queries`;
+
+ //stratification
+ settings.color_by = settings.status_group.value_col;
+ settings.color_dom = settings.status_group.order ? settings.status_group.order.slice() : null;
+ settings.colors = settings.status_group.colors;
+
+ //legend
+ settings.legend.label = settings.status_group.label;
+ settings.legend.order = settings.status_group.order
+ ? settings.status_group.order.slice()
+ : null;
+}
diff --git a/src/configuration/webchartsSettings.js b/src/configuration/webchartsSettings.js
deleted file mode 100644
index dfe7df9..0000000
--- a/src/configuration/webchartsSettings.js
+++ /dev/null
@@ -1,32 +0,0 @@
-export default {
- x: {
- label: '# of Queries',
- behavior: 'flex'
- },
- y: {
- type: 'ordinal',
- column: null, // set in syncSettings()
- label: 'Form',
- sort: 'total-descending'
- },
- marks: [
- {
- type: 'bar',
- per: [null], // set in syncSettings()
- split: null, // set in syncSettings()
- arrange: 'stacked',
- summarizeX: 'count',
- tooltip: null // set in syncSettings()
- }
- ],
- color_by: null, // set in syncSettings()
- color_dom: null, // set in syncSettings()
- legend: {
- location: 'top',
- // label: 'Query Status',
- label: null,
- order: null // set in syncSettings()
- },
- range_band: 15,
- margin: { right: '50' } // room for count annotation
-};
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..00831c8
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,51 @@
+import './util/polyfills';
+import clone from './util/clone';
+import configuration from './configuration/index';
+import layout from './layout';
+import styles from './styles';
+import { createControls, createChart, createTable } from 'webcharts';
+import chartCallbacks from './chart/index';
+import listingCallbacks from './listing/index';
+
+export default function queryOverview(element, settings) {
+ //Settings
+ const mergedSettings = Object.assign({}, configuration.settings, settings);
+ const syncedSettings = configuration.syncSettings(mergedSettings);
+ const syncedControlInputs = configuration.syncControlInputs(
+ configuration.controlInputs,
+ syncedSettings
+ );
+
+ //Layout and styles
+ const containers = layout(element);
+ styles();
+
+ //Controls
+ const controls = createControls(containers.controls.node(), {
+ location: 'top',
+ inputs: syncedControlInputs
+ });
+ controls.element = element;
+
+ //Chart
+ const chart = createChart(containers.chart.node(), syncedSettings, controls);
+ chart.element = element;
+ chart.initialSettings = clone(mergedSettings);
+ for (const callback in chartCallbacks)
+ chart.on(callback.substring(2).toLowerCase(), chartCallbacks[callback]);
+
+ //Listing
+ const listing = createTable(containers.listing.node(), {
+ sortable: false,
+ exportable: syncedSettings.exportable
+ });
+ listing.element = element;
+ for (const callback in listingCallbacks)
+ listing.on(callback.substring(2).toLowerCase(), listingCallbacks[callback]);
+
+ //Intertwine
+ chart.listing = listing;
+ listing.chart = chart;
+
+ return chart;
+}
diff --git a/src/layout.js b/src/layout.js
new file mode 100644
index 0000000..f34c686
--- /dev/null
+++ b/src/layout.js
@@ -0,0 +1,22 @@
+export default function layout(element) {
+ const containers = {
+ main: d3
+ .select(element)
+ .append('div')
+ .classed('.query-overview', true)
+ };
+
+ containers.topRow = containers.main.append('div').classed('qo-row qo-row--top', true);
+ containers.controls = containers.topRow
+ .append('div')
+ .classed('qo-component qo-component--controls', true);
+ containers.chart = containers.controls
+ .append('div')
+ .classed('qo-component qo-component--chart', true);
+ containers.bottomRow = containers.main.append('div').classed('qo-row qo-row--bottom', true);
+ containers.listing = containers.bottomRow
+ .append('div')
+ .classed('qo-component qo-component--listing', true);
+
+ return containers;
+}
diff --git a/src/listing/defineStyles.js b/src/listing/defineStyles.js
deleted file mode 100644
index 619e40c..0000000
--- a/src/listing/defineStyles.js
+++ /dev/null
@@ -1,20 +0,0 @@
-export default function defineStyles() {
- const styles = [
- '.query-table-container {' +
- ' overflow-x: auto;' +
- ' width : 100%;' +
- ' transform: rotate(180deg);' +
- ' -webkit-transform:rotate(180deg); ' +
- '}',
- '.query-table {' +
- ' transform: rotate(180deg);' +
- ' -webkit-transform:rotate(180deg); ' +
- '}'
- ];
-
- //Attach styles to DOM.
- this.style = document.createElement('style');
- this.style.type = 'text/css';
- this.style.innerHTML = styles.join('\n');
- document.getElementsByTagName('head')[0].appendChild(this.style);
-}
diff --git a/src/listing/index.js b/src/listing/index.js
new file mode 100644
index 0000000..27840fd
--- /dev/null
+++ b/src/listing/index.js
@@ -0,0 +1,13 @@
+import onInit from './onInit';
+import onLayout from './onLayout';
+import onPreprocess from './onPreprocess';
+import onDraw from './onDraw';
+import onDestroy from './onDestroy';
+
+export default {
+ onInit,
+ onLayout,
+ onPreprocess,
+ onDraw,
+ onDestroy
+};
diff --git a/src/listing/onDraw.js b/src/listing/onDraw.js
index ce38e44..face692 100644
--- a/src/listing/onDraw.js
+++ b/src/listing/onDraw.js
@@ -1,8 +1,13 @@
-import onClick from './onDraw/onClick';
+import updateColumnSorting from './onDraw/updateColumnSorting';
+import truncateCellText from './onDraw/truncateCellText';
import moveScrollBarLeft from './onDraw/moveScrollBarLeft';
export default function onDraw() {
- onClick.call(this);
+ //Update default Webcharts column sorting.
+ updateColumnSorting.call(this);
+
+ //Truncate cells with length greater than `settings.truncation_cutoff`.
+ truncateCellText.call(this);
//Move table scrollbar all the way to the left.
moveScrollBarLeft.call(this);
diff --git a/src/listing/onDraw/truncateCellText.js b/src/listing/onDraw/truncateCellText.js
new file mode 100644
index 0000000..58e5fb8
--- /dev/null
+++ b/src/listing/onDraw/truncateCellText.js
@@ -0,0 +1,8 @@
+export default function truncateCellText() {
+ if (this.data.raw.length)
+ this.tbody
+ .selectAll('td')
+ .attr('title', d => d.text)
+ .filter(d => d.text.length > this.chart.initialSettings.truncation_cutoff)
+ .text(d => `${d.text.substring(0, this.chart.initialSettings.truncation_cutoff)}...`);
+}
diff --git a/src/listing/onDraw/onClick.js b/src/listing/onDraw/updateColumnSorting.js
similarity index 95%
rename from src/listing/onDraw/onClick.js
rename to src/listing/onDraw/updateColumnSorting.js
index dbb6134..6268e9c 100644
--- a/src/listing/onDraw/onClick.js
+++ b/src/listing/onDraw/updateColumnSorting.js
@@ -1,6 +1,6 @@
-import manualSort from './onClick/manualSort';
+import manualSort from './updateColumnSorting/manualSort';
-export default function onClick() {
+export default function updateColumnSorting() {
const context = this;
this.thead_cells.on('click', function(d) {
diff --git a/src/listing/onDraw/onClick/manualSort.js b/src/listing/onDraw/updateColumnSorting/manualSort.js
similarity index 100%
rename from src/listing/onDraw/onClick/manualSort.js
rename to src/listing/onDraw/updateColumnSorting/manualSort.js
diff --git a/src/listing/onLayout.js b/src/listing/onLayout.js
index 7b87207..3961374 100644
--- a/src/listing/onLayout.js
+++ b/src/listing/onLayout.js
@@ -1,8 +1,8 @@
-import resetListing from './onLayout/resetListing';
+import addResetButton from './onLayout/addResetButton';
import addTableContainer from './onLayout/addTableContainer';
export default function onLayout() {
- resetListing.call(this);
+ addResetButton.call(this);
addTableContainer.call(this);
this.wrap.select('.sortable-container').classed('hidden', false);
this.table.style('width', '100%').style('display', 'table');
diff --git a/src/listing/onLayout/resetListing.js b/src/listing/onLayout/addResetButton.js
similarity index 72%
rename from src/listing/onLayout/resetListing.js
rename to src/listing/onLayout/addResetButton.js
index a1872ca..f146075 100644
--- a/src/listing/onLayout/resetListing.js
+++ b/src/listing/onLayout/addResetButton.js
@@ -1,13 +1,7 @@
-export default function resetListing() {
- this.wrap
+export default function addResetButton() {
+ this.resetButton = this.wrap
.insert('button', ':first-child')
- .attr('id', 'clear-listing')
- .style({
- margin: '5px',
- padding: '5px',
- float: 'left',
- display: 'block'
- })
+ .classed('qo-button qo-button--reset-listing', true)
.text('Reset listing')
.on('click', () => {
this.wrap.selectAll('*').remove();
@@ -28,6 +22,5 @@ export default function resetListing() {
fill: d => this.chart.colorScale(d.key)
});
this.chart.listing.init(this.chart.filtered_data);
- this.chart.wrap.select('#listing-instruction').style('display', 'block');
});
}
diff --git a/src/listing/onLayout/addTableContainer.js b/src/listing/onLayout/addTableContainer.js
index b877873..f34c4b0 100644
--- a/src/listing/onLayout/addTableContainer.js
+++ b/src/listing/onLayout/addTableContainer.js
@@ -4,10 +4,10 @@ export default function addTableContainer() {
const table = this.table.node();
this.tableContainer = this.wrap
.append('div')
- .classed('query-table-container', true)
+ .classed('qo-table-container', true)
.node();
- this.wrap.select('table').classed('query-table', true); // I want to ensure that no other webcharts tables get flipped upside down
+ this.wrap.select('table').classed('qo-table', true); // I want to ensure that no other webcharts tables get flipped upside down
table.parentNode.insertBefore(this.tableContainer, table);
this.tableContainer.appendChild(table);
diff --git a/src/listing/onPreprocess.js b/src/listing/onPreprocess.js
new file mode 100644
index 0000000..c0c0cf1
--- /dev/null
+++ b/src/listing/onPreprocess.js
@@ -0,0 +1 @@
+export default function onPreprocess() {}
diff --git a/src/styles.js b/src/styles.js
new file mode 100644
index 0000000..b7bbe27
--- /dev/null
+++ b/src/styles.js
@@ -0,0 +1,208 @@
+export default function styles() {
+ const styles = [
+
+ /***--------------------------------------------------------------------------------------\
+ Framework
+ \--------------------------------------------------------------------------------------***/
+
+ '.query-overview {' +
+ ' width: 100%;' +
+ ' display: inline-block;' +
+ '}',
+ '.qo-row {' +
+ ' width: 100%;' +
+ ' display: inline-block;' +
+ '}',
+ '.qo-component {' +
+ '}',
+ '.qo-row--top {' +
+ '}',
+ '.qo-row--bottom {' +
+ '}',
+
+ /***--------------------------------------------------------------------------------------\
+ Controls
+ \--------------------------------------------------------------------------------------***/
+
+ '.qo-component--controls {' +
+ ' width: 100%;' +
+ '}',
+ '.qo-component--controls .wc-controls {' +
+ ' margin-bottom: 0;' +
+ '}',
+ '.qo-control-grouping {' +
+ ' display: inline-block;' +
+ '}',
+ '.qo-button {' +
+ ' float: left;' +
+ ' display: block;' +
+ '}',
+ '.qo-control-grouping--label,' +
+ '.wc-control-label {' +
+ ' cursor: help;' +
+ ' margin-bottom: 3px;' +
+ '}',
+ '.qo-control-grouping--label {' +
+ ' text-align: center;' +
+ ' width: 100%;' +
+ ' font-size: 24px;' +
+ ' border-bottom: 2px solid #aaa;' +
+ '}',
+ '.span-description {' +
+ ' display: none !important;' +
+ '}',
+
+ /****---------------------------------------------------------------------------------\
+ Other controls
+ \---------------------------------------------------------------------------------****/
+
+ '.qo-control-grouping--other-controls {' +
+ ' width: 20%;' +
+ ' float: right;' +
+ '}',
+ '.qo-control-grouping--other-controls .control-group {' +
+ ' width: 100%;' +
+ ' margin-bottom: 15px;' +
+ '}',
+ '.qo-control-grouping--other-controls .control-group:nth-child(n+3) {' +
+ ' border-top: 1px solid #aaa;' +
+ '}',
+ '.qo-control-grouping--other-controls .control-group .wc-control-label {' +
+ ' text-align: center;' +
+ ' font-size: 110%;' +
+ '}',
+
+ //dropdowns
+ '.qo-dropdown {' +
+ '}',
+ '.qo-dropdown .wc-control-label {' +
+ '}',
+ '.qo-dropdown .changer {' +
+ ' margin: 0 auto;' +
+ '}',
+
+ //radio buttons
+ '.qo-radio {' +
+ ' display: flex !important;' +
+ ' justify-content: center;' +
+ ' flex-wrap: wrap;' +
+ '}',
+ '.qo-radio .wc-control-label {' +
+ ' width: 100%;' +
+ '}',
+ '.qo-radio .radio {' +
+ ' margin-top: 0 !important;' +
+ '}',
+
+ //checkboxes
+ '.qo-checkbox {' +
+ ' display: flex !important;' +
+ ' justify-content: center;' +
+ '}',
+ '.qo-checkbox .wc-control-label {' +
+ ' margin-right: 5px;' +
+ '}',
+ '.qo-checkbox .changer {' +
+ ' margin-top: 5px !important;' +
+ '}',
+
+ /****---------------------------------------------------------------------------------\
+ Filters
+ \---------------------------------------------------------------------------------****/
+
+ '.qo-control-grouping--filters {' +
+ ' width: 20%;' +
+ ' float: left;' +
+ ' display: flex;' +
+ ' flex-wrap: wrap;' +
+ ' justify-content: space-evenly;' +
+ '}',
+ '.qo-subsetter {' +
+ ' margin: 5px 2px !important;' +
+ ' border-top: 1px solid #aaa;' +
+ ' padding-top: 5px;' +
+ '}',
+ '.qo-subsetter .wc-control-label {' +
+ ' margin: 0 5px 3px 0;' +
+ ' text-align: center;' +
+ '}',
+ '.qo-select-all {' +
+ '}',
+ '.qo-subsetter .changer {' +
+ ' margin: 0 auto;' +
+ '}',
+
+ /***--------------------------------------------------------------------------------------\
+ Chart
+ \--------------------------------------------------------------------------------------***/
+
+ '.qo-component--chart {' +
+ ' width: 58%;' +
+ ' margin: 0 auto;' +
+ ' position: relative;' +
+ '}',
+ '.qo-button--reset-chart {' +
+ ' position: absolute;' +
+ ' top: 0;' +
+ ' left: 0;' +
+ ' z-index: 2;' +
+ ' width: 91px;' +
+ ' padding: 3px 0;' +
+ '}',
+ '.qo-button--undo {' +
+ ' position: absolute;' +
+ ' bottom: 0;' +
+ ' left: 0;' +
+ ' z-index: 2;' +
+ ' width: 91px;' +
+ ' padding: 3px 0;' +
+ '}',
+ '.qo-component--chart .wc-chart {' +
+ ' z-index: 1;' +
+ '}',
+ '.qo-component--chart .legend-title {' +
+ ' cursor: help;' +
+ '}',
+ '.qo-component--chart .legend-item {' +
+ ' cursor: pointer;' +
+ ' border-radius: 4px;' +
+ ' padding: 5px;' +
+ ' padding-left: 8px;' +
+ ' margin-right: 5px !important;' +
+ '}',
+ '.qo-footnote {' +
+ ' width: 100%;' +
+ ' text-align: center;' +
+ ' font-style: italic;' +
+ '}',
+
+ /***--------------------------------------------------------------------------------------\
+ Listing
+ \--------------------------------------------------------------------------------------***/
+
+ '.qo-component--listing {' +
+ ' width: 100%;' +
+ '}',
+ '.qo-button--reset-listing {' +
+ ' padding: 3px;' +
+ ' margin: 10px 5px 10px 0;' +
+ '}',
+ '.qo-table-container {' +
+ ' overflow-x: auto;' +
+ ' width: 100%;' +
+ ' transform: rotate(180deg);' +
+ ' -webkit-transform: rotate(180deg); ' +
+ '}',
+ '.qo-table {' +
+ ' width: 100%;' +
+ ' transform: rotate(180deg);' +
+ ' -webkit-transform: rotate(180deg); ' +
+ '}',
+ ];
+
+ //Attach styles to DOM.
+ const style = document.createElement('style');
+ style.type = 'text/css';
+ style.innerHTML = styles.join('\n');
+ document.getElementsByTagName('head')[0].appendChild(style);
+}
diff --git a/src/util/d3-selection-moveToBack.js b/src/util/d3-selection-moveToBack.js
deleted file mode 100644
index 1cad002..0000000
--- a/src/util/d3-selection-moveToBack.js
+++ /dev/null
@@ -1,6 +0,0 @@
-d3.selection.prototype.moveToBack = function() {
- return this.each(function() {
- var firstChild = this.parentNode.firstChild;
- if (firstChild) this.parentNode.insertBefore(this, firstChild);
- });
-};
diff --git a/src/util/d3-selection-moveToFront.js b/src/util/d3-selection-moveToFront.js
deleted file mode 100644
index 41170f0..0000000
--- a/src/util/d3-selection-moveToFront.js
+++ /dev/null
@@ -1,5 +0,0 @@
-d3.selection.prototype.moveToFront = function() {
- return this.each(function() {
- this.parentNode.appendChild(this);
- });
-};
diff --git a/src/util/flatMap.js b/src/util/flatMap.js
deleted file mode 100644
index f1e61fb..0000000
--- a/src/util/flatMap.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// from https://gist.github.com/samgiles/762ee337dff48623e729
-
-export default (Array.prototype.flatMap = function(lambda) {
- return Array.prototype.concat.apply([], this.map(lambda));
-});
diff --git a/src/util/polyfills.js b/src/util/polyfills.js
index 17f4054..b1e9523 100644
--- a/src/util/polyfills.js
+++ b/src/util/polyfills.js
@@ -1,4 +1,3 @@
-//[].find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
@@ -43,7 +42,6 @@ if (!Array.prototype.find) {
});
}
-//{}.assign
if (typeof Object.assign != 'function') {
Object.defineProperty(Object, 'assign', {
value: function assign(target, varArgs) {
@@ -78,7 +76,6 @@ if (typeof Object.assign != 'function') {
});
}
-//[].findIndex
if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
value: function(predicate) {
@@ -122,3 +119,20 @@ if (!Array.prototype.findIndex) {
}
});
}
+
+Array.prototype.flatMap = function(lambda) {
+ return Array.prototype.concat.apply([], this.map(lambda));
+};
+
+d3.selection.prototype.moveToBack = function() {
+ return this.each(function() {
+ var firstChild = this.parentNode.firstChild;
+ if (firstChild) this.parentNode.insertBefore(this, firstChild);
+ });
+};
+
+d3.selection.prototype.moveToFront = function() {
+ return this.each(function() {
+ this.parentNode.appendChild(this);
+ });
+};
diff --git a/src/wrapper.js b/src/wrapper.js
deleted file mode 100644
index 7a0ee73..0000000
--- a/src/wrapper.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import './util/polyfills';
-import './util/d3-selection-moveToFront';
-import './util/d3-selection-moveToBack';
-import clone from './util/clone';
-
-import configuration from './configuration/index';
-
-import { createControls, createChart, createTable } from 'webcharts';
-
-//chart callbacks
-import onInit from './chart/onInit';
-import onLayout from './chart/onLayout';
-import onPreprocess from './chart/onPreprocess';
-import onDataTransform from './chart/onDataTransform';
-import onDraw from './chart/onDraw';
-import onResize from './chart/onResize';
-import onDestroy from './chart/onDestroy';
-
-//listing callbacks
-import onInitL from './listing/onInit';
-import onLayoutL from './listing/onLayout';
-import onDrawL from './listing/onDraw';
-import onDestroyL from './listing/onDestroy';
-import defineStyles from './listing/defineStyles';
-
-export default function queryOverview(element, settings) {
- const mergedSettings = Object.assign({}, configuration.settings, settings);
- const syncedSettings = configuration.syncSettings(mergedSettings);
- const syncedControlInputs = configuration.syncControlInputs(
- configuration.controlInputs,
- syncedSettings
- );
- const controls = createControls(element, {
- location: 'top',
- inputs: syncedControlInputs
- });
- const chart = createChart(element, syncedSettings, controls);
- const listing = createTable(element, {
- sortable: false,
- exportable: syncedSettings.exportable
- });
-
- chart.initialSettings = clone(mergedSettings);
- chart.on('init', onInit);
- chart.on('layout', onLayout);
- chart.on('preprocess', onPreprocess);
- chart.on('datatransform', onDataTransform);
- chart.on('draw', onDraw);
- chart.on('resize', onResize);
-
- listing.on('init', onInitL);
- listing.on('layout', onLayoutL);
- listing.on('draw', onDrawL);
- listing.on('destroy', onDestroyL);
-
- chart.listing = listing;
- listing.chart = chart;
-
- //add Table stylesheet
- defineStyles.call(listing);
-
- return chart;
-}
diff --git a/build/test-page/index.css b/test-page/index.css
similarity index 76%
rename from build/test-page/index.css
rename to test-page/index.css
index 5ba669a..9f2b3f0 100644
--- a/build/test-page/index.css
+++ b/test-page/index.css
@@ -6,20 +6,20 @@
}
#title {
width: 96%;
- padding: 0 0 1% 0;
+ padding: 0 0 12px 0;
border-bottom: 2px solid lightgray;
- margin: 2% 2% 1% 2%;
+ margin: 24px 2% 12px 2%;
font-size: 32px;
font-weight: normal;
}
#subtitle {
width: 96%;
- margin: 0 2% 1% 2%;
+ margin: 0 2% 12px 2%;
font-size: 24px;
font-weight: lighter;
}
#container {
- width: 50%;
- margin: 1% 2%;
+ width: 96%;
+ margin: 12px 2%;
display: inline-block;
}
diff --git a/build/test-page/index.html b/test-page/index.html
similarity index 66%
rename from build/test-page/index.html
rename to test-page/index.html
index 489537f..c45695d 100644
--- a/build/test-page/index.html
+++ b/test-page/index.html
@@ -5,10 +5,10 @@
-
-
+
+
-
+
diff --git a/test-page/index.js b/test-page/index.js
new file mode 100644
index 0000000..daafd75
--- /dev/null
+++ b/test-page/index.js
@@ -0,0 +1,16 @@
+d3.csv(
+ //'https://raw.githubusercontent.com/RhoInc/viz-library/master/data/queries/queries.csv',
+ '../../viz-library/data/dataCleaning/queries/queries.csv',
+ function(error,data) {
+ if (error)
+ console.log(error);
+
+ var settings = {
+ };
+ var instance = queryOverview(
+ '#container',
+ settings
+ );
+ instance.init(data);
+ }
+);