diff --git a/README.md b/README.md index 87fd4d8..b654bd7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Overview -The web codebook is a javascript library that provides a concise summary of every variable in a dataset. The codebook includes interactive features such as real-time filters and requires minimal user configuration. +The web codebook is a JavaScript library that provides a concise summary of every variable in a dataset. The codebook includes interactive features such as real-time filters and requires minimal user configuration. [Click here for a live demo](https://rhoinc.github.io/viz-library/examples/0009-web-codebook-demo/example.html). When the page loads, the user sees a "codebook" providing a graphical data summary for each data column. @@ -13,16 +13,16 @@ The web codebook is a javascript library that provides a concise summary of ever ## Background -The web-codebook is inspired Frank Harrell's excellent `summarize` method from the [hmisc R package](https://cran.r-project.org/web/packages/Hmisc/Hmisc.pdf). `summarize` creates concise data summaries with minimal user configuration. Further, [Agustin Calatroni](http://graphics.rhoworld.com/pubs/SCT2007_Calatroni.pdf) and [Shane Rosanbalm](https://github.com/RhoInc/sas-codebook) have created SAS data summary methods at Rho in recent years. Our goal here is to create a web-based data summary that uses the same general principles (concise data display, minimal configuration), but with added interactivity (filters, paneled displays, data listings) that is not possible in the static displays created by hmisc or in SAS. +The web-codebook is inspired Frank Harrell's excellent `summarize` method from the [Hmisc R package](https://cran.r-project.org/web/packages/Hmisc/Hmisc.pdf). `summarize` creates concise data summaries with minimal user configuration. Further, [Agustin Calatroni](http://graphics.rhoworld.com/pubs/SCT2007_Calatroni.pdf) and [Shane Rosanbalm](https://github.com/RhoInc/sas-codebook) have created SAS data summary methods at Rho in recent years. Our goal here is to create a web-based data summary that uses the same general principles (concise data display, minimal configuration), but with added interactivity (filters, paneled displays, data listings) that is not possible in the static displays created by Hmisc or in SAS. ## Typical Usage -Generally speaking, no configuration is needed to create a web-codebook. Just [load a json data set](https://github.com/RhoInc/web-codebook/wiki/Data-Guidelines) and the tool will automatically create a user interface (filters, etc.) based on the data set loaded. Initialize the chart like so: +Generally speaking, no configuration is needed to create a web-codebook. Just [load a JSON data set](https://github.com/RhoInc/web-codebook/wiki/Data-Guidelines) and the tool will automatically create a user interface (filters, etc.) based on the data set loaded. Initialize the chart like so: ```javascript webcodebook.createChart('#chartLocation', {}).init(data); ``` -See the [API](https://github.com/RhoInc/web-codebook/wiki/API) and [configuration](https://github.com/RhoInc/web-codebook/wiki/Codebook-Configuration) pages for more details about custom configurations. +See the [API](https://github.com/RhoInc/web-codebook/wiki/API) and [Configuration](https://github.com/RhoInc/web-codebook/wiki/Codebook-Configuration) pages for more details about custom configurations. ## Links diff --git a/build/test-page/explorer-local.html b/build/test-page/explorer-local.html new file mode 100644 index 0000000..ff34f19 --- /dev/null +++ b/build/test-page/explorer-local.html @@ -0,0 +1,24 @@ + + + + Web Codebook + + + + + + + + + + + + + +
Web Codebook - Explorer
+
Test Page
+
+ + + + diff --git a/build/test-page/explorer.html b/build/test-page/explorer.html new file mode 100644 index 0000000..d5b3b63 --- /dev/null +++ b/build/test-page/explorer.html @@ -0,0 +1,24 @@ + + + + Web Codebook + + + + + + + + + + + + + +
Web Codebook - Explorer
+
Test Page
+
+ + + + diff --git a/build/test-page/explorer.js b/build/test-page/explorer.js new file mode 100644 index 0000000..0d39cf2 --- /dev/null +++ b/build/test-page/explorer.js @@ -0,0 +1,65 @@ +document.onreadystatechange = function () { + if (document.readyState === 'complete') { + //Load local build if in local environment. + if (window.origin !== 'https://rhoinc.github.io') { + var head = document.getElementsByTagName('head')[0]; + + //...load local build. + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = '../webcodebook.js'; + head.appendChild(script); + + //...load local stylesheet. + for (var i = 0; i < document.styleSheets.length; i++) { + var styleSheet = document.styleSheets[i]; + if (styleSheet.href.indexOf('webcodebook') > -1) + styleSheet.disabled = true; + } + var link = document.createElement('link'); + link.type = 'text/css'; + link.rel = 'stylesheet'; + link.href = '../../css/webcodebook.css'; + head.appendChild(link); + } + + function initExplorer(fileList){ + // console.log(fileList) + var metaFiles = ["AE","DM","LB"] //list of files to add meta data + d3.csv("https://rawgit.com/RhoInc/viz-library/master/data/safetyData/variableMetaData.csv",function(error,meta){ + meta.forEach(function(f){ + f.file = f.Form+".csv" + f.label = f.Label + // console.log(f) + }) + var fileList_clean = fileList.map(function(f){ + f.path = "https://rawgit.com/RhoInc/viz-library/master"+f.rel_path.slice(1); + f.shortname = f.filename.replace(/\.[^/.]+$/, "") + if(metaFiles.indexOf(f.shortname)>-1){ + f.meta = meta.filter(m=>m.Form == f.shortname) + .map(function(m){ + m.value_col = m.Variable; + return m; + }) + f["Metadata included?"] = "Yes" + }else{ + f["Metadata included?"] = "No" + } + return f; + }) + + var settings = { + labelColumn:"filename", + ignoredColumns: ["local_path","rel_path","path"], + files:fileList_clean, + meta:meta + }; + console.log(settings) + var explorer = webcodebook.createExplorer("#container", settings).init(); + }) + + } + + initExplorer(dataFiles) + } +} diff --git a/build/test-page/index.css b/build/test-page/index.css new file mode 100644 index 0000000..2046b71 --- /dev/null +++ b/build/test-page/index.css @@ -0,0 +1,25 @@ +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300); +* { + padding: 0; + margin: 0; + font-family: 'Open Sans'; +} +#title { + width: 96%; + padding: 0 0 1% 0; + border-bottom: 2px solid lightgray; + margin: 2% 2% 1% 2%; + font-size: 32px; + font-weight: normal; +} +#subtitle { + width: 96%; + margin: 0 2% 1% 2%; + font-size: 24px; + font-weight: lighter; +} +#container { + width: 96%; + margin: 1% 2%; + display: inline-block; +} diff --git a/build/test-page/index.html b/build/test-page/index.html new file mode 100644 index 0000000..91472b1 --- /dev/null +++ b/build/test-page/index.html @@ -0,0 +1,23 @@ + + + + Web Codebook + + + + + + + + + + + + +
Web Codebook
+
Test Page
+
+ + + + diff --git a/build/test-page/index.js b/build/test-page/index.js new file mode 100644 index 0000000..a11e378 --- /dev/null +++ b/build/test-page/index.js @@ -0,0 +1,50 @@ +document.onreadystatechange = function () { + if (document.readyState === 'complete') { + //Load local build if in local environment. + if (window.origin !== 'https://rhoinc.github.io') { + var head = document.getElementsByTagName('head')[0]; + + //...load local build. + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = '../webcodebook.js'; + head.appendChild(script); + + //...load local stylesheet. + for (var i = 0; i < document.styleSheets.length; i++) { + var styleSheet = document.styleSheets[i]; + if (styleSheet.href.indexOf('webcodebook') > -1) + styleSheet.disabled = true; + } + var link = document.createElement('link'); + link.type = 'text/css'; + link.rel = 'stylesheet'; + link.href = '../../css/webcodebook.css'; + head.appendChild(link); head.appendChild(script); + } + + d3.csv( + 'https://rawgit.com/RhoInc/viz-library/master/data/testData/ADTIMELINES_partialMissing.csv', + function(error,data) { + if (error) + console.log(error); + + var settings = { + // chartVisibility: 'hidden', + meta:[ + { + value_col:"USUBJID", + label:"Subject ID", + description: "Unique Identifier" + } + ] + }; + var instance = webcodebook.createCodebook( + '#container', + settings + ); + instance.init(data); + } + ); + }; +} diff --git a/build/test-page/local.html b/build/test-page/local.html new file mode 100644 index 0000000..9ae4b38 --- /dev/null +++ b/build/test-page/local.html @@ -0,0 +1,24 @@ + + + + Web Codebook + + + + + + + + + + + + + + +
Web Codebook
+
Test Page
+
+ + + diff --git a/build/webcodebook.js b/build/webcodebook.js index 405fa40..f9bc167 100644 --- a/build/webcodebook.js +++ b/build/webcodebook.js @@ -4,36 +4,260 @@ : typeof define === 'function' && define.amd ? define(['d3', 'webcharts'], factory) : (global.webcodebook = factory(global.d3, global.webCharts)); -})(this, function(d3, webcharts) { +})(this, function(d3$1, webcharts) { 'use strict'; - var _typeof = typeof Symbol === 'function' && - typeof Symbol.iterator === 'symbol' - ? function(obj) { - return typeof obj; + + 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'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { + // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + + return to; + }, + writable: true, + configurable: true + }); + } + + if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function value(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, 'length')). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, � kValue, k, O �)). + // d. If testResult is true, return kValue. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; } - : function(obj) { - return obj && - typeof Symbol === 'function' && - obj.constructor === Symbol && - obj !== Symbol.prototype - ? 'symbol' - : typeof obj; - }; + }); + } - var defineProperty = function(obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; + if (!Array.prototype.findIndex) { + Object.defineProperty(Array.prototype, 'findIndex', { + value: function value(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, � kValue, k, O �)). + // d. If testResult is true, return k. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return k; + } + // e. Increase k by 1. + k++; + } + + // 7. Return -1. + return -1; + } + }); + } + + var _typeof = + typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' + ? function(obj) { + return typeof obj; + } + : function(obj) { + return obj && + typeof Symbol === 'function' && + obj.constructor === Symbol && + obj !== Symbol.prototype + ? 'symbol' + : typeof obj; + }; + + var asyncGenerator = (function() { + function AwaitValue(value) { + this.value = value; } - return obj; - }; + 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; @@ -79,20 +303,20 @@ //wait until the loading indicator is visible var loading = setInterval(function() { try { - var laidOut = d3.select(element).property('offsetwidth') > 0, - displayNone = d3.select(element).style('display') === 'none'; + var laidOut = d3$1.select(element).property('offsetwidth') > 0, + displayNone = d3$1.select(element).style('display') === 'none'; //loading is complete if (!(laidOut && displayNone)) { if (callback) callback(); clearInterval(loading); codebook.loadingIndicator.style('display', 'none'); - d3.select('#loading-text').remove(); + d3$1.select('#loading-text').remove(); } } catch (err) { clearInterval(loading); codebook.loadingIndicator.style('display', 'none'); - d3.select('#loading-text').remove(); + d3$1.select('#loading-text').remove(); codebook.statusWrap .append('div') @@ -111,10 +335,7 @@ function init(data) { var _this = this; - var settings = this.config; - - //create chart wrapper in specified div - this.wrap = d3 + this.wrap = d3$1 .select(this.element) .append('div') .attr('class', 'web-codebook') @@ -139,13 +360,15 @@ //prepare the data summaries _this.data.makeSummary(_this); + //make the title + _this.title.init(_this); + //draw controls _this.util.makeAutomaticFilters(_this); _this.util.makeAutomaticGroups(_this); _this.controls.init(_this); //initialize nav, title and instructions - _this.title.init(_this); _this.nav.init(_this); _this.instructions.init(_this); @@ -243,8 +466,7 @@ codebook.controls.groups.init(codebook); codebook.controls.filters.init(codebook); codebook.controls.controlToggle.init(codebook); - codebook.controls.highlight.init(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); //Hide group-by options corresponding to variables specified in settings.hiddenVariables. codebook.controls.wrap @@ -272,7 +494,7 @@ //add a list of values to each filter object codebook.config.filters.forEach(function(e) { if (!e.hasOwnProperty('values')) - e.values = d3 + e.values = d3$1 .nest() .key(function(d) { return d[e.value_col]; @@ -317,9 +539,12 @@ .html(function(d) { return d.value_col; }); - filterLabel.append('span').classed('filter-label', true).html(function(d) { - return d.value_col !== d.label ? d.label : ''; - }); + filterLabel + .append('span') + .classed('filter-label', true) + .html(function(d) { + return d.value_col !== d.label ? d.label : ''; + }); var filterCustom = filterItem.append('select').attr('multiple', true); @@ -349,9 +574,9 @@ indicateLoading(codebook, '#loading-indicator', function() { // flag the selected options in the config - var options = d3.select(_this).selectAll('option'); + var options = d3$1.select(_this).selectAll('option'); options.each(function(option_d) { - option_d.selected = d3.select(this).property('selected'); + option_d.selected = d3$1.select(this).property('selected'); }); codebook.config.filters.filter(function(filter) { return filter.value_col === d.value_col; @@ -366,7 +591,7 @@ //clear highlights codebook.data.highlighted = []; codebook.data.makeSummary(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); codebook.summaryTable.draw(codebook); codebook.chartMaker.draw(codebook); codebook.dataListing.init(codebook); @@ -382,8 +607,8 @@ function init$2(codebook) { //initialize the wrapper var selector = codebook.controls.wrap - .append('div') - .attr('class', 'custom-filters'), + .append('div') + .attr('class', 'custom-filters'), filterList = selector.append('ul').attr('class', 'filter-list'); update(codebook); @@ -406,7 +631,7 @@ var groupControl = codebook.controls.wrap.select('div.group-select'), groupSelect = groupControl.select('select'), columns = Object.keys(codebook.data.raw[0]), - groupLevels = d3.merge([ + groupLevels = d3$1.merge([ [{ value_col: 'None', label: 'None' }], codebook.config.groups.map(function(m) { return { @@ -451,7 +676,7 @@ codebook.data.makeSummary(codebook); codebook.summaryTable.draw(codebook); codebook.chartMaker.draw(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); }); }); } @@ -492,9 +717,10 @@ codebook.controls.controlToggle.set(codebook); controlToggle.on('click', function() { - codebook.config.controlVisibility = d3.select(this).text() == 'Hide' - ? 'minimized' //click "-" to minimize controls - : 'visible'; // click "+" to show controls + codebook.config.controlVisibility = + d3$1.select(this).text() == 'Hide' + ? 'minimized' //click "-" to minimize controls + : 'visible'; // click "+" to show controls codebook.controls.controlToggle.set(codebook); }); @@ -540,62 +766,6 @@ set: set$2 }; - /*------------------------------------------------------------------------------------------------\ - Initialize clear highlighting button. -\------------------------------------------------------------------------------------------------*/ - - function init$5(codebook) { - //initialize the wrapper - codebook.controls.highlight.clearButton = codebook.controls.summaryWrap - .append('button') - .classed('clear-highlight', true) - .classed('hidden', codebook.data.highlighted.length == 0) - .text('Clear Highlighting') - .on('click', function() { - codebook.data.highlighted = []; - - codebook.data.makeSummary(codebook); - codebook.dataListing.init(codebook); - codebook.summaryTable.draw(codebook); - codebook.chartMaker.draw(codebook); - codebook.controls.updateRowCount(codebook); - }); - } - - /*------------------------------------------------------------------------------------------------\ - Define clear highlighting button object. -\------------------------------------------------------------------------------------------------*/ - - var highlight = { init: init$5 }; - - function updateRowCount(codebook) { - if (codebook.data.summary.length > 0) { - var nShown = codebook.data.summary[0].statistics.N; - var nTot = codebook.data.raw.length; - var percent = d3.format('0.1%')(nShown / nTot); - var tableSummary = - nShown + ' of ' + nTot + ' (' + percent + ') rows selected'; - codebook.controls.rowCount.text(tableSummary).classed('warn', false); - } else { - codebook.controls.rowCount - .text('No rows selected.') - .classed('warn', true); - } - - //Add note regarding highlighted cells and show/hide the clear highlight button - if (codebook.data.highlighted.length > 0) { - codebook.controls.highlightCount.html( - ' and ' + - codebook.data.highlighted.length + - ' highlighted. ' - ); - codebook.controls.highlight.clearButton.classed('hidden', false); - } else { - codebook.controls.highlightCount.text(''); - codebook.controls.highlight.clearButton.classed('hidden', true); - } - } - /*------------------------------------------------------------------------------------------------\ Define controls object. \------------------------------------------------------------------------------------------------*/ @@ -604,37 +774,35 @@ init: init$1, filters: filters, groups: groups, - controlToggle: controlToggle, - highlight: highlight, - updateRowCount: updateRowCount + controlToggle: controlToggle }; var availableTabs = [ { key: 'files', label: 'Files', - selector: '.web-codebook .fileListing', + selector: '.fileListing', controls: false, instructions: 'Click a row to see the codebook for the file.' }, { key: 'codebook', label: 'Codebook', - selector: '.web-codebook .summaryTable', + selector: '.summaryTable', controls: true, instructions: 'Automatically generated data summaries for each column.' }, { key: 'listing', label: 'Data Listing', - selector: '.web-codebook .dataListing', + selector: '.dataListing', controls: true, instructions: 'Listing of all selected records.' }, { key: 'chartMaker', label: 'Charts', - selector: '.web-codebook .chartMaker', + selector: '.chartMaker', controls: true, instructions: 'Pick two variables to compare. Filter and group (panel) the chart using the controls above.' @@ -642,19 +810,20 @@ { key: 'settings', label: '⚙', - selector: '.web-codebook .settings', + selector: '.settings', controls: false, instructions: "This interactive table allows users to modify each column's metadata. Updating these settings will reset the codebook and data listing." } ]; - function init$6(codebook) { + function init$5(codebook) { + var defaultTabs = clone(availableTabs); codebook.nav.wrap.selectAll('*').remove(); //permanently hide the codebook sections that aren't included - availableTabs.forEach(function(tab) { - tab.wrap = d3.select(tab.selector); + defaultTabs.forEach(function(tab) { + tab.wrap = codebook.wrap.select(tab.selector); tab.wrap.classed( 'hidden', codebook.config.tabs @@ -666,7 +835,7 @@ }); //get the tabs for the current codebook - codebook.nav.tabs = availableTabs.filter(function(tab) { + codebook.nav.tabs = defaultTabs.filter(function(tab) { return ( codebook.config.tabs .map(function(m) { @@ -692,17 +861,6 @@ t.wrap.classed('hidden', !t.active); }); - //set control visibility - var activeTab = codebook.nav.tabs.filter(function(f) { - return f.active; - })[0]; - if (codebook.config.controlVisibility != 'disabled') { - codebook.config.controlVisibility = activeTab.controls - ? 'visible' - : 'hidden'; - codebook.controls.controlToggle.set(codebook); - } - //draw the nav if (codebook.nav.tabs.length > 1) { var chartNav = codebook.nav.wrap @@ -743,9 +901,12 @@ codebook.instructions.update(codebook); //show/hide the controls (unless they are disabled) + if (codebook.config.controlVisibility !== 'hidden') + codebook.config.previousControlVisibility = + codebook.config.controlVisibility; if (codebook.config.controlVisibility != 'disabled') { codebook.config.controlVisibility = d.controls - ? 'visible' + ? codebook.config.previousControlVisibility : 'hidden'; codebook.controls.controlToggle.set(codebook); } @@ -759,7 +920,7 @@ \------------------------------------------------------------------------------------------------*/ var nav = { - init: init$6 + init: init$5 }; /*------------------------------------------------------------------------------------------------\ @@ -782,9 +943,12 @@ }); //ENTER - varRows.enter().append('div').attr('class', function(d) { - return 'variable-row ' + d.type; - }); + varRows + .enter() + .append('div') + .attr('class', function(d) { + return 'variable-row ' + d.type; + }); //Hide variable rows corresponding to variables specified in settings.hiddenVariables. varRows.classed('hidden', function(d) { @@ -794,15 +958,13 @@ //Set chart visibility (on initial load only - then keep user settings) if (codebook.config.chartVisibility != 'user-defined') { varRows.classed( - 'hiddenChart', + 'hiddenDetails', codebook.config.chartVisibility != 'visible' ); } - codebook.config.chartVisibility = codebook.config.chartVisibility == - 'hidden' - ? 'hidden' - : 'user-defined'; + codebook.config.chartVisibility = + codebook.config.chartVisibility == 'hidden' ? 'hidden' : 'user-defined'; //ENTER + Update varRows.each(codebook.summaryTable.renderRow); @@ -826,12 +988,12 @@ x: chart.plot_width }) .text(function(d) { - return d3.format(chart.config.y.format)(d); + return d3$1.format(chart.config.y.format)(d); }); } function makeTooltip(d, i, context) { - var format$$1 = d3.format(context.config.measureFormat); + var format$$1 = d3$1.format(context.config.measureFormat); d.selector = 'bar' + i; //Define tooltips. var tooltip = context.svg.append('g').attr('id', d.selector); @@ -849,9 +1011,8 @@ .attr({ x: context.x(d.values.x), dx: context.x(d.values.x) < context.plot_width / 2 ? '1em' : '-1em', - 'text-anchor': context.x(d.values.x) < context.plot_width / 2 - ? 'start' - : 'end' + 'text-anchor': + context.x(d.values.x) < context.plot_width / 2 ? 'start' : 'end' }) .text('' + d.values.x); text @@ -860,11 +1021,12 @@ x: context.x(d.values.x), dx: context.x(d.values.x) < context.plot_width / 2 ? '1em' : '-1em', dy: '-1.5em', - 'text-anchor': context.x(d.values.x) < context.plot_width / 2 - ? 'start' - : 'end' + 'text-anchor': + context.x(d.values.x) < context.plot_width / 2 ? 'start' : 'end' }) - .text('n=' + d.values.raw[0].n + ' (' + d3.format('0.1%')(d.total) + ')'); + .text( + 'n=' + d.values.raw[0].n + ' (' + d3$1.format('0.1%')(d.total) + ')' + ); var dimensions = text[0][0].getBBox(); tooltip.classed('svg-tooltip', true); //have to run after .getBBox() in FF/EI since this sets display:none @@ -885,21 +1047,22 @@ } function highlightData(chart) { - var codebook = d3 - .select(chart.wrap.node().parentNode.parentNode.parentNode) - .datum(), + var codebook = d3$1 + .select(chart.wrap.node().parentNode.parentNode.parentNode) + .datum(), // codebook object is attached to .summaryTable element bars = chart.svg.selectAll('.bar-group'); bars.on('click', function(d) { indicateLoading(codebook, '.highlightCount', function() { - var newIndexes = chart.config.chartType.indexOf('Bars') > -1 - ? d.values.raw[0].indexes - : chart.config.chartType === 'histogramBoxPlot' - ? d.values.raw.map(function(di) { - return di.index; - }) - : []; + var newIndexes = + chart.config.chartType.indexOf('Bars') > -1 + ? d.values.raw[0].indexes + : chart.config.chartType === 'histogramBoxPlot' + ? d.values.raw.map(function(di) { + return di.index; + }) + : []; var currentIndexes = codebook.data.highlighted.map(function(di) { return di['web-codebook-index']; }); @@ -920,7 +1083,7 @@ codebook.dataListing.init(codebook); codebook.summaryTable.draw(codebook); codebook.chartMaker.draw(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); }); }); } @@ -944,16 +1107,13 @@ this.svg .on('mousemove', function() { //Highlight closest bar. - var mouse$$1 = d3.mouse(this); + var mouse$$1 = d3$1.mouse(this); var x = mouse$$1[0]; - var y = mouse$$1[1]; var minimum = void 0; - var bar = {}; bars.each(function(d, i) { d.distance = Math.abs(context.x(d.values.x) - x); if (i === 0 || d.distance < minimum) { minimum = d.distance; - bar = d; } }); @@ -968,7 +1128,10 @@ return i === arbitrary; }); } - bars.select('rect').style('stroke-width', null).style('stroke', null); + bars + .select('rect') + .style('stroke-width', null) + .style('stroke', null); closest = closest.select('rect'); //Activate tooltip. @@ -980,7 +1143,10 @@ }) .on('mouseout', function() { context.svg.selectAll('g.svg-tooltip').classed('active', false); - bars.select('rect').style('stroke-width', null).style('stroke', null); + bars + .select('rect') + .style('stroke-width', null) + .style('stroke', null); }); //Add event listener to marks to highlight data. @@ -1024,56 +1190,50 @@ } function createVerticalBars(this_, d) { - var _chartSettings; - - var chartContainer = d3.select(this_).node(); - var rowSelector = d3.select(this_).node().parentNode; - var sortType = d3 + var chartContainer = d3$1.select(this_).node(); + var rowSelector = d3$1.select(this_).node().parentNode; + var sortType = d3$1 .select(rowSelector) .select('.row-controls .x-axis-sort select') .property('value'); - var outcome = d3 + var outcome = d3$1 .select(rowSelector) .select('.row-controls .y-axis-outcome select') .property('value'); - var chartSettings = ( - (_chartSettings = { - y: { - column: outcome === 'rate' ? 'prop_n' : 'n', - type: 'linear', - label: '', - format: outcome === 'rate' ? '0.1%' : 'd', - domain: [0, null] - }, - x: { - column: 'key', - type: 'ordinal', - label: '' - }, - marks: [ - { - type: 'bar', - per: ['key'], - attributes: { - stroke: null - } + var chartSettings = { + y: { + column: outcome === 'rate' ? 'prop_n' : 'n', + type: 'linear', + label: '', + format: outcome === 'rate' ? '0.1%' : 'd', + domain: [0, null] + }, + x: { + column: 'key', + type: 'ordinal', + label: '' + }, + marks: [ + { + type: 'bar', + per: ['key'], + attributes: { + stroke: null } - ], - colors: ['#999'], - gridlines: '', - resizable: false, - height: this_.height, - margin: this_.margin, - value_col: d.value_col, - group_col: d.group || null, - group_label: d.groupLabel || null, - overall: d.statistics.values - }), - defineProperty(_chartSettings, 'gridlines', 'y'), - defineProperty(_chartSettings, 'sort', sortType), - defineProperty(_chartSettings, 'chartType', d.chartType), - _chartSettings - ); + } + ], + colors: ['#999'], + gridlines: 'y', + resizable: false, + height: this_.height, + margin: this_.margin, + value_col: d.value_col, + group_col: d.group || null, + group_label: d.groupLabel || null, + overall: d.statistics.values, + sort: sortType, //Alphabetical, Ascending, Descending + chartType: d.chartType + }; chartSettings.margin.bottom = 10; @@ -1096,7 +1256,7 @@ d.statistics.highlightValues.forEach(function(d) { d.type = 'sub'; }); - chartData = d3.merge([chartData, d.statistics.highlightValues]); + chartData = d3$1.merge([chartData, d.statistics.highlightValues]); chartSettings.marks[0].per = ['key', 'type']; chartSettings.marks[0].arrange = 'nested'; @@ -1106,8 +1266,8 @@ if (d.groups) { //Set upper limit of y-axis domain to the maximum group rate. - chartSettings.y.domain[1] = d3.max(d.groups, function(di) { - return d3.max(di.statistics.values, function(dii) { + chartSettings.y.domain[1] = d3$1.max(d.groups, function(di) { + return d3$1.max(di.statistics.values, function(dii) { return dii[chartSettings.y.column]; }); }); @@ -1126,7 +1286,10 @@ group.statistics.highlightValues.forEach(function(d) { d.type = 'sub'; }); - group.data = d3.merge([group.data, group.statistics.highlightValues]); + group.data = d3$1.merge([ + group.data, + group.statistics.highlightValues + ]); group.chartSettings.marks[0].per = ['key', 'type']; group.chartSettings.marks[0].arrange = 'nested'; @@ -1144,7 +1307,7 @@ if (group.data.length) group.chart.init(group.data); else { - d3 + d3$1 .select(chartContainer) .append('p') .text( @@ -1156,7 +1319,7 @@ ')' ); - d3 + d3$1 .select(chartContainer) .append('div') .html('No data available for this level..

'); @@ -1172,7 +1335,7 @@ } function createVerticalBarsControls(this_, d) { - var controlsContainer = d3 + var controlsContainer = d3$1 .select(this_) .append('div') .classed('row-controls', true); @@ -1194,8 +1357,14 @@ }); outcomeSelect.on('change', function() { - d3.select(this_).selectAll('.wc-chart').remove(); - d3.select(this_).selectAll('.panel-label').remove(); + d3$1 + .select(this_) + .selectAll('.wc-chart') + .remove(); + d3$1 + .select(this_) + .selectAll('.panel-label') + .remove(); createVerticalBars(this_, d); }); @@ -1214,8 +1383,14 @@ }); x_sort.on('change', function() { - d3.select(this_).selectAll('.wc-chart').remove(); - d3.select(this_).selectAll('.panel-label').remove(); + d3$1 + .select(this_) + .selectAll('.wc-chart') + .remove(); + d3$1 + .select(this_) + .selectAll('.panel-label') + .remove(); createVerticalBars(this_, d); }); } @@ -1289,7 +1464,7 @@ }); rateLine .append('title') - .text('Overall rate: ' + d3.format(chart.config.x.format)(x)); + .text('Overall rate: ' + d3$1.format(chart.config.x.format)(x)); } } }); @@ -1306,8 +1481,8 @@ }) .forEach(function(d) { var overall = chart.config.overall.filter(function(di) { - return di.key === d.values.raw[0].key; - })[0], + return di.key === d.values.raw[0].key; + })[0], g = chart.svg .append('g') .classed('difference-from-total', true) @@ -1333,7 +1508,7 @@ .append('title') .text( 'Difference from overall rate: ' + - d3.format('.1f')((d.total - x) * 100) + d3$1.format('.1f')((d.total - x) * 100) ); var diffText = g .append('text') @@ -1347,7 +1522,7 @@ .text( '' + (x < d.total ? '+' : x > d.total ? '-' : '') + - d3.format('.1f')(Math.abs(d.total - x) * 100) + d3$1.format('.1f')(Math.abs(d.total - x) * 100) ); }); @@ -1356,7 +1531,7 @@ .on('mouseover', function() { chart.svg.selectAll('.difference-from-total').style('display', 'block'); chart.svg.selectAll('.difference-from-total text').each(function() { - d3.select(this).attr('dy', this.getBBox().height / 4); + d3$1.select(this).attr('dy', this.getBBox().height / 4); }); }) .on('mouseout', function() { @@ -1367,8 +1542,6 @@ } function onResize$1() { - var context = this; - moveYaxis$1(this); if (this.config.x.column === 'prop_n') { drawOverallMark(this); @@ -1384,14 +1557,14 @@ } function createHorizontalBars(this_, d) { - var rowSelector = d3.select(this_).node().parentNode, - outcome = d3 + var rowSelector = d3$1.select(this_).node().parentNode, + outcome = d3$1 .select(rowSelector) .select('.row-controls .x-axis-outcome select') .property('value'), custom_height = d.statistics.values.length * 20 + 35, // let height vary based on the number of levels; 35 ~= top and bottom margin - chartContainer = d3.select(this_).node(), + chartContainer = d3$1.select(this_).node(), chartSettings = { x: { column: outcome === 'rate' ? 'prop_n' : 'n', @@ -1429,7 +1602,11 @@ var chartData = d.statistics.values.sort(function(a, b) { return a.prop_n > b.prop_n ? -2 - : a.prop_n < b.prop_n ? 2 : a.key < b.key ? -1 : 1; + : a.prop_n < b.prop_n + ? 2 + : a.key < b.key + ? -1 + : 1; }); // sort data by descending rate and keep only the first five categories. chartSettings.y.order = chartData @@ -1447,7 +1624,7 @@ d.statistics.highlightValues.forEach(function(d) { d.type = 'sub'; }); - chartData = d3.merge([chartData, d.statistics.highlightValues]); + chartData = d3$1.merge([chartData, d.statistics.highlightValues]); chartSettings.marks[0].per = ['key', 'type']; chartSettings.marks[0].arrange = 'nested'; @@ -1457,8 +1634,8 @@ if (d.groups) { //Set upper limit of x-axis domain to the maximum group rate. - chartSettings.x.domain[1] = d3.max(d.groups, function(di) { - return d3.max(di.statistics.values, function(dii) { + chartSettings.x.domain[1] = d3$1.max(d.groups, function(di) { + return d3$1.max(di.statistics.values, function(dii) { return dii[chartSettings.x.column]; }); }); @@ -1477,7 +1654,11 @@ .sort(function(a, b) { return a.prop_n > b.prop_n ? -2 - : a.prop_n < b.prop_n ? 2 : a.key < b.key ? -1 : 1; + : a.prop_n < b.prop_n + ? 2 + : a.key < b.key + ? -1 + : 1; }); group.data.forEach(function(d) { @@ -1487,7 +1668,10 @@ group.statistics.highlightValues.forEach(function(d) { d.type = 'sub'; }); - group.data = d3.merge([group.data, group.statistics.highlightValues]); + group.data = d3$1.merge([ + group.data, + group.statistics.highlightValues + ]); group.chartSettings.marks[0].per = ['key', 'type']; group.chartSettings.marks[0].arrange = 'nested'; @@ -1505,7 +1689,7 @@ if (group.data.length) group.chart.init(group.data); else { - d3 + d3$1 .select(chartContainer) .append('p') .text( @@ -1516,7 +1700,7 @@ group.chartSettings.n + ')' ); - d3 + d3$1 .select(chartContainer) .append('div') .html('All values missing in this group..

'); @@ -1576,7 +1760,9 @@ 'stroke-width': '2px', 'stroke-opacity': '1' }); - rateLine.append('title').text('Overall rate: ' + d3.format('.1%')(x)); + rateLine + .append('title') + .text('Overall rate: ' + d3$1.format('.1%')(x)); } } }); @@ -1595,9 +1781,9 @@ .append('line') .classed('legend-mark', true) .attr({ - x1: 3 * BBox.width / 4, + x1: (3 * BBox.width) / 4, y1: 0, - x2: 3 * BBox.width / 4, + x2: (3 * BBox.width) / 4, y2: BBox.height }) .style({ @@ -1621,12 +1807,12 @@ } function createDotPlot(this_, d) { - var rowSelector = d3.select(this_).node().parentNode, - outcome = d3 + var rowSelector = d3$1.select(this_).node().parentNode, + outcome = d3$1 .select(rowSelector) .select('.row-controls .x-axis-outcome select') .property('value'), - chartContainer = d3.select(this_).node(), + chartContainer = d3$1.select(this_).node(), chartSettings = { x: { column: outcome === 'rate' ? 'prop_n' : 'n', @@ -1662,7 +1848,11 @@ .sort(function(a, b) { return a.prop_n > b.prop_n ? -2 - : a.prop_n < b.prop_n ? 2 : a.key < b.key ? -1 : 1; + : a.prop_n < b.prop_n + ? 2 + : a.key < b.key + ? -1 + : 1; }) .slice(0, 5); // sort data by descending rate and keep only the first five categories. @@ -1687,7 +1877,11 @@ .sort(function(a, b) { return a.prop_n > b.prop_n ? -2 - : a.prop_n < b.prop_n ? 2 : a.key < b.key ? -1 : 1; + : a.prop_n < b.prop_n + ? 2 + : a.key < b.key + ? -1 + : 1; }) .forEach(function(value) { value.group = group.group; @@ -1730,7 +1924,7 @@ } function createHorizontalBarsControls(this_, d) { - var controlsContainer = d3 + var controlsContainer = d3$1 .select(this_) .append('div') .classed('row-controls', true); @@ -1752,8 +1946,14 @@ }); outcomeSelect.on('change', function() { - d3.select(this_).selectAll('.wc-chart').remove(); - d3.select(this_).selectAll('.panel-label').remove(); + d3$1 + .select(this_) + .selectAll('.wc-chart') + .remove(); + d3$1 + .select(this_) + .selectAll('.panel-label') + .remove(); if (type_control.property('value') === 'Paneled (Bar Charts)') { createHorizontalBars(this_, d); } else { @@ -1779,8 +1979,14 @@ }); type_control.on('change', function() { - d3.select(this_).selectAll('.wc-chart').remove(); - d3.select(this_).selectAll('.panel-label').remove(); + d3$1 + .select(this_) + .selectAll('.wc-chart') + .remove(); + d3$1 + .select(this_) + .selectAll('.panel-label') + .remove(); if (this.value == 'Paneled (Bar Charts)') { createHorizontalBars(this_, d); } else { @@ -1789,31 +1995,6 @@ }); } - if (typeof Object.assign != 'function') { - (function() { - Object.assign = function(target) { - 'use strict'; - if (target === undefined || target === null) - throw new TypeError('Cannot convert undefined or null to object'); - - var output = Object(target); - - for (var index = 1; index < arguments.length; index++) { - var source = arguments[index]; - - if (source !== undefined && source !== null) { - for (var nextKey in source) { - if (source.hasOwnProperty(nextKey)) - output[nextKey] = source[nextKey]; - } - } - } - - return output; - }; - })(); - } - var defaultSettings = //Custom settings { @@ -1857,8 +2038,8 @@ aspect: 12, margin: { right: 25, - left: 100 - } // space for panel value + left: 100 // space for panel value + } }; //Replicate settings in multiple places in the settings object. @@ -1876,15 +2057,16 @@ } function makeTooltip$1(d, i, context) { - var format$$1 = d3.format(context.config.measureFormat), + var format$$1 = d3$1.format(context.config.measureFormat), offset = context.plot_width / context.config.x.bin / 2 + 8; d.midpoint = (d.rangeHigh + d.rangeLow) / 2; d.range = format$$1(d.rangeLow) + '-' + format$$1(d.rangeHigh); d.selector = 'bar' + i; d.side = context.x(d.midpoint) < context.plot_width / 2 ? 'left' : 'right'; - d.xPosition = d.side === 'left' - ? context.x(d.midpoint) + offset - : context.x(d.midpoint) - offset; + d.xPosition = + d.side === 'left' + ? context.x(d.midpoint) + offset + : context.x(d.midpoint) - offset; //Define tooltips. var tooltip = context.svg.append('g').attr('id', d.selector), @@ -1968,14 +2150,14 @@ function addHighlightMarks(chart) { //add highlights for each bar (if any exist) var bars = chart.svg.selectAll('g.bar-group').each(function(d) { - var highlightCount = d3.sum(d.values.raw, function(d) { + var highlightCount = d3$1.sum(d.values.raw, function(d) { return d.highlighted ? 1 : 0; }); //Clone the rect (if there are highlights) if (highlightCount > 0) { - var rect = d3.select(this).select('rect'); + var rect = d3$1.select(this).select('rect'); var rectNode = rect.node(); - var highlightRect = d3.select(this).append('rect'); + var highlightRect = d3$1.select(this).append('rect'); highlightRect .attr('x', chart.x(d.rangeLow) + 1) @@ -1988,7 +2170,7 @@ } function addBoxPlot(chart) { - var format$$1 = d3.format(chart.config.measureFormat); + var format$$1 = d3$1.format(chart.config.measureFormat); //Annotate quantiles if (chart.config.boxPlot) { @@ -2002,7 +2184,7 @@ for (var item in quantiles) { var quantile$$1 = quantiles[item]; - quantile$$1.quantile = d3.quantile( + quantile$$1.quantile = d3$1.quantile( chart.values, quantile$$1.probability ); @@ -2010,7 +2192,7 @@ //Horizontal lines if ([0.05, 0.75].indexOf(quantile$$1.probability) > -1) { var rProbability = quantiles[+item + 1].probability; - var rQuantile = d3.quantile(chart.values, rProbability); + var rQuantile = d3$1.quantile(chart.values, rProbability); var whisker = chart.svg .append('line') .attr({ @@ -2041,7 +2223,7 @@ //Box if (quantile$$1.probability === 0.25) { - var q3 = d3.quantile(chart.values, 0.75); + var q3 = d3$1.quantile(chart.values, 0.75); var interQ = chart.svg .append('rect') .attr({ @@ -2076,11 +2258,12 @@ y2: chart.plot_height + chart.config.boxPlotHeight }) .style({ - stroke: [0.05, 0.95].indexOf(quantile$$1.probability) > -1 - ? 'black' - : [0.25, 0.75].indexOf(quantile$$1.probability) > -1 + stroke: + [0.05, 0.95].indexOf(quantile$$1.probability) > -1 ? 'black' - : 'black', + : [0.25, 0.75].indexOf(quantile$$1.probability) > -1 + ? 'black' + : 'black', 'stroke-width': '3px' }); quantile$$1.mark @@ -2133,8 +2316,8 @@ //Annotate mean. if (chart.config.mean) { - var mean$$1 = d3.mean(chart.values); - var sd = d3.deviation(chart.values); + var mean$$1 = d3$1.mean(chart.values); + var sd = d3$1.deviation(chart.values); var meanMark = chart.svg .append('circle') .attr({ @@ -2168,16 +2351,14 @@ chart.svg .on('mousemove', function() { //Highlight closest bar. - var mouse$$1 = d3.mouse(this); + var mouse$$1 = d3$1.mouse(this); var x = chart.x.invert(mouse$$1[0]); var y = chart.y.invert(mouse$$1[1]); var minimum = void 0; - var bar = {}; bars.each(function(d, i) { d.distance = Math.abs(d.midpoint - x); if (i === 0 || d.distance < minimum) { minimum = d.distance; - bar = d; } }); var closest = bars @@ -2277,7 +2458,7 @@ //Define x-axis domain as the range of the measure, regardless of subgrouping. if (!this.initialSettings.xDomain) { - this.initialSettings.xDomain = d3.extent(this.values); + this.initialSettings.xDomain = d3$1.extent(this.values); } this.config.x.domain = this.initialSettings.xDomain; @@ -2290,14 +2471,14 @@ //in a single bin within a subgrouping. var max$$1 = 0; if (!config.y.domain[1]) { - var nestedData = d3 + var nestedData = d3$1 .nest() .key(function(d) { return d[panel]; }) .entries(context.raw_data); nestedData.forEach(function(group) { - var domain = d3.extent(group.values, function(d) { + var domain = d3$1.extent(group.values, function(d) { return +d[measure]; }); var binWidth = (domain[1] - domain[0]) / config.nBins; @@ -2306,7 +2487,7 @@ Math.floor((+d[measure] - domain[0]) / binWidth) - (+d[measure] === domain[1]) * 1; }); - var bins = d3 + var bins = d3$1 .nest() .key(function(d) { return d.bin; @@ -2317,7 +2498,7 @@ .entries(group.values); max$$1 = Math.max( max$$1, - d3.max(bins, function(d) { + d3$1.max(bins, function(d) { return d.values; }) ); @@ -2325,7 +2506,7 @@ } //Plot the chart for each group. - var groups = d3 + var groups = d3$1 .set( context.raw_data.map(function(d) { return d[panel]; @@ -2348,7 +2529,7 @@ }); group.settings.xDomain = config.commonScale ? config.xDomain - : d3.extent(group.data, function(d) { + : d3$1.extent(group.data, function(d) { return +d[measure]; }); group.settings.x.domain = group.settings.xDomain; @@ -2387,7 +2568,7 @@ } function createHistogramBoxPlot(this_, d) { - var chartContainer = d3.select(this_).node(); + var chartContainer = d3$1.select(this_).node(); var chartSettings = { measure: ' ', resizable: false, @@ -2428,7 +2609,7 @@ } function createHistogramBoxPlotControls(this_, d) { - var controlsContainer = d3 + var controlsContainer = d3$1 .select(this_) .append('div') .classed('row-controls', true); @@ -2445,8 +2626,14 @@ .attr('checked', true); commonScaleCheckbox.on('change', function() { - d3.select(this_).selectAll('.wc-chart').remove(); - d3.select(this_).selectAll('.panel-label').remove(); + d3$1 + .select(this_) + .selectAll('.wc-chart') + .remove(); + d3$1 + .select(this_) + .selectAll('.panel-label') + .remove(); d.commonScale = this.checked; createHistogramBoxPlot(this_, d); }); @@ -2485,7 +2672,7 @@ console.warn('Invalid chart type for ' + d.key); } } else { - d3 + d3$1 .select(this) .append('div') .attr('class', 'missingText') @@ -2494,8 +2681,6 @@ } function renderValues(d, list) { - list.selectAll('*').remove(); - //make a list of values if (d.type == 'categorical') { var topValues = d.statistics.values @@ -2507,21 +2692,26 @@ }); var valueItems = list - .selectAll('li') + .selectAll('li.value') .data(topValues) .enter() - .append('li'); + .append('li') + .attr('class', 'value'); valueItems .append('div') .text(function(d) { return d.key; }) - .attr('class', 'wcb-label'); + .attr('class', 'wcb-label') + .attr('title', function(d) { + return d.key; + }); + valueItems .append('div') .text(function(d) { - return d.n + ' (' + d3.format('0.1%')(d.prop_n) + ')'; + return d.n + ' (' + d3$1.format('0.1%')(d.prop_n) + ')'; }) .attr('class', 'value'); @@ -2530,12 +2720,13 @@ var extraCount = totLength - 5; var extra_span = list .append('li') + .attr('class', 'value') .append('div') .attr('class', 'wcb-label') .html('and ' + extraCount + ' more.'); } } else if (d.type == 'continuous') { - var sortedValues = d3 + var sortedValues = d3$1 .set( d.values.map(function(d) { return +d.value; @@ -2554,15 +2745,23 @@ var maxValues = sortedValues.filter(function(d, i) { return i >= nValues - 3; }); - var valList = d3.merge([minValues, ['...'], maxValues]); + var valList = d3$1.merge([minValues, ['...'], maxValues]); } else { var valList = sortedValues; } - var valueItems = list.selectAll('li').data(valList).enter().append('li'); + var valueItems = list + .selectAll('li.value') + .data(valList) + .enter() + .append('li') + .attr('class', 'value'); - valueItems.append('div').attr('class', 'wcb-label').text(function(d, i) { - return i == 0 ? 'Min' : i == valList.length - 1 ? 'Max' : ' '; - }); + valueItems + .append('div') + .attr('class', 'wcb-label') + .text(function(d, i) { + return i == 0 ? 'Min' : i == valList.length - 1 ? 'Max' : ' '; + }); valueItems .append('div') .attr('class', 'value') @@ -2580,9 +2779,17 @@ //Render Summary Stats function renderStats(d, list) { - list.selectAll('*').remove(); + var ignoreStats = [ + 'values', + 'highlightValues', + 'min', + 'max', + 'n', + 'N', + 'nMissing', + 'percentMissing' + ]; - var ignoreStats = ['values', 'highlightValues', 'min', 'max']; var statNames = Object.keys(d.statistics) .filter(function(f) { return ignoreStats.indexOf(f) === -1; @@ -2593,7 +2800,7 @@ var statList = statNames.map(function(stat) { return { - key: stat !== 'nMissing' ? stat : 'Missing', + key: stat !== 'missingSummary' ? stat : 'Missing', value: d.statistics[stat] }; }); @@ -2634,7 +2841,11 @@ //render the items var metaItems = list .selectAll('li.meta') - .data(d.meta) + .data( + d.meta.filter(function(f) { + return f.key != 'Type'; + }) + ) .enter() .append('li') .classed('meta', true) @@ -2658,8 +2869,8 @@ if (dropped.length) { list .append('li') - .append('div') .attr('class', 'details') + .append('div') .html('ⓘ') .property( 'title', @@ -2672,56 +2883,142 @@ } } - function clearDetails(d, list) { - list.selectAll('*').remove(); + function makeDetails(d) { + var stat_list = d3$1 + .select(this) + .append('ul') + .attr('class', 'stats'); + var val_list = d3$1 + .select(this) + .append('ul') + .attr('class', 'values'); + + var parent = d3$1.select(this.parentNode.parentNode); + + //render stats & values on initial load + renderStats(d, stat_list); + renderValues(d, val_list); } - var detailList = [ - { key: 'Stats', action: renderStats }, - { key: 'Meta', action: renderMeta }, - { key: 'Values', action: renderValues }, - { key: 'None', action: clearDetails } - ]; + function makeMeta(d) { + var hasMeta = + d.meta + .filter(function(f) { + return !f.hidden; + }) + .filter(function(f) { + return f.key != 'Type'; + }).length > 0; + if (hasMeta) { + var meta_list = d3$1 + .select(this) + .append('ul') + .attr('class', 'meta'); - function makeDetails(d) { - var list = d3.select(this).append('div').append('ul'); - var parent = d3.select(this.parentNode.parentNode); - var controls = parent - .select('.row-chart') - .select('.row-controls') - .append('div') - .attr('class', 'detail-controls'); + var parent = d3$1.select(this.parentNode.parentNode); + renderMeta(d, meta_list); + } else { + d3$1.select(this).style('display', 'none'); + } + } - controls.append('small').html('Header Details: '); - var detailSelect = controls.append('select'); + function makeHist(this_, d) { + var height = 15, + width = 100; - var detailItems = detailSelect - .selectAll('option') - .data(detailList) + var svg = d3 + .select(this_) + .append('svg') + .attr('height', height) + .attr('width', width) + .style('margin-right', '0.1em'); + + if (d.type == 'categorical') { + var bins = d.statistics.values; + bins.forEach(function(d) { + d.title = d.key + ' - ' + d.n + ' (' + d.prop_n_text + ')'; + d.color = '#999'; + }); + } else if (d.type == 'continuous') { + var values = d.values.map(function(m) { + return +m.value; + }); + var x_linear = d3.scale + .linear() + .domain(d3.extent(values)) + .range([0, width]); + var bins = d3.layout + .histogram() + .bins(x_linear.ticks(50))(values) + .map(function(m, i) { + m.key = '[' + m.x + '-' + (m.x + m.dx) + ')'; + m.n = m.length; + m.title = m.key + ' - ' + m.n; + m.color = 'black'; + return m; + }); + } + + // scales + var x = d3.scale + .ordinal() + .domain( + bins.map(function(d) { + return d.key; + }) + ) + .rangeBands([0, width], 0.1, 0); + + var width = x.rangeBand(); + + var y = d3.scale + .linear() + .domain([ + 0, + d3.max(bins, function(d) { + return d.n; + }) + ]) + .range([height, 0]); + + var bar = svg + .selectAll('.bar') + .data(bins) .enter() - .append('option') - .html(function(d) { - return d.key; + .append('g') + .attr('class', 'bar') + .attr('transform', function(d) { + return 'translate(' + x(d.key) + ',' + y(d.n) + ')'; }); - //Handlers for label events - detailSelect.on('change', function() { - var current = this.value; - var detailObj = detailList.filter(function(f) { - return f.key == current; - })[0]; - detailObj.action(d, list); - }); + bar + .append('rect') + .attr('x', 1) + .attr('width', width) + .attr('height', function(d) { + return height - y(d.n); + }) + .attr('fill', d.type == 'categorical' ? '#999' : 'black') + .append('title') + .text(function(d) { + return d.title; + }); + } - //render stats on initial load - renderStats(d, list); + function createSpark() { + var d = d3.select(this).datum(); + if (d.statistics.n > 0) { + makeHist(this, d); + } } function makeTitle(d) { - var rowDiv = d3.select(this.parentNode.parentNode.parentNode); + var rowDiv = d3$1.select(this.parentNode.parentNode.parentNode); var chartDiv = rowDiv.select('.row-chart'); - var hiddenFlag = rowDiv.classed('hiddenChart'); - d3 + var hiddenFlag = rowDiv.classed('hiddenDetails'); + + //Add row toggle + d3$1 .select(this) .append('div') .attr('class', 'row-toggle') @@ -2730,14 +3027,15 @@ return d.chartVisibility == 'hidden'; }) .on('click', function() { - var rowDiv = d3.select(this.parentNode.parentNode.parentNode); + var rowDiv = d3$1.select(this.parentNode.parentNode.parentNode); var chartDiv = rowDiv.select('.row-chart'); - var hiddenFlag = rowDiv.classed('hiddenChart'); - rowDiv.classed('hiddenChart', !hiddenFlag); - d3.select(this).html(hiddenFlag ? '▼' : '►'); + var hiddenFlag = rowDiv.classed('hiddenDetails'); + rowDiv.classed('hiddenDetails', !hiddenFlag); + d3$1.select(this).html(hiddenFlag ? '▼' : '►'); }); - d3 + //add variable name in quotes + d3$1 .select(this) .append('span') .attr('class', 'title-span') @@ -2745,8 +3043,9 @@ return "'" + d.value_col + "'"; }); + //add variable label (if any) if (d.value_col != d.label) { - d3 + d3$1 .select(this) .append('span') .attr('class', 'label-span') @@ -2754,6 +3053,41 @@ return d.label; }); } + + //add variable type + /* + d3select(this) + .append('span') + .attr('class', 'type') + .text(d => d.type); + */ + + //add sparklines + d3$1 + .select(this) + .append('div') + .attr('class', 'spark') + .datum(d) + .each(createSpark); + + //add percent missing (if > 0%) + d3$1 + .select(this) + .append('span') + .attr('class', 'percent-missing') + .text(function(d) { + return d3$1.format('0.1%')(d.statistics.percentMissing) + ' missing'; + }) + .style('display', function(d) { + return d.statistics.percentMissing == 0 ? 'none' : null; + }) + .style('cursor', 'pointer') + .style('color', function(d) { + return d.statistics.percentMissing >= 0.1 ? 'red' : '#999'; + }) + .attr('title', function(d) { + return d.statistics.nMissing + ' of ' + d.statistics.N + ' missing'; + }); } /*------------------------------------------------------------------------------------------------\ @@ -2761,17 +3095,30 @@ \------------------------------------------------------------------------------------------------*/ function renderRow(d) { - var rowWrap = d3.select(this); + var rowWrap = d3$1.select(this); rowWrap.selectAll('*').remove(); - var rowHead = rowWrap.append('div').attr('class', 'row-head section'); + rowWrap + .append('div') + .attr('class', 'row-head section') + .append('div') + .attr('class', 'row-title') + .each(makeTitle); - rowHead.append('div').attr('class', 'row-title').each(makeTitle); - //rowHead.append('div').attr('class', 'row-values').each(makeValues); + rowWrap + .append('div') + .attr('class', 'row-details section') + .each(makeDetails); - rowWrap.append('div').attr('class', 'row-chart section').each(makeChart); + rowWrap + .append('div') + .attr('class', 'row-chart section') + .each(makeChart); - rowHead.append('div').attr('class', 'row-details').each(makeDetails); + rowWrap + .append('div') + .attr('class', 'row-meta section') + .each(makeMeta); } /*------------------------------------------------------------------------------------------------\ @@ -2811,7 +3158,7 @@ }); } - function init$7(codebook) { + function init$6(codebook) { //indicateLoading(codebook, '.web-codebook .dataListing .wc-chart'); var dataListing = codebook.dataListing; @@ -2820,7 +3167,10 @@ dataListing.wrap.selectAll('*').remove(); //Define table. - dataListing.table = webcharts.createTable('.web-codebook .dataListing', {}); + dataListing.table = webcharts.createTable( + codebook.wrap.select('.dataListing').node(), + {} + ); //Define callback. onDraw(dataListing); @@ -2846,45 +3196,7 @@ Define dataListing object (the meat and potatoes). \------------------------------------------------------------------------------------------------*/ - var dataListing = { init: init$7 }; - - function clone$1(obj) { - var copy = void 0; - - //boolean, number, string, null, undefined - if ( - 'object' != (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) || - null == obj - ) - return obj; - - //date - if (obj instanceof Date) { - copy = new Date(); - copy.setTime(obj.getTime()); - return copy; - } - - //array - if (obj instanceof Array) { - copy = []; - for (var i = 0, len = obj.length; i < len; i++) { - copy[i] = clone$1(obj[i]); - } - return copy; - } - - //object - if (obj instanceof Object) { - copy = {}; - for (var attr in obj) { - if (obj.hasOwnProperty(attr)) copy[attr] = clone$1(obj[attr]); - } - return copy; - } - - throw new Error('Unable to copy [obj]! Its type is not supported.'); - } + var dataListing = { init: init$6 }; var chartMakerSettings = { width: 800, //changed to 300 for paneled charts @@ -3053,7 +3365,7 @@ } else { chartMaker.chartSettings = makeSettings(chartMakerSettings, x_obj, y_obj); chartMaker.chartSettings.width = codebook.config.group ? 320 : 600; - chartMaker.chartData = clone$1(codebook.data.filtered); + chartMaker.chartData = clone(codebook.data.filtered); //flag highlighted rows var highlightedRows = codebook.data.highlighted.map(function(m) { @@ -3065,7 +3377,7 @@ //Define chart. chartMaker.chart = webcharts.createChart( - '.web-codebook .chartMaker.section .cm-chart', + codebook.wrap.select('.chartMaker.section .cm-chart').node(), chartMaker.chartSettings ); @@ -3162,11 +3474,11 @@ /*------------------------------------------------------------------------------------------------\ Initialize detail select \------------------------------------------------------------------------------------------------*/ - function init$9(codebook) { + function init$8(codebook) { initAxisSelect(codebook); } - function init$8(codebook) { + function init$7(codebook) { var chartMaker = codebook.chartMaker; chartMaker.codebook = codebook; chartMaker.config = codebook.config; @@ -3181,7 +3493,7 @@ .attr('class', 'cm-chart'); if (codebook.data.summary.length > 2) { - init$9(codebook); //make controls + init$8(codebook); //make controls chartMaker.draw(codebook); //draw the initial codebook } else { chartMaker.wrap @@ -3197,7 +3509,7 @@ var chartMaker = { draw: draw$1, - init: init$8 + init: init$7 }; var defaultSettings$1 = { @@ -3242,11 +3554,12 @@ }); //autofilter - don't use automatic filter if user specifies filters object - codebook.config.autofilter = codebook.config.filters.length > 0 - ? false - : codebook.config.autofilter == null - ? defaultSettings$1.autofilter - : codebook.config.autofilter; + codebook.config.autofilter = + codebook.config.filters.length > 0 + ? false + : codebook.config.autofilter == null + ? defaultSettings$1.autofilter + : codebook.config.autofilter; /********************* Group Settings *********************/ codebook.config.groups = codebook.config.groups || defaultSettings$1.groups; @@ -3262,8 +3575,8 @@ codebook.config.variableLabels = codebook.config.variableLabels.filter( function(label, i) { var is_object = - (typeof label === 'undefined' ? 'undefined' : _typeof(label)) === - 'object', + (typeof label === 'undefined' ? 'undefined' : _typeof(label)) === + 'object', has_value_col = label.hasOwnProperty('value_col'), has_label = label.hasOwnProperty('label'), legit = is_object && has_value_col && has_label; @@ -3301,11 +3614,12 @@ : defaultSettings$1.variableLabels; } //autogroups - don't use automatic groups if user specifies groups object - codebook.config.autogroups = codebook.config.groups.length > 0 - ? false - : codebook.config.autogroups == null - ? defaultSettings$1.autogroups - : codebook.config.autogroups; + codebook.config.autogroups = + codebook.config.groups.length > 0 + ? false + : codebook.config.autogroups == null + ? defaultSettings$1.autogroups + : codebook.config.autogroups; /********************* Hidden Variable Settings ***************/ codebook.config.hiddenVariables = @@ -3314,9 +3628,10 @@ /********************* Histogram Settings *********************/ codebook.config.nBins = codebook.config.nBins || defaultSettings$1.nBins; - codebook.config.autobins = codebook.config.autobins == null - ? defaultSettings$1.autobins - : codebook.config.autobins; + codebook.config.autobins = + codebook.config.autobins == null + ? defaultSettings$1.autobins + : codebook.config.autobins; codebook.config.levelSplit = codebook.config.levelSplit || defaultSettings$1.levelSplit; @@ -3356,8 +3671,8 @@ codebook.config.chartVisibility || defaultSettings$1.chartVisibility; //hide the controls appropriately according to the start tab - if (codebook.config.controlVisibility != 'disabled') { - var startTab = availableTabs.filter(function(f) { + if (codebook.config.controlVisibility !== 'disabled') { + var startTab = availableTabs.find(function(f) { return f.key == codebook.config.defaultTab; }); codebook.config.controlVisibility = startTab.controls @@ -3447,11 +3762,12 @@ var range = +summaryData.statistics['max'] - +summaryData.statistics['min']; var binSize = FreedmanDiaconis(IQR, n); - var bins = binSize > 0 - ? Math.ceil(range / binSize) - : codebook.config.nBins > 0 - ? codebook.config.nBins - : defaultSettings$1.nBins; + var bins = + binSize > 0 + ? Math.ceil(range / binSize) + : codebook.config.nBins > 0 + ? codebook.config.nBins + : defaultSettings$1.nBins; return bins; } @@ -3509,7 +3825,7 @@ var numericValues = nonMissingValues.filter(function(d) { return !isNaN(+d.value); }); - var distinctValues = d3 + var distinctValues = d3$1 .set( numericValues.map(function(d) { return d.value; @@ -3531,7 +3847,15 @@ }); statistics.n = nonMissing.length; statistics.nMissing = vector.length - statistics.n; - statistics.values = d3 + statistics.percentMissing = statistics.nMissing / statistics.N; + statistics.missingSummary = + statistics.nMissing + + '/' + + statistics.N + + ' (' + + d3$1.format('0.1%')(statistics.percentMissing) + + ')'; + statistics.values = d3$1 .nest() .key(function(d) { return d.value; @@ -3541,8 +3865,8 @@ n: d.length, prop_N: d.length / statistics.N, prop_n: d.length / statistics.n, - prop_N_text: d3.format('0.1%')(d.length / statistics.N), - prop_n_text: d3.format('0.1%')(d.length / statistics.n), + prop_N_text: d3$1.format('0.1%')(d.length / statistics.N), + prop_n_text: d3$1.format('0.1%')(d.length / statistics.n), indexes: d.map(function(di) { return di.index; }) @@ -3551,7 +3875,7 @@ }) .entries(nonMissing); - statistics.Unique = d3 + statistics.Unique = d3$1 .set( vector.map(function(d) { return d.value; @@ -3567,7 +3891,7 @@ }); if (sub) { - statistics.highlightValues = d3 + statistics.highlightValues = d3$1 .nest() .key(function(d) { return d.value; @@ -3577,8 +3901,8 @@ n: d.length, prop_N: d.length / statistics.N, prop_n: d.length / statistics.n, - prop_N_text: d3.format('0.1%')(d.length / statistics.N), - prop_n_text: d3.format('0.1%')(d.length / statistics.n), + prop_N_text: d3$1.format('0.1%')(d.length / statistics.N), + prop_n_text: d3$1.format('0.1%')(d.length / statistics.n), indexes: d.map(function(di) { return di.index; }) @@ -3613,8 +3937,16 @@ }); statistics.n = nonMissing.length; statistics.nMissing = vector.length - statistics.n; - statistics.mean = d3.format('0.2f')(d3.mean(nonMissing)); - statistics.SD = d3.format('0.2f')(d3.deviation(nonMissing)); + statistics.percentMissing = statistics.nMissing / statistics.N; + statistics.missingSummary = + statistics.nMissing + + '/' + + statistics.N + + ' (' + + d3$1.format('0.1%')(statistics.percentMissing) + + ')'; + statistics.mean = d3$1.format('0.2f')(d3$1.mean(nonMissing)); + statistics.SD = d3$1.format('0.2f')(d3$1.deviation(nonMissing)); var quantiles = [ ['min', 0], ['5th percentile', 0.05], @@ -3626,8 +3958,8 @@ ]; quantiles.forEach(function(quantile$$1) { var statistic = quantile$$1[0]; - statistics[statistic] = d3.format('0.1f')( - d3.quantile(nonMissing, quantile$$1[1]) + statistics[statistic] = d3$1.format('0.1f')( + d3$1.quantile(nonMissing, quantile$$1[1]) ); }); @@ -3643,12 +3975,12 @@ .sort(function(a, b) { return a - b; }); - statistics.mean_sub = d3.format('0.2f')(d3.mean(sub_vector)); - statistics.SD_sub = d3.format('0.2f')(d3.deviation(sub_vector)); + statistics.mean_sub = d3$1.format('0.2f')(d3$1.mean(sub_vector)); + statistics.SD_sub = d3$1.format('0.2f')(d3$1.deviation(sub_vector)); quantiles.forEach(function(quantile$$1) { var statistic = quantile$$1[0]; - statistics[statistic + '_sub'] = d3.format('0.1f')( - d3.quantile(sub_vector, quantile$$1[1]) + statistics[statistic + '_sub'] = d3$1.format('0.1f')( + d3$1.quantile(sub_vector, quantile$$1[1]) ); }); } @@ -3693,15 +4025,16 @@ variables[i].chartVisibility = codebook.config.chartVisibility; //get variable label - variables[i].label = codebook.config.variableLabels - .map(function(variableLabel) { - return variableLabel.value_col; - }) - .indexOf(variable) > -1 - ? codebook.config.variableLabels.filter(function(variableLabel) { - return variableLabel.value_col === variable; - })[0].label - : variable; + variables[i].label = + codebook.config.variableLabels + .map(function(variableLabel) { + return variableLabel.value_col; + }) + .indexOf(variable) > -1 + ? codebook.config.variableLabels.filter(function(variableLabel) { + return variableLabel.value_col === variable; + })[0].label + : variable; // Add metadata Object variables[i].meta = [{ key: 'Type', value: variables[i].type }]; @@ -3719,41 +4052,45 @@ } //calculate variable statistics (including for highlights - if any) - var sub = codebook.data.highlighted.length > 0 - ? function(d) { - return d.highlighted; - } - : null; - variables[i].statistics = variables[i].type === 'continuous' - ? summarize.continuous(variables[i].values, sub) - : summarize.categorical(variables[i].values, sub); + var sub = + codebook.data.highlighted.length > 0 + ? function(d) { + return d.highlighted; + } + : null; + variables[i].statistics = + variables[i].type === 'continuous' + ? summarize.continuous(variables[i].values, sub) + : summarize.categorical(variables[i].values, sub); //get chart type - variables[i].chartType = variables[i].type == 'continuous' - ? 'histogramBoxPlot' - : (variables[i].type == 'categorical') & + variables[i].chartType = + variables[i].type == 'continuous' + ? 'histogramBoxPlot' + : (variables[i].type == 'categorical') & (variables[i].statistics.values.length > codebook.config.levelSplit) - ? 'verticalBars' - : (variables[i].type == 'categorical') & + ? 'verticalBars' + : (variables[i].type == 'categorical') & (variables[i].statistics.values.length <= codebook.config.levelSplit) - ? 'horizontalBars' - : 'error'; + ? 'horizontalBars' + : 'error'; //Handle groups. if (group) { variables[i].group = group; - variables[i].groupLabel = codebook.config.variableLabels - .map(function(variableLabel) { - return variableLabel.value_col; - }) - .indexOf(group) > -1 - ? codebook.config.variableLabels.filter(function(variableLabel) { - return variableLabel.value_col === group; - })[0].label - : group; - variables[i].groups = d3 + variables[i].groupLabel = + codebook.config.variableLabels + .map(function(variableLabel) { + return variableLabel.value_col; + }) + .indexOf(group) > -1 + ? codebook.config.variableLabels.filter(function(variableLabel) { + return variableLabel.value_col === group; + })[0].label + : group; + variables[i].groups = d3$1 .set( data.map(function(d) { return d[group]; @@ -3806,7 +4143,7 @@ makeSummary: makeSummary }; - function init$10(codebook) { + function init$9(codebook) { indicateLoading(codebook, '.web-codebook .settings .column-table'); codebook.settings.layout(codebook); @@ -3837,31 +4174,34 @@ //redraw data summary, codebook, and listing. codebook.data.makeSummary(codebook); - codebook.title.updateColumnCount(codebook); + codebook.title.updateCountSummary(codebook); codebook.summaryTable.draw(codebook); codebook.dataListing.init(codebook); codebook.chartMaker.init(codebook); - codebook.controls.updateRowCount(codebook); } ); } function updateSettings(codebook, column) { - var setting = column === 'Label' - ? 'variableLabels' - : column === 'Group' - ? 'groups' - : column === 'Filter' - ? 'filters' - : column === 'Hide' - ? 'hiddenVariables' - : console.warn('Something unsetting has occurred...'); + var setting = + column === 'Label' + ? 'variableLabels' + : column === 'Group' + ? 'groups' + : column === 'Filter' + ? 'filters' + : column === 'Hide' + ? 'hiddenVariables' + : console.warn('Something unsetting has occurred...'); var inputs = codebook.settings.wrap.selectAll('.column-table td.' + column); if (['Group', 'Filter', 'Hide'].indexOf(column) > -1) { //redefine settings array codebook.config[setting] = inputs .filter(function() { - return d3.select(this).select('input').property('checked'); + return d3$1 + .select(this) + .select('input') + .property('checked'); }) .data() .map(function(d) { @@ -3871,7 +4211,10 @@ //redefine settings array codebook.config[setting] = inputs .filter(function(d) { - d.value.label = d3.select(this).select('input').property('value'); + d.value.label = d3$1 + .select(this) + .select('input') + .property('value'); return d.value.label !== ''; }) .data() @@ -3887,8 +4230,8 @@ function layout$1(codebook) { //Create list of columns in the data file. var columns = codebook.data.summary.map(function(d) { - return d.value_col; - }), + return d.value_col; + }), groupColumns = codebook.config.groups.map(function(d) { return d.value_col; }), @@ -3905,10 +4248,11 @@ Column: column, Label: { type: 'text', - label: labeledColumns.indexOf(column) > -1 - ? codebook.config.variableLabels[labeledColumns.indexOf(column)] - .label - : '' + label: + labeledColumns.indexOf(column) > -1 + ? codebook.config.variableLabels[labeledColumns.indexOf(column)] + .label + : '' }, Group: { type: 'checkbox', @@ -3966,7 +4310,7 @@ return d.key; }) .each(function(d, i) { - var cell = d3.select(this); + var cell = d3$1.select(this); switch (d.key) { case 'Column': @@ -4010,11 +4354,11 @@ \------------------------------------------------------------------------------------------------*/ var settings = { - init: init$10, + init: init$9, layout: layout$1 }; - function init$11(codebook) { + function init$10(codebook) { codebook.title.fileWrap = codebook.title.wrap .append('span') .attr('class', 'file') @@ -4024,22 +4368,79 @@ : 'Codebook' ); - codebook.title.countSpan = codebook.title.wrap + codebook.title.countSummary = codebook.title.wrap .append('span') - .attr('class', 'columnCount'); + .attr('class', 'countSummary'); + + codebook.title.highlight.init(codebook); + + codebook.title.updateCountSummary(codebook); + } + + /*------------------------------------------------------------------------------------------------\ + Initialize clear highlighting button. +\------------------------------------------------------------------------------------------------*/ + + function init$11(codebook) { + //initialize the wrapper + codebook.title.highlight.clearButton = codebook.title.wrap + .append('button') + .classed('clear-highlight', true) + .classed('hidden', codebook.data.highlighted.length == 0) + .text('Clear Highlighting') + .on('click', function() { + codebook.data.highlighted = []; - codebook.title.updateColumnCount(codebook); + codebook.data.makeSummary(codebook); + codebook.dataListing.init(codebook); + codebook.summaryTable.draw(codebook); + codebook.chartMaker.draw(codebook); + codebook.title.updateCountSummary(codebook); + }); } - function updateColumnCount(codebook) { + /*------------------------------------------------------------------------------------------------\ + Define clear highlighting button object. +\------------------------------------------------------------------------------------------------*/ + + var highlight = { init: init$11 }; + + function updateCountSummary(codebook) { + if (codebook.data.summary.length > 0) { + var nShown = codebook.data.summary[0].statistics.N; + var nTot = codebook.data.raw.length; + var percent = d3$1.format('0.1%')(nShown / nTot); + var rowSummary = + nShown + ' of ' + nTot + ' (' + percent + ') rows selected'; + } else { + var rowSummary = 'No rows selected.'; + } + + //Add note regarding highlighted cells and show/hide the clear highlight button + var highlightSummary = + codebook.data.highlighted.length > 0 + ? ' and ' + + codebook.data.highlighted.length + + ' highlighted. ' + : '.'; + + codebook.title.highlight.clearButton.classed( + 'hidden', + codebook.data.highlighted.length == 0 + ); + + //get number of columns hidden var nCols_sub = codebook.data.summary.filter(function(d) { return !d.hidden; }).length; var nCols_all = codebook.data.summary.length - 1; //-1 is for the index var - var percent = d3.format('0.1%')(nCols_sub / nCols_all); - var tableSummary = - nCols_sub + ' of ' + nCols_all + ' (' + percent + ') columns selected.'; - codebook.title.countSpan.text(tableSummary); + var nCols_diff = nCols_all - nCols_sub; + //var percent = d3format('0.1%')(nCols_sub / nCols_all); + var colSummary = nCols_diff > 0 ? nCols_diff + ' columns hidden' : ''; + + var tableSummary = rowSummary + highlightSummary + ' ' + colSummary; + + codebook.title.countSummary.html(tableSummary); } /*------------------------------------------------------------------------------------------------\ @@ -4047,8 +4448,9 @@ \------------------------------------------------------------------------------------------------*/ var title = { - init: init$11, - updateColumnCount: updateColumnCount + init: init$10, + highlight: highlight, + updateCountSummary: updateCountSummary }; function init$12(codebook) { @@ -4068,67 +4470,26 @@ .attr('class', 'control chart-toggle') .classed('hidden', codebook.config.chartVisibility == 'hidden'); - selector.append('small').text('Toggle Charts: '); + selector.append('small').text('Toggle Details: '); var showAllButton = selector .append('button') - .text('Show All Charts') + .text('Show All Details') .on('click', function() { - codebook.wrap.selectAll('.variable-row').classed('hiddenChart', false); + codebook.wrap + .selectAll('.variable-row') + .classed('hiddenDetails', false); codebook.wrap.selectAll('.row-toggle').html('▼'); }); var hideAllButton = selector .append('button') - .text('Hide All Charts') + .text('Hide All Details') .on('click', function() { - codebook.wrap.selectAll('.variable-row').classed('hiddenChart', true); + codebook.wrap.selectAll('.variable-row').classed('hiddenDetails', true); codebook.wrap.selectAll('.row-toggle').html('►'); }); } - /*------------------------------------------------------------------------------------------------\ - Initialize detail select -\------------------------------------------------------------------------------------------------*/ - //export function init(selector, data, vars, settings) { - function init$14(codebook) { - //initialize the wrapper - var control = codebook.instructions.wrap - .append('span') - .attr('class', 'control detail-select'); - - control.append('small').html('Header Details: '); - var detailSelect = control.append('select'); - - var detailItems = detailSelect - .selectAll('option') - .data(detailList) - .enter() - .append('option') - .html(function(d) { - return d.key; - }); - - //Handlers for label events - detailSelect.on('change', function() { - var current = this.value; - var detailObj = detailList.filter(function(f) { - return f.key == current; - })[0]; - - codebook.wrap.selectAll('.variable-row').each(function(d) { - //show the requested detail for each row - var list = d3.select(this).select('.row-head .row-details ul'); - detailObj.action(d, list); - - //update the select on each row - d3 - .select(this) - .select('.row-chart .row-controls .detail-controls select') - .property('value', current); - }); - }); - } - function update$2(codebook) { var activeTab = codebook.nav.tabs.filter(function(d) { return d.active; @@ -4139,7 +4500,6 @@ //add tab-specific controls if (activeTab.key == 'codebook') { - init$14(codebook); init$13(codebook); } } @@ -4154,9 +4514,10 @@ }; function createCodebook() { - var element = arguments.length > 0 && arguments[0] !== undefined - ? arguments[0] - : 'body'; + var element = + arguments.length > 0 && arguments[0] !== undefined + ? arguments[0] + : 'body'; var config = arguments[1]; var codebook = { @@ -4176,22 +4537,23 @@ settings: settings }; - codebook.events = { + var cbClone = clone(codebook); + cbClone.events = { init: function init$$1() {}, complete: function complete() {} }; - codebook.on = function(event, callback) { + cbClone.on = function(event, callback) { var possible_events = ['init', 'complete']; if (possible_events.indexOf(event) < 0) { return; } if (callback) { - codebook.events[event] = callback; + cbClone.events[event] = callback; } }; - return codebook; + return cbClone; } var defaultSettings$3 = { @@ -4231,8 +4593,7 @@ Initialize explorer \------------------------------------------------------------------------------------------------*/ - function init$15() { - var settings = this.config; + function init$14() { setDefaults$1(this); //prepare to draw the codebook for the first file @@ -4240,7 +4601,7 @@ this.current.event = 'load'; //create wrapper in specified div - this.wrap = d3 + this.wrap = d3$1 .select(this.element) .append('div') .attr('class', 'web-codebook-explorer'); @@ -4267,29 +4628,27 @@ .select('tbody') .selectAll('tr') .filter(function(f) { - return f.raw == explorer.current; + return f.path === explorer.current.path; }) .classed('selected', true); //Linkify the labelColumn var labelCells = this.table + .selectAll('tbody tr') + .on('click', function(d) { + explorer.current = d; + explorer.current.event = 'click'; + explorer.makeCodebook(explorer); + }) .selectAll('td') .filter(function(f) { return f.col == explorer.config.labelColumn; }) - .classed('link', true) - .on('click', function() { - var current_text = d3.select(this).text(); - explorer.current = explorer.config.files.filter(function(f) { - return f[explorer.config.labelColumn] == current_text; - })[0]; - explorer.current.event = 'click'; - explorer.makeCodebook(explorer); - }); + .classed('link', true); }); } - function init$16(explorer) { + function init$15(explorer) { var fileWrap = explorer.codebook.fileListing.wrap; fileWrap.selectAll('*').remove(); //Clear controls. @@ -4331,7 +4690,7 @@ \------------------------------------------------------------------------------------------------*/ var fileListing = { - init: init$16 + init: init$15 }; function makeCodebook(explorer) { @@ -4341,18 +4700,21 @@ //add the Files section to the nav for each config this.current.settings.tabs = this.current.settings.tabs - ? d3.merge([['files'], this.current.settings.tabs]) + ? d3$1.merge([['files'], this.current.settings.tabs]) : ['files', 'codebook', 'listing', 'chartMaker', 'settings']; //set the default tab to the codebook or listing view assuming they are visible if (this.current.event == 'click') { - this.current.settings.defaultTab = this.current.settings.tabs.indexOf( - 'codebook' - ) > -1 - ? 'codebook' - : this.current.settings.tabs.indexOf('listing') > -1 - ? 'listing' - : 'files'; + this.current.settings.defaultTab = + this.current.settings.tabs + .map(function(tab) { + return tab.key ? tab.key : tab; + }) + .indexOf('codebook') > -1 + ? 'codebook' + : this.current.settings.tabs.indexOf('listing') > -1 + ? 'listing' + : 'files'; } this.current.settings.dataName = @@ -4379,7 +4741,7 @@ if (this.current.json) { explorer.codebook.init(this.current.json); } else if (this.current.path) { - d3.csv(this.current.path, function(error, data) { + d3$1.csv(this.current.path, function(error, data) { explorer.codebook.init(data); }); } else { @@ -4395,22 +4757,23 @@ }); //add new files to file list - this.config.files = d3.merge([this.config.files, newFiles]); + this.config.files = d3$1.merge([this.config.files, newFiles]); //re-draw the file listing explorer.codebook.fileListing.table.draw(this.config.files); } function createExplorer() { - var element = arguments.length > 0 && arguments[0] !== undefined - ? arguments[0] - : 'body'; + var element = + arguments.length > 0 && arguments[0] !== undefined + ? arguments[0] + : 'body'; var config = arguments[1]; var explorer = { element: element, config: config, - init: init$15, + init: init$14, layout: layout$2, fileListing: fileListing, makeCodebook: makeCodebook, diff --git a/css/webcodebook.css b/css/webcodebook.css index 85b2f6a..632608a 100644 --- a/css/webcodebook.css +++ b/css/webcodebook.css @@ -89,13 +89,13 @@ font-size: 1.4em; } -.web-codebook .rowCount, .web-codebook .columnCount { +.web-codebook .title .countSummary { padding-left: 1em; font-size: 0.8em; color: #666; } -.web-codebook .rowCount.warn, .web-codebook .columnCount.warn{ +.web-codebook .title .countSummary.warn{ color: red; } @@ -148,7 +148,7 @@ padding-right: 1em; } -.web-codebook .controls button, .web-codebook .instructions button +.web-codebook .controls button, .web-codebook .instructions button, .web-codebook .title button { margin-right: 0.5em; background-color: white; @@ -157,7 +157,9 @@ padding: .2em .4em; } -.web-codebook .controls button: hover, .web-codebook .instructions button: hover{ +.web-codebook .controls button:hover, +.web-codebook .instructions button:hover, +.web-codebook .title button:hover{ cursor: pointer; background-color: #008CBA; color: white; @@ -182,7 +184,7 @@ cursor: pointer; } -.web-codebook .controls span.update-controls: hover +.web-codebook .controls span.update-controls:hover { font-weight: bold; } @@ -192,7 +194,7 @@ } .web-codebook .controls .custom-filters ul li.active > a > label, -.web-codebook .controls .custom-filters ul li: hover > a > label { +.web-codebook .controls .custom-filters ul li:hover > a > label { color: white; } @@ -236,15 +238,20 @@ } .web-codebook .summaryTable .variable-row { + position: relative; margin-top: 0.3em; display: block; border: 1px solid #999; } + .web-codebook .summaryTable .variable-row .section { display: block; + padding-left:0.2em; } -.web-codebook .summaryTable .variable-row.hiddenChart .row-chart { +.web-codebook .summaryTable .variable-row.hiddenDetails .row-chart, +.web-codebook .summaryTable .variable-row.hiddenDetails .row-details, +.web-codebook .summaryTable .variable-row.hiddenDetails .row-meta { display: none; } @@ -266,7 +273,8 @@ Header layout - Toggle \-------------------------------------------------------------------------------------------**/ .web-codebook .summaryTable .variable-row .row-title{ -padding-right: 1em; + padding-right: 1em; + width:100%; } .web-codebook .summaryTable .variable-row .row-title > * { @@ -281,7 +289,7 @@ padding-right: 1em; padding-right: 0.5em; } -.web-codebook .summaryTable .variable-row .row-title .row-toggle: hover{ +.web-codebook .summaryTable .variable-row .row-title .row-toggle:hover{ background: #eee; } @@ -299,69 +307,78 @@ padding-right: 1em; } /**-------------------------------------------------------------------------------------------\ - Header - Details layout + Header - Type Tag \-------------------------------------------------------------------------------------------**/ -.web-codebook .summaryTable .variable-row { - position: relative; +.web-codebook .summaryTable .variable-row .row-title .type{ + background-color:white; + border:1px solid black; + border-radius:2px; + padding: 2px 4px 2px 4px; + margin-left: 0.4em; + font-size: 0.6em; + vertical-align: middle; + float:right; } -.web-codebook .summaryTable .variable-row .type-label{ - display: block; - position: absolute; - right: -2em; - top: 0; - width: 2em +.web-codebook .summaryTable .variable-row .row-title .spark{ + float:right; } -.web-codebook .summaryTable .variable-row .type-label li{ - display: block; - list-style: none; - text-align: left; - background: black; - color: white; - border: 1px solid #999; - border-radius: 0 5px 5px 0; - padding: 2px; - cursor: pointer; - font-size: 0.6em; - opacity: 0.8; -} +/**-------------------------------------------------------------------------------------------\ + Header - Missing Count +\-------------------------------------------------------------------------------------------**/ +.web-codebook .summaryTable .variable-row .row-title .percent-missing { -.web-codebook .summaryTable .variable-row .type-label li.selected{ - background: white; - color: black; - font-weight: bold; + margin-right: 0.4em; + font-size: 0.8em; + vertical-align: middle; + float:right; } -.web-codebook .summaryTable .variable-row .type-label li: hover{ - text-decoration: underline; -} -.web-codebook .summaryTable .variable-row .row-details { - float: right; +/**-------------------------------------------------------------------------------------------\ + Row Details - layout +\-------------------------------------------------------------------------------------------**/ +.web-codebook .summaryTable .variable-row .row-details{ + border-top:1px solid #ccc; + border-bottom:1px solid #ccc; + padding-bottom:0.2em; +} +.web-codebook .summaryTable .variable-row .row-details ul { + display:inline-block; } -.web-codebook .summaryTable .variable-row .row-details ul { - - +.web-codebook .summaryTable .variable-row .row-details ul.values { + float:right; } + .web-codebook .summaryTable .variable-row .row-details ul li { display: inline-block; - padding-right: .5em; vertical-align: top; } .web-codebook .summaryTable .variable-row .row-details ul li div { display: block; text-align: left; + width:100%; + padding-right: .4em; } .web-codebook .summaryTable .variable-row .row-details ul li div.wcb-label { display: inline-block; - padding-right: .2em; - font-size: 0.8em; + font-size: 0.65em; color: #999; min-width: 1em; + max-width: 100px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.web-codebook .summaryTable .variable-row .row-details ul li div.value { + font-size: 0.85em; + color: #333; + vertical-align:bottom; } .web-codebook .summaryTable .variable-row .row-details ul li div.details { @@ -371,41 +388,12 @@ padding-right: 1em; color: blue; } -/**-------------------------------------------------------------------------------------------\ - Header layout - variable summary -\-------------------------------------------------------------------------------------------**/ - - -.web-codebook .summaryTable .variable-row .row-head .row-values .value-list{ - margin-top: 0.1em; - font-size: 0.7em; - list-style: none; -} - -.web-codebook .summaryTable .variable-row .row-head .row-values .value-list li{ - display: inline-block; - padding: 0 0.3em 0 0.3em; - border-right : 1px solid #333; -} - -.web-codebook .summaryTable .variable-row .row-head .row-values .value-list li: first-of-type{ - padding-left: none; -} - -.web-codebook .summaryTable .variable-row .row-head .row-values .value-list li: last-of-type{ - border-right: none; -} /**-------------------------------------------------------------------------------------------\ Chart layout \-------------------------------------------------------------------------------------------**/ -.web-codebook .summaryTable .variable-row .row-controls { - border-bottom: 1px dashed #999; - padding: 0.2em 0em 0.2em 0.4em; -} - .web-codebook .summaryTable .variable-row .row-controls > * { display: inline-block; margin-right: .5em; @@ -424,7 +412,7 @@ padding-right: 1em; fill: orange; } -.web-codebook .summaryTable g.bar-group rect: hover { +.web-codebook .summaryTable g.bar-group rect:hover { cursor: pointer; stroke-width: 3px; stroke: black; @@ -443,6 +431,58 @@ padding-right: 1em; display: block; } +/**-------------------------------------------------------------------------------------------\ + Metadata - layout +\-------------------------------------------------------------------------------------------**/ +.web-codebook .summaryTable .variable-row .row-meta{ + border-top:1px solid #ccc; + padding-bottom:0.2em; +} +.web-codebook .summaryTable .variable-row .row-meta ul { + display:inline-block; +} + +.web-codebook .summaryTable .variable-row .row-meta ul li { + display: inline-block; + background-color:white; + border:1px solid black; + border-radius:2px; + padding: 2px 4px 2px 4px; + margin-left: 0.4em; + font-size: 0.6em; + +} +.web-codebook .summaryTable .variable-row .row-meta ul li.details { + border:none; +} + +.web-codebook .summaryTable .variable-row .row-meta ul li div { + display: inline-block; + text-align: left; + margin: .1em; + vertical-align: middle; + +} + +.web-codebook .summaryTable .variable-row .row-meta ul li div.wcb-label { + display: inline-block; + font-size: 0.65em; + color: #999; + min-width: 1em; + max-width: 100px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + +} + +.web-codebook .summaryTable .variable-row .row-meta ul li div.value { + display: inline-block; + font-size: 0.85em; + color: #333; +} + /*------------------------------------------------------------------------------------------------\ Data listing \------------------------------------------------------------------------------------------------*/ @@ -471,7 +511,7 @@ padding-right: 1em; padding: 0; } - .web-codebook .wcb-nav ul.wcb-nav-tabs li { + .web-codebook .wcb-nav ul.wcb-nav-tabs li { float: left; cursor: pointer; border: 2px solid black; @@ -488,7 +528,7 @@ padding-right: 1em; text-decoration: none; } - .web-codebook .wcb-nav ul.wcb-nav-tabs li: hover { + .web-codebook .wcb-nav ul.wcb-nav-tabs li:hover { background-color: #777; border: 2px solid #777; } @@ -497,16 +537,13 @@ padding-right: 1em; float: right; } - .web-codebook .wcb-nav ul.wcb-nav-tabs li.settings a { - } - - .web-codebook .wcb-nav ul.wcb-nav-tabs li.active, ul.wcb-nav-tabs li.active: hover { + .web-codebook .wcb-nav ul.wcb-nav-tabs li.active, ul.wcb-nav-tabs li.active:hover { background-color: white; border: 2px solid black; } - .web-codebook .wcb-nav ul.wcb-nav-tabs li.active a, ul.wcb-nav-tabs li.active: hover a{ + .web-codebook .wcb-nav ul.wcb-nav-tabs li.active a, ul.wcb-nav-tabs li.active:hover a{ color: black; } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6a52cb7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2558 @@ +{ + "name": "webcodebook", + "version": "1.4.0", + "lockfileVersion": 1, + "requires": true, + "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 + }, + "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 + }, + "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" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + } + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "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-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "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-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "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-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "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": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "lodash": "4.17.5" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "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-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "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-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-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "6.26.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-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "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-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "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-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "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-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "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-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "6.26.2", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "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-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "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-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "6.24.1", + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "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-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "6.26.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "6.26.0", + "babel-runtime": "6.26.0", + "regexpu-core": "2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "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-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "0.10.1" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", + "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.2", + "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": "3.2.8", + "invariant": "2.2.4", + "semver": "5.5.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "6.26.0", + "babel-runtime": "6.26.0", + "core-js": "2.5.5", + "home-or-tmp": "2.0.0", + "lodash": "4.17.5", + "mkdirp": "0.5.1", + "source-map-support": "0.4.18" + } + }, + "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.5", + "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" + } + }, + "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 + }, + "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" + } + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "1.0.30000847", + "electron-to-chromium": "1.3.48" + } + }, + "caniuse-lite": { + "version": "1.0.30000847", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000847.tgz", + "integrity": "sha512-Weo+tRtVWcN2da782Ebx/27hFNEb+KP+uP6tdqAa+2S5bp1zOJhVH9tEpDygagrfvU4QjeuPwi/5VGsgT4SLaA==", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://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.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "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", + "fsevents": "1.1.3", + "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 + }, + "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.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz", + "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs=", + "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 + }, + "d3": { + "version": "3.5.17", + "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.1" + } + }, + "electron-to-chromium": { + "version": "1.3.48", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz", + "integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=", + "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 + }, + "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" + } + }, + "fsevents": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", + "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", + "dev": true, + "optional": true, + "requires": { + "nan": "2.10.0", + "node-pre-gyp": "0.6.39" + }, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", + "dev": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "optional": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "aproba": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", + "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=", + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.2.9" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", + "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", + "dev": true, + "requires": { + "balanced-match": "0.4.2", + "concat-map": "0.0.1" + } + }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "1.0.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 + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "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 + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.2.tgz", + "integrity": "sha1-ca1dIEvxempsqPRQxhRUBm70YeE=", + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "optional": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "dev": true, + "optional": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "optional": true, + "requires": { + "aproba": "1.1.1", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.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 + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true, + "optional": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", + "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "optional": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "dev": true, + "requires": { + "mime-db": "1.27.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": "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, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.39", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", + "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.2", + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.0", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", + "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==", + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "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 + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "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, + "optional": true + }, + "os-tmpdir": { + "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, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "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 + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", + "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", + "dev": true, + "requires": { + "buffer-shims": "1.0.0", + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.1", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.0.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.0.1" + } + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", + "dev": true + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", + "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", + "dev": true, + "optional": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jodid25519": "1.0.2", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true, + "optional": true + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", + "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true, + "optional": true + }, + "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" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", + "dev": true, + "optional": true, + "requires": { + "debug": "2.6.8", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.2.9", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "dev": true, + "optional": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", + "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "dev": true, + "optional": true, + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "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" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "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" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "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-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-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" + } + }, + "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" + } + }, + "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": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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 + }, + "nan": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "dev": true, + "optional": true + }, + "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" + } + }, + "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 + }, + "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" + } + }, + "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": { + "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 + }, + "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 + }, + "prettier": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.13.3.tgz", + "integrity": "sha512-CIWJNU+cFFdeA0GJSzzFkxiq2WuMvWZMlz6cV/EXhPlRnI3esSFMh+lNmyZ8z/X8O8C1U60Sc8puXALj4/WmZw==", + "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 + }, + "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.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "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.1.1", + "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.6", + "set-immediate-shim": "1.0.1" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "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 + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "private": "0.1.8" + } + }, + "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" + } + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "1.4.0", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "0.5.0" + } + }, + "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 + }, + "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" + } + }, + "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": { + "version": "0.50.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.50.1.tgz", + "integrity": "sha512-XwrnqjSTk+yR8GbP6hiJuVe83MVmBw/gm4P3qP34A10fRXvv6ppl0ZUg1+Pj1tIZSR/aw5ZaILLEiVxwXIAdAw==", + "dev": true + }, + "rollup-plugin-babel": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-3.0.4.tgz", + "integrity": "sha512-TGhQbliTZnRoUhd2214K3r4KJUBu9J1DPzcrAnkluVXOVrveU9OvAaYQ16KyOmujAoq+LMC1+x6YF2xBrU7t+g==", + "dev": true, + "requires": { + "rollup-pluginutils": "1.5.2" + } + }, + "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-watch": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/rollup-watch/-/rollup-watch-4.3.1.tgz", + "integrity": "sha512-6yjnIwfjpSrqA8IafyIu7fsEyeImNR4aDjA1bQ7KWeVuiA+Clfsx8+PGQkyABWIQzmauQ//tIJ5wAxLXsXs8qQ==", + "dev": true, + "requires": { + "chokidar": "1.7.0", + "require-relative": "0.8.7", + "rollup-pluginutils": "2.0.1" + }, + "dependencies": { + "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 + }, + "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" + } + } + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "dev": true + }, + "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 + }, + "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" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "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 + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "webcharts": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/webcharts/-/webcharts-1.7.1.tgz", + "integrity": "sha1-M0A8mhI8xnSTKRX+6JM4F23s7mQ=", + "requires": { + "d3": "3.5.17" + } + } + } +} diff --git a/package.json b/package.json index 74a9333..1bcabb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webcodebook", - "version": "1.3.1", + "version": "1.4.0", "description": "Interactive data set summaries", "keywords": [ "data", @@ -11,30 +11,33 @@ "homepage": "https://github.com/rhoinc/web-codebook", "license": "MIT", "author": "Rho, Inc.", - "main": "build/webcodebook.js", + "main": "./build/webcodebook.js", + "module": "./src/index.js", "repository": { "type": "git", "url": "https://github.com/rhoinc/web-codebook.git" }, + "dependencies": { + "d3": "~3", + "webcharts": "~1.7" + }, "scripts": { - "build": "npm run bundle && npm run format", + "build": "npm run bundle && npm run format && npm run test-page", "bundle": "rollup -c", "format": "npm run format-src && npm run format-bundle", "format-bundle": "prettier --single-quote --write ./build/webcodebook.js", "format-src": "prettier --single-quote --write \"./src/**/*.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", "watch": "rollup -c -w" }, "devDependencies": { - "babel-cli": "^6.0.0", - "babel-preset-es2015-rollup": "^1.1.1", - "rollup": "^0.41.6", - "rollup-plugin-babel": "^2.4.0", - "rollup-watch": "~3.2.2", - "prettier":"1.4.4" - }, - "dependencies": { - "d3": "~3", - "webcharts": "~1.7" + "babel-plugin-external-helpers": "^6.22.0", + "babel-preset-env": "^1.7.0", + "babel-register": "^6.26.0", + "prettier": "^1.13.3", + "rollup": "^0.50.1", + "rollup-plugin-babel": "^3.0.4", + "rollup-watch": "^4.3.1" }, "bugs": { "url": "https://github.com/rhoinc/web-codebook/issues" diff --git a/rollup.config.js b/rollup.config.js index 256246d..8b73123 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,34 +1,40 @@ import babel from 'rollup-plugin-babel'; +var pkg = require('./package.json'); + module.exports = { - moduleName: 'webcodebook', - entry: './src/index.js', - dest: './build/webcodebook.js', - format: 'umd', - globals: { - d3: 'd3', - webcharts: 'webCharts' - }, - external: (function() { - var dependencies = require('./package.json').dependencies; + input: pkg.module, + output: { + name: pkg.name + .split('-') + .map((str,i) => + i === 0 ? + str : + (str.substring(0,1).toUpperCase() + str.substring(1)) + ) + .join(''), + file: pkg.main, + format: 'umd', + globals: { + d3: 'd3', + webcharts: 'webCharts' + }, + }, + external: (function() { + var dependencies = pkg.dependencies; - return Object.keys(dependencies); - }()), - plugins: [ - babel( - { - "presets": [ - [ - "es2015", - { - "modules": false - } - ] - ], - "plugins": [ - "external-helpers" - ], - "exclude": "node_modules/**" - }) - ] + return Object.keys(dependencies); + }()), + plugins: [ + babel({ + exclude: 'node_modules/**', + presets: [ + [ 'env', {modules: false} ] + ], + plugins: [ + 'external-helpers' + ], + babelrc: false + }) + ] }; diff --git a/settings-schema.json b/settings-schema.json new file mode 100644 index 0000000..e2ba48b --- /dev/null +++ b/settings-schema.json @@ -0,0 +1,190 @@ +{ + "description": "", + "overview": "The most straightforward way to customize a codebook is to define a settings object whose properties describe the codebook's behavior and appearance. The settings object can be passed as the second argument to `webcodebook.createCodebook()` (or its alias `webcodebook.createChart()`). All defaults can be overwritten by users when creating a codebook. Parameters for the settings object are described in detail below.", + "version": "1.4.0", + "type": "object", + "properties": { + "filters": { + "type": "array", + "title": "Filters", + "description": "an array of filter variables; by default variables with 10 or fewer levels become filters", + "items": { + "type": "object", + "properties": { + "value_col": { + "type": "string", + "title": "Variable Name", + "description": "the name of the filter variable" + }, + "label": { + "type": "string", + "title": "Variable Label", + "description": "a label that describes the filter variable" + } + } + } + }, + "groups": { + "type": "array", + "title": "Groups", + "description": "an array of group variables; by default variables with 5 or fewer levels become group options", + "items": { + "type": "object", + "properties": { + "value_col": { + "type": "string", + "title": "Variable Name", + "description": "the name of the group variable" + }, + "label": { + "type": "string", + "title": "Variable Label", + "description": "a label that describes the group variable" + } + } + } + }, + "variableLabels": { + "type": "array", + "title": "Variable Labels", + "description": "an array of variable objects with both the variable name and a brief description of the variable", + "items": { + "type": "object", + "properties": { + "value_col": { + "type": "string", + "title": "Variable Name", + "description": "the name of the variable" + }, + "label": { + "type": "string", + "title": "Variable Label", + "description": "a label that describes the variable" + } + } + } + }, + "hiddenVariables": { + "type": "array", + "title": "Hidden Variables", + "description": "an array of variables that will be hidden throughout the codebook", + "items": { + "type": "string" + } + }, + "meta": { + "type": "array", + "title": "Variable Metadata", + "description": "an array of variable metadata", + "items": { + "type": "object", + "properties": { + "value_col": { + "type": "string", + "title": "Variable Name", + "description": "the name of the variable" + }, + "label": { + "type": "string", + "title": "Variable Label", + "description": "a label that describes the variable" + }, + "filter": { + "type": "boolean", + "title": "Filter?", + "description": "includes variable as a filter" + }, + "group": { + "type": "boolean", + "title": "Group?", + "description": "includes variable as a group option" + }, + "type": { + "type": "string", + "title": "Variable Type", + "description": "the type of data contained in the variable" + } + } + } + }, + "autogroups": { + "type": "number", + "title": "Level Cutoff to Choose Group Variables", + "description": "the number of levels in a variable over which the variable will not be added as a group option", + "default": 5 + }, + "autofilter": { + "type": "number", + "title": "Level Cutoff to Choose Filter Variables", + "description": "the number of levels in a variable over which the variable will not be added as a filter", + "default": 10 + }, + "autobins": { + "type": "boolean", + "title": "Automatically Choose Number of Bins in Histogram?", + "description": "the number of bins in histograms will be determined algorithmically based on the range of the variable and the number of observations", + "default": true + }, + "nBins": { + "type": "number", + "title": "Number of Bins in Histogram", + "description": "the number of bins in which to split out each continuous variable into", + "default": 100 + }, + "levelSplit": { + "type": "number", + "title": "Level Cutoff to Choose Horizontal or Vertical Bar Charts", + "description": "the number of levels in a variable over which the variable will be summarized with a vertical bar chart as opposed to a horizontal bar chart", + "default": 5 + }, + "controlVisibility": { + "type": "string", + "title": "Control Visibility", + "description": "the initial state of the display of the controls", + "default": "visible", + "enum": [ + "hidden", + "minimized", + "visible" + ] + }, + "chartVisibility": { + "type": "string", + "title": "Chart Visibility", + "description": "the initial state of the display of the charts", + "default": "minimized", + "enum": [ + "hidden", + "minimized", + "visible" + ] + }, + "tabs": { + "type": "array", + "title": "Codebook Tabs", + "description": "an array of the tabs displayed in the codebook", + "default": [ + "codebook", + "listing", + "chartMaker", + "settings" + ], + "items": { + "type": "string", + "enum": [ + "codebook", + "listing", + "chartMaker", + "settings", + "files" + ] + } + }, + "dataName": { + "type": "string", + "title": "Data Name", + "description": "the name of the data file summarized in the codebook", + "default": "" + } + } +} diff --git a/src/charts/createDotPlot.js b/src/charts/createDotPlot.js index 8e1732c..31ca600 100644 --- a/src/charts/createDotPlot.js +++ b/src/charts/createDotPlot.js @@ -45,7 +45,11 @@ export function createDotPlot(this_, d) { (a, b) => a.prop_n > b.prop_n ? -2 - : a.prop_n < b.prop_n ? 2 : a.key < b.key ? -1 : 1 + : a.prop_n < b.prop_n + ? 2 + : a.key < b.key + ? -1 + : 1 ) .slice(0, 5); // sort data by descending rate and keep only the first five categories. @@ -63,7 +67,11 @@ export function createDotPlot(this_, d) { (a, b) => a.prop_n > b.prop_n ? -2 - : a.prop_n < b.prop_n ? 2 : a.key < b.key ? -1 : 1 + : a.prop_n < b.prop_n + ? 2 + : a.key < b.key + ? -1 + : 1 ) .forEach(value => { value.group = group.group; diff --git a/src/charts/createHistogramBoxPlotControls.js b/src/charts/createHistogramBoxPlotControls.js index 4480a5a..00b3040 100644 --- a/src/charts/createHistogramBoxPlotControls.js +++ b/src/charts/createHistogramBoxPlotControls.js @@ -18,8 +18,12 @@ export function createHistogramBoxPlotControls(this_, d) { .attr('checked', true); commonScaleCheckbox.on('change', function() { - d3select(this_).selectAll('.wc-chart').remove(); - d3select(this_).selectAll('.panel-label').remove(); + d3select(this_) + .selectAll('.wc-chart') + .remove(); + d3select(this_) + .selectAll('.panel-label') + .remove(); d.commonScale = this.checked; createHistogramBoxPlot(this_, d); }); diff --git a/src/charts/createHorizontalBars.js b/src/charts/createHorizontalBars.js index d88ec8b..89b86b6 100644 --- a/src/charts/createHorizontalBars.js +++ b/src/charts/createHorizontalBars.js @@ -49,7 +49,11 @@ export function createHorizontalBars(this_, d) { (a, b) => a.prop_n > b.prop_n ? -2 - : a.prop_n < b.prop_n ? 2 : a.key < b.key ? -1 : 1 + : a.prop_n < b.prop_n + ? 2 + : a.key < b.key + ? -1 + : 1 ); // sort data by descending rate and keep only the first five categories. chartSettings.y.order = chartData.map(d => d.key).reverse(); @@ -90,7 +94,11 @@ export function createHorizontalBars(this_, d) { (a, b) => a.prop_n > b.prop_n ? -2 - : a.prop_n < b.prop_n ? 2 : a.key < b.key ? -1 : 1 + : a.prop_n < b.prop_n + ? 2 + : a.key < b.key + ? -1 + : 1 ); group.data.forEach(function(d) { @@ -118,8 +126,9 @@ export function createHorizontalBars(this_, d) { d3select(chartContainer) .append('p') .text( - `${chartSettings.group_col}: ${group.chartSettings - .group_val} (n=${group.chartSettings.n})` + `${chartSettings.group_col}: ${group.chartSettings.group_val} (n=${ + group.chartSettings.n + })` ); d3select(chartContainer) .append('div') diff --git a/src/charts/createHorizontalBarsControls.js b/src/charts/createHorizontalBarsControls.js index 6edcea3..009b57f 100644 --- a/src/charts/createHorizontalBarsControls.js +++ b/src/charts/createHorizontalBarsControls.js @@ -22,8 +22,12 @@ export function createHorizontalBarsControls(this_, d) { .text(d => d); outcomeSelect.on('change', function() { - d3select(this_).selectAll('.wc-chart').remove(); - d3select(this_).selectAll('.panel-label').remove(); + d3select(this_) + .selectAll('.wc-chart') + .remove(); + d3select(this_) + .selectAll('.panel-label') + .remove(); if (type_control.property('value') === 'Paneled (Bar Charts)') { createHorizontalBars(this_, d); } else { @@ -47,8 +51,12 @@ export function createHorizontalBarsControls(this_, d) { .text(d => d); type_control.on('change', function() { - d3select(this_).selectAll('.wc-chart').remove(); - d3select(this_).selectAll('.panel-label').remove(); + d3select(this_) + .selectAll('.wc-chart') + .remove(); + d3select(this_) + .selectAll('.panel-label') + .remove(); if (this.value == 'Paneled (Bar Charts)') { createHorizontalBars(this_, d); } else { diff --git a/src/charts/createSpark.js b/src/charts/createSpark.js new file mode 100644 index 0000000..cc2cc26 --- /dev/null +++ b/src/charts/createSpark.js @@ -0,0 +1,8 @@ +import makeHist from './spark/makeHist'; + +export default function createSpark() { + var d = d3.select(this).datum(); + if (d.statistics.n > 0) { + makeHist(this, d); + } +} diff --git a/src/charts/createVerticalBars.js b/src/charts/createVerticalBars.js index f8c3eb7..cf81de0 100644 --- a/src/charts/createVerticalBars.js +++ b/src/charts/createVerticalBars.js @@ -37,7 +37,7 @@ export function createVerticalBars(this_, d) { } ], colors: ['#999'], - gridlines: '', + gridlines: 'y', resizable: false, height: this_.height, margin: this_.margin, @@ -45,7 +45,6 @@ export function createVerticalBars(this_, d) { group_col: d.group || null, group_label: d.groupLabel || null, overall: d.statistics.values, - gridlines: 'y', sort: sortType, //Alphabetical, Ascending, Descending chartType: d.chartType }; @@ -113,8 +112,9 @@ export function createVerticalBars(this_, d) { d3select(chartContainer) .append('p') .text( - `${chartSettings.group_col}: ${group.chartSettings - .group_val} (n=${group.chartSettings.n})` + `${chartSettings.group_col}: ${group.chartSettings.group_val} (n=${ + group.chartSettings.n + })` ); d3select(chartContainer) diff --git a/src/charts/createVerticalBarsControls.js b/src/charts/createVerticalBarsControls.js index 52901f3..47fb251 100644 --- a/src/charts/createVerticalBarsControls.js +++ b/src/charts/createVerticalBarsControls.js @@ -21,8 +21,12 @@ export function createVerticalBarsControls(this_, d) { .text(d => d); outcomeSelect.on('change', function() { - d3select(this_).selectAll('.wc-chart').remove(); - d3select(this_).selectAll('.panel-label').remove(); + d3select(this_) + .selectAll('.wc-chart') + .remove(); + d3select(this_) + .selectAll('.panel-label') + .remove(); createVerticalBars(this_, d); }); @@ -39,8 +43,12 @@ export function createVerticalBarsControls(this_, d) { .text(d => d); x_sort.on('change', function() { - d3select(this_).selectAll('.wc-chart').remove(); - d3select(this_).selectAll('.panel-label').remove(); + d3select(this_) + .selectAll('.wc-chart') + .remove(); + d3select(this_) + .selectAll('.panel-label') + .remove(); createVerticalBars(this_, d); }); } diff --git a/src/charts/dotPlot/modifyOverallLegendMark.js b/src/charts/dotPlot/modifyOverallLegendMark.js index 3509d54..72014a8 100644 --- a/src/charts/dotPlot/modifyOverallLegendMark.js +++ b/src/charts/dotPlot/modifyOverallLegendMark.js @@ -7,9 +7,9 @@ export default function modifyOverallLegendMark(chart) { .append('line') .classed('legend-mark', true) .attr({ - x1: 3 * BBox.width / 4, + x1: (3 * BBox.width) / 4, y1: 0, - x2: 3 * BBox.width / 4, + x2: (3 * BBox.width) / 4, y2: BBox.height }) .style({ diff --git a/src/charts/histogramBoxPlot/addBoxPlot.js b/src/charts/histogramBoxPlot/addBoxPlot.js index 1b892cc..c2dbd71 100644 --- a/src/charts/histogramBoxPlot/addBoxPlot.js +++ b/src/charts/histogramBoxPlot/addBoxPlot.js @@ -83,11 +83,12 @@ export default function addBoxPlot(chart) { y2: chart.plot_height + chart.config.boxPlotHeight }) .style({ - stroke: [0.05, 0.95].indexOf(quantile.probability) > -1 - ? 'black' - : [0.25, 0.75].indexOf(quantile.probability) > -1 + stroke: + [0.05, 0.95].indexOf(quantile.probability) > -1 ? 'black' - : 'black', + : [0.25, 0.75].indexOf(quantile.probability) > -1 + ? 'black' + : 'black', 'stroke-width': '3px' }); quantile.mark diff --git a/src/charts/histogramBoxPlot/defineHistogram.js b/src/charts/histogramBoxPlot/defineHistogram.js index 6bad0e0..7b10a8b 100644 --- a/src/charts/histogramBoxPlot/defineHistogram.js +++ b/src/charts/histogramBoxPlot/defineHistogram.js @@ -1,4 +1,3 @@ -import '../../util/object-assign'; import clone from '../../util/clone'; import defaultSettings, { syncSettings } from './defaultSettings'; diff --git a/src/charts/histogramBoxPlot/makeTooltip.js b/src/charts/histogramBoxPlot/makeTooltip.js index ecc4f59..ab4e9ab 100644 --- a/src/charts/histogramBoxPlot/makeTooltip.js +++ b/src/charts/histogramBoxPlot/makeTooltip.js @@ -7,9 +7,10 @@ export default function makeTooltip(d, i, context) { d.range = `${format(d.rangeLow)}-${format(d.rangeHigh)}`; d.selector = `bar` + i; d.side = context.x(d.midpoint) < context.plot_width / 2 ? 'left' : 'right'; - d.xPosition = d.side === 'left' - ? context.x(d.midpoint) + offset - : context.x(d.midpoint) - offset; + d.xPosition = + d.side === 'left' + ? context.x(d.midpoint) + offset + : context.x(d.midpoint) - offset; //Define tooltips. const tooltip = context.svg.append('g').attr('id', d.selector), diff --git a/src/charts/histogramBoxPlot/onInit.js b/src/charts/histogramBoxPlot/onInit.js index 868c4b3..6e34d79 100644 --- a/src/charts/histogramBoxPlot/onInit.js +++ b/src/charts/histogramBoxPlot/onInit.js @@ -21,8 +21,9 @@ export default function onInit() { .attr('class', 'panel-label') .style('margin-left', context.config.margin.left + 'px') .html( - `${this.config.group_col}: ${this.group} (n=${this - .raw_data.length})` + `${this.config.group_col}: ${this.group} (n=${ + this.raw_data.length + })` ); this.wrap .node() @@ -55,7 +56,9 @@ export default function onInit() { //in a single bin within a subgrouping. let max = 0; if (!config.y.domain[1]) { - const nestedData = d3nest().key(d => d[panel]).entries(context.raw_data); + const nestedData = d3nest() + .key(d => d[panel]) + .entries(context.raw_data); nestedData.forEach(group => { const domain = d3extent(group.values, d => +d[measure]); const binWidth = (domain[1] - domain[0]) / config.nBins; diff --git a/src/charts/horizontalBars/drawDifferences.js b/src/charts/horizontalBars/drawDifferences.js index a2facfd..169fb1b 100644 --- a/src/charts/horizontalBars/drawDifferences.js +++ b/src/charts/horizontalBars/drawDifferences.js @@ -11,8 +11,8 @@ export default function drawDifferences(chart) { }) .forEach(d => { const overall = chart.config.overall.filter(function(di) { - return di.key === d.values.raw[0].key; - })[0], + return di.key === d.values.raw[0].key; + })[0], g = chart.svg .append('g') .classed('difference-from-total', true) diff --git a/src/charts/horizontalBars/onInit.js b/src/charts/horizontalBars/onInit.js index 53fd1dd..cacffa1 100644 --- a/src/charts/horizontalBars/onInit.js +++ b/src/charts/horizontalBars/onInit.js @@ -7,8 +7,9 @@ export default function onInit() { .attr('class', 'panel-label') .style('margin-left', chart.config.margin.left + 'px') .html( - `${this.config.group_col}: ${this.config - .group_val} (n=${this.config.n})` + `${this.config.group_col}: ${ + this.config.group_val + } (n=${this.config.n})` ); this.wrap .node() diff --git a/src/charts/spark/makeHist.js b/src/charts/spark/makeHist.js new file mode 100644 index 0000000..6e5361d --- /dev/null +++ b/src/charts/spark/makeHist.js @@ -0,0 +1,82 @@ +export default function makeHist(this_, d) { + var height = 15, + width = 100; + + var svg = d3 + .select(this_) + .append('svg') + .attr('height', height) + .attr('width', width) + .style('margin-right', '0.1em'); + + if (d.type == 'categorical') { + var bins = d.statistics.values; + bins.forEach(function(d) { + d.title = d.key + ' - ' + d.n + ' (' + d.prop_n_text + ')'; + d.color = '#999'; + }); + } else if (d.type == 'continuous') { + var values = d.values.map(function(m) { + return +m.value; + }); + var x_linear = d3.scale + .linear() + .domain(d3.extent(values)) + .range([0, width]); + var bins = d3.layout + .histogram() + .bins(x_linear.ticks(50))(values) + .map(function(m, i) { + m.key = '[' + m.x + '-' + (m.x + m.dx) + ')'; + m.n = m.length; + m.title = m.key + ' - ' + m.n; + m.color = 'black'; + return m; + }); + } + + // scales + var x = d3.scale + .ordinal() + .domain( + bins.map(function(d) { + return d.key; + }) + ) + .rangeBands([0, width], 0.1, 0); + + var width = x.rangeBand(); + + var y = d3.scale + .linear() + .domain([ + 0, + d3.max(bins, function(d) { + return d.n; + }) + ]) + .range([height, 0]); + + var bar = svg + .selectAll('.bar') + .data(bins) + .enter() + .append('g') + .attr('class', 'bar') + .attr('transform', function(d) { + return 'translate(' + x(d.key) + ',' + y(d.n) + ')'; + }); + + bar + .append('rect') + .attr('x', 1) + .attr('width', width) + .attr('height', function(d) { + return height - y(d.n); + }) + .attr('fill', d.type == 'categorical' ? '#999' : 'black') + .append('title') + .text(function(d) { + return d.title; + }); +} diff --git a/src/charts/util/highlightData.js b/src/charts/util/highlightData.js index 0963e92..c58d198 100644 --- a/src/charts/util/highlightData.js +++ b/src/charts/util/highlightData.js @@ -3,17 +3,18 @@ import indicateLoading from '../../codebook/util/indicateLoading'; export default function highlightData(chart) { const codebook = d3select( - chart.wrap.node().parentNode.parentNode.parentNode - ).datum(), // codebook object is attached to .summaryTable element + chart.wrap.node().parentNode.parentNode.parentNode + ).datum(), // codebook object is attached to .summaryTable element bars = chart.svg.selectAll('.bar-group'); bars.on('click', function(d) { indicateLoading(codebook, '.highlightCount', () => { - const newIndexes = chart.config.chartType.indexOf('Bars') > -1 - ? d.values.raw[0].indexes - : chart.config.chartType === 'histogramBoxPlot' - ? d.values.raw.map(di => di.index) - : []; + const newIndexes = + chart.config.chartType.indexOf('Bars') > -1 + ? d.values.raw[0].indexes + : chart.config.chartType === 'histogramBoxPlot' + ? d.values.raw.map(di => di.index) + : []; const currentIndexes = codebook.data.highlighted.map( di => di['web-codebook-index'] ); @@ -34,7 +35,7 @@ export default function highlightData(chart) { codebook.dataListing.init(codebook); codebook.summaryTable.draw(codebook); codebook.chartMaker.draw(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); }); }); } diff --git a/src/charts/verticalBars/makeTooltip.js b/src/charts/verticalBars/makeTooltip.js index 1714cf1..5e7ab6f 100644 --- a/src/charts/verticalBars/makeTooltip.js +++ b/src/charts/verticalBars/makeTooltip.js @@ -19,9 +19,8 @@ export default function makeTooltip(d, i, context) { .attr({ x: context.x(d.values.x), dx: context.x(d.values.x) < context.plot_width / 2 ? '1em' : '-1em', - 'text-anchor': context.x(d.values.x) < context.plot_width / 2 - ? 'start' - : 'end' + 'text-anchor': + context.x(d.values.x) < context.plot_width / 2 ? 'start' : 'end' }) .text(`${d.values.x}`); text @@ -30,9 +29,8 @@ export default function makeTooltip(d, i, context) { x: context.x(d.values.x), dx: context.x(d.values.x) < context.plot_width / 2 ? '1em' : '-1em', dy: '-1.5em', - 'text-anchor': context.x(d.values.x) < context.plot_width / 2 - ? 'start' - : 'end' + 'text-anchor': + context.x(d.values.x) < context.plot_width / 2 ? 'start' : 'end' }) .text('n=' + d.values.raw[0].n + ' (' + d3format('0.1%')(d.total) + ')'); const dimensions = text[0][0].getBBox(); diff --git a/src/charts/verticalBars/onInit.js b/src/charts/verticalBars/onInit.js index 53fd1dd..cacffa1 100644 --- a/src/charts/verticalBars/onInit.js +++ b/src/charts/verticalBars/onInit.js @@ -7,8 +7,9 @@ export default function onInit() { .attr('class', 'panel-label') .style('margin-left', chart.config.margin.left + 'px') .html( - `${this.config.group_col}: ${this.config - .group_val} (n=${this.config.n})` + `${this.config.group_col}: ${ + this.config.group_val + } (n=${this.config.n})` ); this.wrap .node() diff --git a/src/charts/verticalBars/onResize.js b/src/charts/verticalBars/onResize.js index c50f2e3..dfb3c64 100644 --- a/src/charts/verticalBars/onResize.js +++ b/src/charts/verticalBars/onResize.js @@ -44,7 +44,10 @@ export default function onResize() { return i === arbitrary; }); } - bars.select('rect').style('stroke-width', null).style('stroke', null); + bars + .select('rect') + .style('stroke-width', null) + .style('stroke', null); closest = closest.select('rect'); //Activate tooltip. @@ -56,7 +59,10 @@ export default function onResize() { }) .on('mouseout', function() { context.svg.selectAll('g.svg-tooltip').classed('active', false); - bars.select('rect').style('stroke-width', null).style('stroke', null); + bars + .select('rect') + .style('stroke-width', null) + .style('stroke', null); }); //Add event listener to marks to highlight data. diff --git a/src/codebook/chartMaker/draw.js b/src/codebook/chartMaker/draw.js index d286436..c22b9fb 100644 --- a/src/codebook/chartMaker/draw.js +++ b/src/codebook/chartMaker/draw.js @@ -1,7 +1,7 @@ import { createChart } from 'webcharts'; import { multiply } from 'webcharts'; import indicateLoading from '../util/indicateLoading'; -import clone from '../util/clone'; +import clone from '../../util/clone'; import chartMakerSettings from './chartMakerSettings.js'; import makeSettings from './makeSettings.js'; @@ -45,7 +45,7 @@ export function draw(codebook) { //Define chart. chartMaker.chart = createChart( - '.web-codebook .chartMaker.section .cm-chart', + codebook.wrap.select('.chartMaker.section .cm-chart').node(), chartMaker.chartSettings ); diff --git a/src/codebook/controls.js b/src/codebook/controls.js index d34134d..af8d49a 100644 --- a/src/codebook/controls.js +++ b/src/codebook/controls.js @@ -6,14 +6,10 @@ import { init } from './controls/init'; import { filters } from './controls/filters'; import { groups } from './controls/groups'; import { controlToggle } from './controls/controlToggle'; -import { highlight } from './controls/highlight'; -import { updateRowCount } from './controls/updateRowCount'; export const controls = { init: init, filters: filters, groups: groups, - controlToggle: controlToggle, - highlight: highlight, - updateRowCount: updateRowCount + controlToggle: controlToggle }; diff --git a/src/codebook/controls/controlToggle/init.js b/src/codebook/controls/controlToggle/init.js index 36f6bdc..7c35a1e 100644 --- a/src/codebook/controls/controlToggle/init.js +++ b/src/codebook/controls/controlToggle/init.js @@ -14,9 +14,10 @@ export function init(codebook) { codebook.controls.controlToggle.set(codebook); controlToggle.on('click', function() { - codebook.config.controlVisibility = d3select(this).text() == 'Hide' - ? 'minimized' //click "-" to minimize controls - : 'visible'; // click "+" to show controls + codebook.config.controlVisibility = + d3select(this).text() == 'Hide' + ? 'minimized' //click "-" to minimize controls + : 'visible'; // click "+" to show controls codebook.controls.controlToggle.set(codebook); }); diff --git a/src/codebook/controls/filters/init.js b/src/codebook/controls/filters/init.js index 4603ee7..b1b87fa 100644 --- a/src/codebook/controls/filters/init.js +++ b/src/codebook/controls/filters/init.js @@ -9,8 +9,8 @@ import { update } from './update'; export function init(codebook) { //initialize the wrapper const selector = codebook.controls.wrap - .append('div') - .attr('class', 'custom-filters'), + .append('div') + .attr('class', 'custom-filters'), filterList = selector.append('ul').attr('class', 'filter-list'); update(codebook); diff --git a/src/codebook/controls/filters/update.js b/src/codebook/controls/filters/update.js index 1538c00..4676e35 100644 --- a/src/codebook/controls/filters/update.js +++ b/src/codebook/controls/filters/update.js @@ -99,7 +99,7 @@ export function update(codebook) { //clear highlights codebook.data.highlighted = []; codebook.data.makeSummary(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); codebook.summaryTable.draw(codebook); codebook.chartMaker.draw(codebook); codebook.dataListing.init(codebook); diff --git a/src/codebook/controls/groups/update.js b/src/codebook/controls/groups/update.js index 007c1fe..b46747a 100644 --- a/src/codebook/controls/groups/update.js +++ b/src/codebook/controls/groups/update.js @@ -48,7 +48,7 @@ export function update(codebook) { codebook.data.makeSummary(codebook); codebook.summaryTable.draw(codebook); codebook.chartMaker.draw(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); }); }); } diff --git a/src/codebook/controls/init.js b/src/codebook/controls/init.js index 38c0d95..7b8e1ac 100644 --- a/src/codebook/controls/init.js +++ b/src/codebook/controls/init.js @@ -23,8 +23,7 @@ export function init(codebook) { codebook.controls.groups.init(codebook); codebook.controls.filters.init(codebook); codebook.controls.controlToggle.init(codebook); - codebook.controls.highlight.init(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); //Hide group-by options corresponding to variables specified in settings.hiddenVariables. codebook.controls.wrap diff --git a/src/codebook/controls/updateRowCount.js b/src/codebook/controls/updateRowCount.js deleted file mode 100644 index 5b621eb..0000000 --- a/src/codebook/controls/updateRowCount.js +++ /dev/null @@ -1,27 +0,0 @@ -import { format as d3format } from 'd3'; - -export function updateRowCount(codebook) { - if (codebook.data.summary.length > 0) { - var nShown = codebook.data.summary[0].statistics.N; - var nTot = codebook.data.raw.length; - var percent = d3format('0.1%')(nShown / nTot); - var tableSummary = - nShown + ' of ' + nTot + ' (' + percent + ') rows selected'; - codebook.controls.rowCount.text(tableSummary).classed('warn', false); - } else { - codebook.controls.rowCount.text('No rows selected.').classed('warn', true); - } - - //Add note regarding highlighted cells and show/hide the clear highlight button - if (codebook.data.highlighted.length > 0) { - codebook.controls.highlightCount.html( - ' and ' + - codebook.data.highlighted.length + - ' highlighted. ' - ); - codebook.controls.highlight.clearButton.classed('hidden', false); - } else { - codebook.controls.highlightCount.text(''); - codebook.controls.highlight.clearButton.classed('hidden', true); - } -} diff --git a/src/codebook/data/makeSummary.js b/src/codebook/data/makeSummary.js index dd3d383..5a15f55 100644 --- a/src/codebook/data/makeSummary.js +++ b/src/codebook/data/makeSummary.js @@ -32,13 +32,14 @@ export function makeSummary(codebook) { variables[i].chartVisibility = codebook.config.chartVisibility; //get variable label - variables[i].label = codebook.config.variableLabels - .map(variableLabel => variableLabel.value_col) - .indexOf(variable) > -1 - ? codebook.config.variableLabels.filter( - variableLabel => variableLabel.value_col === variable - )[0].label - : variable; + variables[i].label = + codebook.config.variableLabels + .map(variableLabel => variableLabel.value_col) + .indexOf(variable) > -1 + ? codebook.config.variableLabels.filter( + variableLabel => variableLabel.value_col === variable + )[0].label + : variable; // Add metadata Object variables[i].meta = [{ key: 'Type', value: variables[i].type }]; @@ -54,40 +55,46 @@ export function makeSummary(codebook) { } //calculate variable statistics (including for highlights - if any) - var sub = codebook.data.highlighted.length > 0 - ? function(d) { - return d.highlighted; - } - : null; - variables[i].statistics = variables[i].type === 'continuous' - ? summarize.continuous(variables[i].values, sub) - : summarize.categorical(variables[i].values, sub); + var sub = + codebook.data.highlighted.length > 0 + ? function(d) { + return d.highlighted; + } + : null; + variables[i].statistics = + variables[i].type === 'continuous' + ? summarize.continuous(variables[i].values, sub) + : summarize.categorical(variables[i].values, sub); //get chart type - variables[i].chartType = variables[i].type == 'continuous' - ? 'histogramBoxPlot' - : (variables[i].type == 'categorical') & - (variables[i].statistics.values.length > codebook.config.levelSplit) - ? 'verticalBars' + variables[i].chartType = + variables[i].type == 'continuous' + ? 'histogramBoxPlot' : (variables[i].type == 'categorical') & + (variables[i].statistics.values.length > codebook.config.levelSplit) + ? 'verticalBars' + : (variables[i].type == 'categorical') & (variables[i].statistics.values.length <= codebook.config.levelSplit) - ? 'horizontalBars' - : 'error'; + ? 'horizontalBars' + : 'error'; //Handle groups. if (group) { variables[i].group = group; - variables[i].groupLabel = codebook.config.variableLabels - .map(variableLabel => variableLabel.value_col) - .indexOf(group) > -1 - ? codebook.config.variableLabels.filter( - variableLabel => variableLabel.value_col === group - )[0].label - : group; - variables[i].groups = d3set(data.map(d => d[group])).values().map(g => { - return { group: g }; - }); + variables[i].groupLabel = + codebook.config.variableLabels + .map(variableLabel => variableLabel.value_col) + .indexOf(group) > -1 + ? codebook.config.variableLabels.filter( + variableLabel => variableLabel.value_col === group + )[0].label + : group; + variables[i].groups = d3set(data.map(d => d[group])) + .values() + .map(g => { + return { group: g }; + }); variables[i].groups.forEach(g => { //Define variable metadata and generate data array. diff --git a/src/codebook/data/summarize/categorical.js b/src/codebook/data/summarize/categorical.js index f6d9ccc..dc50bb0 100644 --- a/src/codebook/data/summarize/categorical.js +++ b/src/codebook/data/summarize/categorical.js @@ -8,6 +8,14 @@ export default function categorical(vector, sub) { ); statistics.n = nonMissing.length; statistics.nMissing = vector.length - statistics.n; + statistics.percentMissing = statistics.nMissing / statistics.N; + statistics.missingSummary = + statistics.nMissing + + '/' + + statistics.N + + ' (' + + d3format('0.1%')(statistics.percentMissing) + + ')'; statistics.values = d3nest() .key(d => d.value) .rollup(function(d) { diff --git a/src/codebook/data/summarize/continuous.js b/src/codebook/data/summarize/continuous.js index 96c00a6..544ccac 100644 --- a/src/codebook/data/summarize/continuous.js +++ b/src/codebook/data/summarize/continuous.js @@ -14,6 +14,14 @@ export default function continuous(vector, sub) { .sort((a, b) => a - b); statistics.n = nonMissing.length; statistics.nMissing = vector.length - statistics.n; + statistics.percentMissing = statistics.nMissing / statistics.N; + statistics.missingSummary = + statistics.nMissing + + '/' + + statistics.N + + ' (' + + d3format('0.1%')(statistics.percentMissing) + + ')'; statistics.mean = d3format('0.2f')(d3mean(nonMissing)); statistics.SD = d3format('0.2f')(d3deviation(nonMissing)); const quantiles = [ diff --git a/src/codebook/dataListing/init.js b/src/codebook/dataListing/init.js index 807bfce..9090b7c 100644 --- a/src/codebook/dataListing/init.js +++ b/src/codebook/dataListing/init.js @@ -11,7 +11,10 @@ export function init(codebook) { dataListing.wrap.selectAll('*').remove(); //Define table. - dataListing.table = createTable('.web-codebook .dataListing', {}); + dataListing.table = createTable( + codebook.wrap.select('.dataListing').node(), + {} + ); //Define callback. onDraw(dataListing); diff --git a/src/codebook/init.js b/src/codebook/init.js index 60e88fc..ec5135d 100644 --- a/src/codebook/init.js +++ b/src/codebook/init.js @@ -34,13 +34,15 @@ export function init(data) { //prepare the data summaries this.data.makeSummary(this); + //make the title + this.title.init(this); + //draw controls this.util.makeAutomaticFilters(this); this.util.makeAutomaticGroups(this); this.controls.init(this); //initialize nav, title and instructions - this.title.init(this); this.nav.init(this); this.instructions.init(this); diff --git a/src/codebook/instructions/chartToggle/init.js b/src/codebook/instructions/chartToggle/init.js index 185797d..9942643 100644 --- a/src/codebook/instructions/chartToggle/init.js +++ b/src/codebook/instructions/chartToggle/init.js @@ -10,20 +10,20 @@ export function init(codebook) { .attr('class', 'control chart-toggle') .classed('hidden', codebook.config.chartVisibility == 'hidden'); - selector.append('small').text('Toggle Charts: '); + selector.append('small').text('Toggle Details: '); var showAllButton = selector .append('button') - .text('Show All Charts') + .text('Show All Details') .on('click', function() { - codebook.wrap.selectAll('.variable-row').classed('hiddenChart', false); + codebook.wrap.selectAll('.variable-row').classed('hiddenDetails', false); codebook.wrap.selectAll('.row-toggle').html('▼'); }); var hideAllButton = selector .append('button') - .text('Hide All Charts') + .text('Hide All Details') .on('click', function() { - codebook.wrap.selectAll('.variable-row').classed('hiddenChart', true); + codebook.wrap.selectAll('.variable-row').classed('hiddenDetails', true); codebook.wrap.selectAll('.row-toggle').html('►'); }); } diff --git a/src/codebook/instructions/detailSelect/init.js b/src/codebook/instructions/detailSelect/init.js deleted file mode 100644 index cfe9deb..0000000 --- a/src/codebook/instructions/detailSelect/init.js +++ /dev/null @@ -1,40 +0,0 @@ -/*------------------------------------------------------------------------------------------------\ - Initialize detail select -\------------------------------------------------------------------------------------------------*/ -import detailList from '../../summaryTable/renderRow/details/detailList'; -import { select as d3select } from 'd3'; - -//export function init(selector, data, vars, settings) { -export function init(codebook) { - //initialize the wrapper - var control = codebook.instructions.wrap - .append('span') - .attr('class', 'control detail-select'); - - control.append('small').html('Header Details: '); - var detailSelect = control.append('select'); - - var detailItems = detailSelect - .selectAll('option') - .data(detailList) - .enter() - .append('option') - .html(d => d.key); - - //Handlers for label events - detailSelect.on('change', function() { - var current = this.value; - var detailObj = detailList.filter(f => f.key == current)[0]; - - codebook.wrap.selectAll('.variable-row').each(function(d) { - //show the requested detail for each row - var list = d3select(this).select('.row-head .row-details ul'); - detailObj.action(d, list); - - //update the select on each row - d3select(this) - .select('.row-chart .row-controls .detail-controls select') - .property('value', current); - }); - }); -} diff --git a/src/codebook/instructions/update.js b/src/codebook/instructions/update.js index 8be9ffa..f0a49d6 100644 --- a/src/codebook/instructions/update.js +++ b/src/codebook/instructions/update.js @@ -1,5 +1,4 @@ import { init as initToggle } from './chartToggle/init'; -import { init as initDetailSelect } from './detailSelect/init'; export function update(codebook) { var activeTab = codebook.nav.tabs.filter(d => d.active)[0]; @@ -9,7 +8,6 @@ export function update(codebook) { //add tab-specific controls if (activeTab.key == 'codebook') { - initDetailSelect(codebook); initToggle(codebook); } } diff --git a/src/codebook/nav/availableTabs.js b/src/codebook/nav/availableTabs.js index 80a594a..d884205 100644 --- a/src/codebook/nav/availableTabs.js +++ b/src/codebook/nav/availableTabs.js @@ -2,28 +2,28 @@ const availableTabs = [ { key: 'files', label: 'Files', - selector: '.web-codebook .fileListing', + selector: '.fileListing', controls: false, instructions: 'Click a row to see the codebook for the file.' }, { key: 'codebook', label: 'Codebook', - selector: '.web-codebook .summaryTable', + selector: '.summaryTable', controls: true, instructions: 'Automatically generated data summaries for each column.' }, { key: 'listing', label: 'Data Listing', - selector: '.web-codebook .dataListing', + selector: '.dataListing', controls: true, instructions: 'Listing of all selected records.' }, { key: 'chartMaker', label: 'Charts', - selector: '.web-codebook .chartMaker', + selector: '.chartMaker', controls: true, instructions: 'Pick two variables to compare. Filter and group (panel) the chart using the controls above.' @@ -31,7 +31,7 @@ const availableTabs = [ { key: 'settings', label: '⚙', - selector: '.web-codebook .settings', + selector: '.settings', controls: false, instructions: "This interactive table allows users to modify each column's metadata. Updating these settings will reset the codebook and data listing." diff --git a/src/codebook/nav/init.js b/src/codebook/nav/init.js index 24e3afa..deab8fc 100644 --- a/src/codebook/nav/init.js +++ b/src/codebook/nav/init.js @@ -1,12 +1,14 @@ import availableTabs from './availableTabs'; import { select as d3select } from 'd3'; +import clone from '../../util/clone'; export function init(codebook) { + const defaultTabs = clone(availableTabs); codebook.nav.wrap.selectAll('*').remove(); //permanently hide the codebook sections that aren't included - availableTabs.forEach(function(tab) { - tab.wrap = d3select(tab.selector); + defaultTabs.forEach(function(tab) { + tab.wrap = codebook.wrap.select(tab.selector); tab.wrap.classed( 'hidden', codebook.config.tabs.map(m => m.key).indexOf(tab.key) == -1 @@ -14,7 +16,7 @@ export function init(codebook) { }); //get the tabs for the current codebook - codebook.nav.tabs = availableTabs.filter( + codebook.nav.tabs = defaultTabs.filter( tab => codebook.config.tabs.map(m => m.key).indexOf(tab.key) > -1 ); @@ -32,15 +34,6 @@ export function init(codebook) { t.wrap.classed('hidden', !t.active); }); - //set control visibility - var activeTab = codebook.nav.tabs.filter(f => f.active)[0]; - if (codebook.config.controlVisibility != 'disabled') { - codebook.config.controlVisibility = activeTab.controls - ? 'visible' - : 'hidden'; - codebook.controls.controlToggle.set(codebook); - } - //draw the nav if (codebook.nav.tabs.length > 1) { var chartNav = codebook.nav.wrap @@ -73,8 +66,13 @@ export function init(codebook) { codebook.instructions.update(codebook); //show/hide the controls (unless they are disabled) + if (codebook.config.controlVisibility !== 'hidden') + codebook.config.previousControlVisibility = + codebook.config.controlVisibility; if (codebook.config.controlVisibility != 'disabled') { - codebook.config.controlVisibility = d.controls ? 'visible' : 'hidden'; + codebook.config.controlVisibility = d.controls + ? codebook.config.previousControlVisibility + : 'hidden'; codebook.controls.controlToggle.set(codebook); } } diff --git a/src/codebook/settings/layout.js b/src/codebook/settings/layout.js index 6d08969..f287dbc 100644 --- a/src/codebook/settings/layout.js +++ b/src/codebook/settings/layout.js @@ -14,10 +14,11 @@ export function layout(codebook) { Column: column, Label: { type: 'text', - label: labeledColumns.indexOf(column) > -1 - ? codebook.config.variableLabels[labeledColumns.indexOf(column)] - .label - : '' + label: + labeledColumns.indexOf(column) > -1 + ? codebook.config.variableLabels[labeledColumns.indexOf(column)] + .label + : '' }, Group: { type: 'checkbox', @@ -84,10 +85,9 @@ export function layout(codebook) { default: cell.attr( 'title', - `${d.value.checked ? 'Remove' : 'Add'} ${d.column} ${d.value - .checked - ? 'from' - : 'to'} ${d.key.toLowerCase()} list` + `${d.value.checked ? 'Remove' : 'Add'} ${d.column} ${ + d.value.checked ? 'from' : 'to' + } ${d.key.toLowerCase()} list` ); const checkbox = cell .append('input') diff --git a/src/codebook/settings/updateSettings.js b/src/codebook/settings/updateSettings.js index 57925d5..5f8fe68 100644 --- a/src/codebook/settings/updateSettings.js +++ b/src/codebook/settings/updateSettings.js @@ -2,21 +2,24 @@ import { select as d3select } from 'd3'; import reset from './updateSettings/reset'; export default function updateSettings(codebook, column) { - const setting = column === 'Label' - ? 'variableLabels' - : column === 'Group' - ? 'groups' - : column === 'Filter' - ? 'filters' - : column === 'Hide' - ? 'hiddenVariables' - : console.warn('Something unsetting has occurred...'); + const setting = + column === 'Label' + ? 'variableLabels' + : column === 'Group' + ? 'groups' + : column === 'Filter' + ? 'filters' + : column === 'Hide' + ? 'hiddenVariables' + : console.warn('Something unsetting has occurred...'); const inputs = codebook.settings.wrap.selectAll(`.column-table td.${column}`); if (['Group', 'Filter', 'Hide'].indexOf(column) > -1) { //redefine settings array codebook.config[setting] = inputs .filter(function() { - return d3select(this).select('input').property('checked'); + return d3select(this) + .select('input') + .property('checked'); }) .data() .map(d => { @@ -26,7 +29,9 @@ export default function updateSettings(codebook, column) { //redefine settings array codebook.config[setting] = inputs .filter(function(d) { - d.value.label = d3select(this).select('input').property('value'); + d.value.label = d3select(this) + .select('input') + .property('value'); return d.value.label !== ''; }) .data() diff --git a/src/codebook/settings/updateSettings/reset.js b/src/codebook/settings/updateSettings/reset.js index 2f4fe4f..4053cc1 100644 --- a/src/codebook/settings/updateSettings/reset.js +++ b/src/codebook/settings/updateSettings/reset.js @@ -20,10 +20,9 @@ export default function reset(codebook) { //redraw data summary, codebook, and listing. codebook.data.makeSummary(codebook); - codebook.title.updateColumnCount(codebook); + codebook.title.updateCountSummary(codebook); codebook.summaryTable.draw(codebook); codebook.dataListing.init(codebook); codebook.chartMaker.init(codebook); - codebook.controls.updateRowCount(codebook); }); } diff --git a/src/codebook/summaryTable/draw.js b/src/codebook/summaryTable/draw.js index 32a0f2b..0862884 100644 --- a/src/codebook/summaryTable/draw.js +++ b/src/codebook/summaryTable/draw.js @@ -18,9 +18,12 @@ export function draw(codebook) { .data(codebook.data.summary, d => d.value_col); //ENTER - varRows.enter().append('div').attr('class', function(d) { - return 'variable-row ' + d.type; - }); + varRows + .enter() + .append('div') + .attr('class', function(d) { + return 'variable-row ' + d.type; + }); //Hide variable rows corresponding to variables specified in settings.hiddenVariables. varRows.classed( @@ -31,14 +34,13 @@ export function draw(codebook) { //Set chart visibility (on initial load only - then keep user settings) if (codebook.config.chartVisibility != 'user-defined') { varRows.classed( - 'hiddenChart', + 'hiddenDetails', codebook.config.chartVisibility != 'visible' ); } - codebook.config.chartVisibility = codebook.config.chartVisibility == 'hidden' - ? 'hidden' - : 'user-defined'; + codebook.config.chartVisibility = + codebook.config.chartVisibility == 'hidden' ? 'hidden' : 'user-defined'; //ENTER + Update varRows.each(codebook.summaryTable.renderRow); diff --git a/src/codebook/summaryTable/renderRow.js b/src/codebook/summaryTable/renderRow.js index 9085ac9..e6b5b23 100644 --- a/src/codebook/summaryTable/renderRow.js +++ b/src/codebook/summaryTable/renderRow.js @@ -4,6 +4,7 @@ import makeChart from './renderRow/makeChart.js'; import makeDetails from './renderRow/makeDetails.js'; +import makeMeta from './renderRow/makeMeta.js'; import makeTitle from './renderRow/makeTitle.js'; import { select as d3select } from 'd3'; @@ -12,12 +13,25 @@ export function renderRow(d) { var rowWrap = d3select(this); rowWrap.selectAll('*').remove(); - var rowHead = rowWrap.append('div').attr('class', 'row-head section'); + rowWrap + .append('div') + .attr('class', 'row-head section') + .append('div') + .attr('class', 'row-title') + .each(makeTitle); - rowHead.append('div').attr('class', 'row-title').each(makeTitle); - //rowHead.append('div').attr('class', 'row-values').each(makeValues); + rowWrap + .append('div') + .attr('class', 'row-details section') + .each(makeDetails); - rowWrap.append('div').attr('class', 'row-chart section').each(makeChart); + rowWrap + .append('div') + .attr('class', 'row-chart section') + .each(makeChart); - rowHead.append('div').attr('class', 'row-details').each(makeDetails); + rowWrap + .append('div') + .attr('class', 'row-meta section') + .each(makeMeta); } diff --git a/src/codebook/summaryTable/renderRow/details/renderMeta.js b/src/codebook/summaryTable/renderRow/details/renderMeta.js index 66b6a92..3c643b8 100644 --- a/src/codebook/summaryTable/renderRow/details/renderMeta.js +++ b/src/codebook/summaryTable/renderRow/details/renderMeta.js @@ -14,20 +14,26 @@ export default function renderMeta(d, list) { //render the items var metaItems = list .selectAll('li.meta') - .data(d.meta) + .data(d.meta.filter(f => f.key != 'Type')) .enter() .append('li') .classed('meta', true) .classed('hidden', d => d.hidden); - metaItems.append('div').text(d => d.key).attr('class', 'wcb-label'); - metaItems.append('div').text(d => d.value).attr('class', 'value'); + metaItems + .append('div') + .text(d => d.key) + .attr('class', 'wcb-label'); + metaItems + .append('div') + .text(d => d.value) + .attr('class', 'value'); if (dropped.length) { list .append('li') - .append('div') .attr('class', 'details') + .append('div') .html('ⓘ') .property( 'title', diff --git a/src/codebook/summaryTable/renderRow/details/renderStats.js b/src/codebook/summaryTable/renderRow/details/renderStats.js index 29a1c3e..c412bee 100644 --- a/src/codebook/summaryTable/renderRow/details/renderStats.js +++ b/src/codebook/summaryTable/renderRow/details/renderStats.js @@ -1,15 +1,23 @@ //Render Summary Stats export default function renderStats(d, list) { - list.selectAll('*').remove(); + var ignoreStats = [ + 'values', + 'highlightValues', + 'min', + 'max', + 'n', + 'N', + 'nMissing', + 'percentMissing' + ]; - var ignoreStats = ['values', 'highlightValues', 'min', 'max']; var statNames = Object.keys(d.statistics) .filter(f => ignoreStats.indexOf(f) === -1) //remove value lists .filter(f => f.indexOf('ile') === -1); //remove "percentiles" var statList = statNames.map(stat => { return { - key: stat !== 'nMissing' ? stat : 'Missing', + key: stat !== 'missingSummary' ? stat : 'Missing', value: d.statistics[stat] }; }); @@ -20,6 +28,12 @@ export default function renderStats(d, list) { .enter() .append('li') .attr('class', 'stat'); - stats.append('div').text(d => d.key).attr('class', 'wcb-label'); - stats.append('div').text(d => d.value).attr('class', 'value'); + stats + .append('div') + .text(d => d.key) + .attr('class', 'wcb-label'); + stats + .append('div') + .text(d => d.value) + .attr('class', 'value'); } diff --git a/src/codebook/summaryTable/renderRow/details/renderValues.js b/src/codebook/summaryTable/renderRow/details/renderValues.js index bb4154c..dbff392 100644 --- a/src/codebook/summaryTable/renderRow/details/renderValues.js +++ b/src/codebook/summaryTable/renderRow/details/renderValues.js @@ -6,8 +6,6 @@ import { } from 'd3'; export default function renderValues(d, list) { - list.selectAll('*').remove(); - //make a list of values if (d.type == 'categorical') { var topValues = d.statistics.values @@ -18,9 +16,19 @@ export default function renderValues(d, list) { return i < 5; }); - var valueItems = list.selectAll('li').data(topValues).enter().append('li'); + var valueItems = list + .selectAll('li.value') + .data(topValues) + .enter() + .append('li') + .attr('class', 'value'); + + valueItems + .append('div') + .text(d => d.key) + .attr('class', 'wcb-label') + .attr('title', d => d.key); - valueItems.append('div').text(d => d.key).attr('class', 'wcb-label'); valueItems .append('div') .text(d => d.n + ' (' + d3format('0.1%')(d.prop_n) + ')') @@ -31,6 +39,7 @@ export default function renderValues(d, list) { var extraCount = totLength - 5; var extra_span = list .append('li') + .attr('class', 'value') .append('div') .attr('class', 'wcb-label') .html('and ' + extraCount + ' more.'); @@ -54,11 +63,19 @@ export default function renderValues(d, list) { } else { var valList = sortedValues; } - var valueItems = list.selectAll('li').data(valList).enter().append('li'); + var valueItems = list + .selectAll('li.value') + .data(valList) + .enter() + .append('li') + .attr('class', 'value'); - valueItems.append('div').attr('class', 'wcb-label').text(function(d, i) { - return i == 0 ? 'Min' : i == valList.length - 1 ? 'Max' : ' '; - }); + valueItems + .append('div') + .attr('class', 'wcb-label') + .text(function(d, i) { + return i == 0 ? 'Min' : i == valList.length - 1 ? 'Max' : ' '; + }); valueItems .append('div') .attr('class', 'value') diff --git a/src/codebook/summaryTable/renderRow/makeDetails.js b/src/codebook/summaryTable/renderRow/makeDetails.js index 764f336..56ba53f 100644 --- a/src/codebook/summaryTable/renderRow/makeDetails.js +++ b/src/codebook/summaryTable/renderRow/makeDetails.js @@ -1,33 +1,19 @@ import { select as d3select } from 'd3'; import detailList from './details/detailList'; import renderStats from './details/renderStats'; +import renderValues from './details/renderValues'; export default function makeDetails(d) { - var list = d3select(this).append('div').append('ul'); - var parent = d3select(this.parentNode.parentNode); - var controls = parent - .select('.row-chart') - .select('.row-controls') - .append('div') - .attr('class', 'detail-controls'); - - controls.append('small').html('Header Details: '); - var detailSelect = controls.append('select'); + var stat_list = d3select(this) + .append('ul') + .attr('class', 'stats'); + var val_list = d3select(this) + .append('ul') + .attr('class', 'values'); - var detailItems = detailSelect - .selectAll('option') - .data(detailList) - .enter() - .append('option') - .html(d => d.key); - - //Handlers for label events - detailSelect.on('change', function() { - var current = this.value; - var detailObj = detailList.filter(f => f.key == current)[0]; - detailObj.action(d, list); - }); + var parent = d3select(this.parentNode.parentNode); - //render stats on initial load - renderStats(d, list); + //render stats & values on initial load + renderStats(d, stat_list); + renderValues(d, val_list); } diff --git a/src/codebook/summaryTable/renderRow/makeMeta.js b/src/codebook/summaryTable/renderRow/makeMeta.js new file mode 100644 index 0000000..a543ab0 --- /dev/null +++ b/src/codebook/summaryTable/renderRow/makeMeta.js @@ -0,0 +1,17 @@ +import { select as d3select } from 'd3'; +import renderMeta from './details/renderMeta'; + +export default function makeMeta(d) { + var hasMeta = + d.meta.filter(f => !f.hidden).filter(f => f.key != 'Type').length > 0; + if (hasMeta) { + var meta_list = d3select(this) + .append('ul') + .attr('class', 'meta'); + + var parent = d3select(this.parentNode.parentNode); + renderMeta(d, meta_list); + } else { + d3select(this).style('display', 'none'); + } +} diff --git a/src/codebook/summaryTable/renderRow/makeTitle.js b/src/codebook/summaryTable/renderRow/makeTitle.js index cd06751..dbbcfc1 100644 --- a/src/codebook/summaryTable/renderRow/makeTitle.js +++ b/src/codebook/summaryTable/renderRow/makeTitle.js @@ -1,9 +1,13 @@ import { select as d3select } from 'd3'; +import { format as d3format } from 'd3'; +import createSpark from '../../../charts/createSpark'; export default function makeTitle(d) { var rowDiv = d3select(this.parentNode.parentNode.parentNode); var chartDiv = rowDiv.select('.row-chart'); - var hiddenFlag = rowDiv.classed('hiddenChart'); + var hiddenFlag = rowDiv.classed('hiddenDetails'); + + //Add row toggle d3select(this) .append('div') .attr('class', 'row-toggle') @@ -14,20 +18,50 @@ export default function makeTitle(d) { .on('click', function() { var rowDiv = d3select(this.parentNode.parentNode.parentNode); var chartDiv = rowDiv.select('.row-chart'); - var hiddenFlag = rowDiv.classed('hiddenChart'); - rowDiv.classed('hiddenChart', !hiddenFlag); + var hiddenFlag = rowDiv.classed('hiddenDetails'); + rowDiv.classed('hiddenDetails', !hiddenFlag); d3select(this).html(hiddenFlag ? '▼' : '►'); }); + //add variable name in quotes d3select(this) .append('span') .attr('class', 'title-span') .text(d => "'" + d.value_col + "'"); + //add variable label (if any) if (d.value_col != d.label) { d3select(this) .append('span') .attr('class', 'label-span') .text(d => d.label); } + + //add variable type + /* + d3select(this) + .append('span') + .attr('class', 'type') + .text(d => d.type); + */ + + //add sparklines + d3select(this) + .append('div') + .attr('class', 'spark') + .datum(d) + .each(createSpark); + + //add percent missing (if > 0%) + d3select(this) + .append('span') + .attr('class', 'percent-missing') + .text(d => d3format('0.1%')(d.statistics.percentMissing) + ' missing') + .style('display', d => (d.statistics.percentMissing == 0 ? 'none' : null)) + .style('cursor', 'pointer') + .style('color', d => (d.statistics.percentMissing >= 0.1 ? 'red' : '#999')) + .attr( + 'title', + d => d.statistics.nMissing + ' of ' + d.statistics.N + ' missing' + ); } diff --git a/src/codebook/title.js b/src/codebook/title.js index 894263b..4e2902d 100644 --- a/src/codebook/title.js +++ b/src/codebook/title.js @@ -3,9 +3,11 @@ \------------------------------------------------------------------------------------------------*/ import { init } from './title/init'; -import { updateColumnCount } from './title/updateColumnCount'; +import { highlight } from './title/highlight'; +import { updateCountSummary } from './title/updateCountSummary'; export const title = { init: init, - updateColumnCount: updateColumnCount + highlight: highlight, + updateCountSummary: updateCountSummary }; diff --git a/src/codebook/controls/highlight.js b/src/codebook/title/highlight.js similarity index 100% rename from src/codebook/controls/highlight.js rename to src/codebook/title/highlight.js diff --git a/src/codebook/controls/highlight/init.js b/src/codebook/title/highlight/init.js similarity index 85% rename from src/codebook/controls/highlight/init.js rename to src/codebook/title/highlight/init.js index 492c14f..8ac521a 100644 --- a/src/codebook/controls/highlight/init.js +++ b/src/codebook/title/highlight/init.js @@ -4,7 +4,7 @@ export function init(codebook) { //initialize the wrapper - codebook.controls.highlight.clearButton = codebook.controls.summaryWrap + codebook.title.highlight.clearButton = codebook.title.wrap .append('button') .classed('clear-highlight', true) .classed('hidden', codebook.data.highlighted.length == 0) @@ -16,6 +16,6 @@ export function init(codebook) { codebook.dataListing.init(codebook); codebook.summaryTable.draw(codebook); codebook.chartMaker.draw(codebook); - codebook.controls.updateRowCount(codebook); + codebook.title.updateCountSummary(codebook); }); } diff --git a/src/codebook/title/init.js b/src/codebook/title/init.js index 08ec1fe..8323df7 100644 --- a/src/codebook/title/init.js +++ b/src/codebook/title/init.js @@ -8,9 +8,11 @@ export function init(codebook) { : 'Codebook' ); - codebook.title.countSpan = codebook.title.wrap + codebook.title.countSummary = codebook.title.wrap .append('span') - .attr('class', 'columnCount'); + .attr('class', 'countSummary'); - codebook.title.updateColumnCount(codebook); + codebook.title.highlight.init(codebook); + + codebook.title.updateCountSummary(codebook); } diff --git a/src/codebook/title/updateColumnCount.js b/src/codebook/title/updateColumnCount.js deleted file mode 100644 index 67bd557..0000000 --- a/src/codebook/title/updateColumnCount.js +++ /dev/null @@ -1,10 +0,0 @@ -import { format as d3format } from 'd3'; - -export function updateColumnCount(codebook) { - var nCols_sub = codebook.data.summary.filter(d => !d.hidden).length; - var nCols_all = codebook.data.summary.length - 1; //-1 is for the index var - var percent = d3format('0.1%')(nCols_sub / nCols_all); - var tableSummary = - nCols_sub + ' of ' + nCols_all + ' (' + percent + ') columns selected.'; - codebook.title.countSpan.text(tableSummary); -} diff --git a/src/codebook/title/updateCountSummary.js b/src/codebook/title/updateCountSummary.js new file mode 100644 index 0000000..938f6b5 --- /dev/null +++ b/src/codebook/title/updateCountSummary.js @@ -0,0 +1,40 @@ +import { format as d3format } from 'd3'; + +export function updateCountSummary(codebook) { + var warn = false; + //get number of rows shown + if (codebook.data.summary.length > 0) { + var nShown = codebook.data.summary[0].statistics.N; + var nTot = codebook.data.raw.length; + var percent = d3format('0.1%')(nShown / nTot); + var rowSummary = + nShown + ' of ' + nTot + ' (' + percent + ') rows selected'; + } else { + var rowSummary = 'No rows selected.'; + warn = true; + } + + //Add note regarding highlighted cells and show/hide the clear highlight button + var highlightSummary = + codebook.data.highlighted.length > 0 + ? ' and ' + + codebook.data.highlighted.length + + ' highlighted. ' + : '.'; + + codebook.title.highlight.clearButton.classed( + 'hidden', + codebook.data.highlighted.length == 0 + ); + + //get number of columns hidden + var nCols_sub = codebook.data.summary.filter(d => !d.hidden).length; + var nCols_all = codebook.data.summary.length - 1; //-1 is for the index var + var nCols_diff = nCols_all - nCols_sub; + //var percent = d3format('0.1%')(nCols_sub / nCols_all); + var colSummary = nCols_diff > 0 ? nCols_diff + ' columns hidden' : ''; + + var tableSummary = rowSummary + highlightSummary + ' ' + colSummary; + + codebook.title.countSummary.html(tableSummary); +} diff --git a/src/codebook/util/clone.js b/src/codebook/util/clone.js deleted file mode 100644 index 8b6e812..0000000 --- a/src/codebook/util/clone.js +++ /dev/null @@ -1,33 +0,0 @@ -export default function clone(obj) { - let copy; - - //boolean, number, string, null, undefined - if ('object' != typeof obj || null == obj) return obj; - - //date - if (obj instanceof Date) { - copy = new Date(); - copy.setTime(obj.getTime()); - return copy; - } - - //array - if (obj instanceof Array) { - copy = []; - for (var i = 0, len = obj.length; i < len; i++) { - copy[i] = clone(obj[i]); - } - return copy; - } - - //object - if (obj instanceof Object) { - copy = {}; - for (var attr in obj) { - if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]); - } - return copy; - } - - throw new Error('Unable to copy [obj]! Its type is not supported.'); -} diff --git a/src/codebook/util/getBinCounts.js b/src/codebook/util/getBinCounts.js index d023db1..00e05ed 100644 --- a/src/codebook/util/getBinCounts.js +++ b/src/codebook/util/getBinCounts.js @@ -20,11 +20,12 @@ export function getBinCounts(codebook) { var n = summaryData.statistics['n']; var range = +summaryData.statistics['max'] - +summaryData.statistics['min']; var binSize = FreedmanDiaconis(IQR, n); - var bins = binSize > 0 - ? Math.ceil(range / binSize) - : codebook.config.nBins > 0 - ? codebook.config.nBins - : defaultSettings.nBins; + var bins = + binSize > 0 + ? Math.ceil(range / binSize) + : codebook.config.nBins > 0 + ? codebook.config.nBins + : defaultSettings.nBins; return bins; } diff --git a/src/codebook/util/setDefaults.js b/src/codebook/util/setDefaults.js index 6c14289..00cb4e5 100644 --- a/src/codebook/util/setDefaults.js +++ b/src/codebook/util/setDefaults.js @@ -25,11 +25,12 @@ export function setDefaults(codebook) { }); //autofilter - don't use automatic filter if user specifies filters object - codebook.config.autofilter = codebook.config.filters.length > 0 - ? false - : codebook.config.autofilter == null - ? defaultSettings.autofilter - : codebook.config.autofilter; + codebook.config.autofilter = + codebook.config.filters.length > 0 + ? false + : codebook.config.autofilter == null + ? defaultSettings.autofilter + : codebook.config.autofilter; /********************* Group Settings *********************/ codebook.config.groups = codebook.config.groups || defaultSettings.groups; @@ -78,11 +79,12 @@ export function setDefaults(codebook) { : defaultSettings.variableLabels; } //autogroups - don't use automatic groups if user specifies groups object - codebook.config.autogroups = codebook.config.groups.length > 0 - ? false - : codebook.config.autogroups == null - ? defaultSettings.autogroups - : codebook.config.autogroups; + codebook.config.autogroups = + codebook.config.groups.length > 0 + ? false + : codebook.config.autogroups == null + ? defaultSettings.autogroups + : codebook.config.autogroups; /********************* Hidden Variable Settings ***************/ codebook.config.hiddenVariables = @@ -91,9 +93,10 @@ export function setDefaults(codebook) { /********************* Histogram Settings *********************/ codebook.config.nBins = codebook.config.nBins || defaultSettings.nBins; - codebook.config.autobins = codebook.config.autobins == null - ? defaultSettings.autobins - : codebook.config.autobins; + codebook.config.autobins = + codebook.config.autobins == null + ? defaultSettings.autobins + : codebook.config.autobins; codebook.config.levelSplit = codebook.config.levelSplit || defaultSettings.levelSplit; @@ -130,10 +133,8 @@ export function setDefaults(codebook) { codebook.config.chartVisibility || defaultSettings.chartVisibility; //hide the controls appropriately according to the start tab - if (codebook.config.controlVisibility != 'disabled') { - var startTab = availableTabs.filter( - f => f.key == codebook.config.defaultTab - ); + if (codebook.config.controlVisibility !== 'disabled') { + var startTab = availableTabs.find(f => f.key == codebook.config.defaultTab); codebook.config.controlVisibility = startTab.controls ? codebook.config.controlVisibility : 'hidden'; diff --git a/src/createCodebook.js b/src/createCodebook.js index bf45c19..0359745 100644 --- a/src/createCodebook.js +++ b/src/createCodebook.js @@ -10,6 +10,7 @@ import { data } from './codebook/data'; import { settings } from './codebook/settings'; import { title } from './codebook/title'; import { instructions } from './codebook/instructions'; +import clone from './util/clone'; export function createCodebook(element = 'body', config) { let codebook = { @@ -29,20 +30,21 @@ export function createCodebook(element = 'body', config) { settings: settings }; - codebook.events = { + var cbClone = clone(codebook); + cbClone.events = { init() {}, complete() {} }; - codebook.on = function(event, callback) { + cbClone.on = function(event, callback) { let possible_events = ['init', 'complete']; if (possible_events.indexOf(event) < 0) { return; } if (callback) { - codebook.events[event] = callback; + cbClone.events[event] = callback; } }; - return codebook; + return cbClone; } diff --git a/src/explorer/fileListing/onDraw.js b/src/explorer/fileListing/onDraw.js index 6527392..b3ffe6e 100644 --- a/src/explorer/fileListing/onDraw.js +++ b/src/explorer/fileListing/onDraw.js @@ -6,21 +6,19 @@ export function onDraw(explorer) { this.table .select('tbody') .selectAll('tr') - .filter(f => f.raw == explorer.current) + .filter(f => f.path === explorer.current.path) .classed('selected', true); //Linkify the labelColumn var labelCells = this.table - .selectAll('td') - .filter(f => f.col == explorer.config.labelColumn) - .classed('link', true) - .on('click', function() { - var current_text = d3select(this).text(); - explorer.current = explorer.config.files.filter( - f => f[explorer.config.labelColumn] == current_text - )[0]; + .selectAll('tbody tr') + .on('click', function(d) { + explorer.current = d; explorer.current.event = 'click'; explorer.makeCodebook(explorer); - }); + }) + .selectAll('td') + .filter(f => f.col == explorer.config.labelColumn) + .classed('link', true); }); } diff --git a/src/explorer/makeCodebook.js b/src/explorer/makeCodebook.js index 832e19f..ffed348 100644 --- a/src/explorer/makeCodebook.js +++ b/src/explorer/makeCodebook.js @@ -11,13 +11,14 @@ export function makeCodebook(explorer) { //set the default tab to the codebook or listing view assuming they are visible if (this.current.event == 'click') { - this.current.settings.defaultTab = this.current.settings.tabs.indexOf( - 'codebook' - ) > -1 - ? 'codebook' - : this.current.settings.tabs.indexOf('listing') > -1 - ? 'listing' - : 'files'; + this.current.settings.defaultTab = + this.current.settings.tabs + .map(tab => (tab.key ? tab.key : tab)) + .indexOf('codebook') > -1 + ? 'codebook' + : this.current.settings.tabs.indexOf('listing') > -1 + ? 'listing' + : 'files'; } this.current.settings.dataName = diff --git a/src/index.js b/src/index.js index d0ca3b6..0291e23 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +import './polyfills/index'; import { createCodebook } from './createCodebook'; import { createExplorer } from './createExplorer'; import { charts } from './charts'; diff --git a/src/polyfills/array-find.js b/src/polyfills/array-find.js new file mode 100644 index 0000000..7010764 --- /dev/null +++ b/src/polyfills/array-find.js @@ -0,0 +1,43 @@ +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, 'length')). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, � kValue, k, O �)). + // d. If testResult is true, return kValue. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; + } + }); +} diff --git a/src/polyfills/array-findIndex.js b/src/polyfills/array-findIndex.js new file mode 100644 index 0000000..38a2653 --- /dev/null +++ b/src/polyfills/array-findIndex.js @@ -0,0 +1,43 @@ +if (!Array.prototype.findIndex) { + Object.defineProperty(Array.prototype, 'findIndex', { + value: function(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, � kValue, k, O �)). + // d. If testResult is true, return k. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return k; + } + // e. Increase k by 1. + k++; + } + + // 7. Return -1. + return -1; + } + }); +} diff --git a/src/polyfills/index.js b/src/polyfills/index.js new file mode 100644 index 0000000..518132b --- /dev/null +++ b/src/polyfills/index.js @@ -0,0 +1,3 @@ +import './object-assign'; +import './array-find'; +import './array-findIndex'; diff --git a/src/polyfills/object-assign.js b/src/polyfills/object-assign.js new file mode 100644 index 0000000..e3d61f9 --- /dev/null +++ b/src/polyfills/object-assign.js @@ -0,0 +1,33 @@ +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'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { + // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + + return to; + }, + writable: true, + configurable: true + }); +} diff --git a/src/util/object-assign.js b/src/util/object-assign.js deleted file mode 100644 index 048999e..0000000 --- a/src/util/object-assign.js +++ /dev/null @@ -1,24 +0,0 @@ -if (typeof Object.assign != 'function') { - (function() { - Object.assign = function(target) { - 'use strict'; - if (target === undefined || target === null) - throw new TypeError('Cannot convert undefined or null to object'); - - const output = Object(target); - - for (let index = 1; index < arguments.length; index++) { - const source = arguments[index]; - - if (source !== undefined && source !== null) { - for (let nextKey in source) { - if (source.hasOwnProperty(nextKey)) - output[nextKey] = source[nextKey]; - } - } - } - - return output; - }; - })(); -}