diff --git a/lib/gui/app/app.js b/lib/gui/app/app.js index 01a0dfc6..2f1bb576 100644 --- a/lib/gui/app/app.js +++ b/lib/gui/app/app.js @@ -77,6 +77,7 @@ const app = angular.module('Etcher', [ require('./components/svg-icon'), require('./components/warning-modal/warning-modal'), require('./components/safe-webview'), + require('./components/file-selector'), // Pages require('./pages/main/main'), diff --git a/lib/gui/app/components/file-selector/controllers/file-selector.js b/lib/gui/app/components/file-selector/controllers/file-selector.js new file mode 100644 index 00000000..b56057b3 --- /dev/null +++ b/lib/gui/app/components/file-selector/controllers/file-selector.js @@ -0,0 +1,48 @@ +/* + * Copyright 2018 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +module.exports = function ( + $uibModalInstance +) { + /** + * @summary Close the modal + * @function + * @public + * + * @example + * FileSelectorController.close(); + */ + this.close = () => { + $uibModalInstance.close() + } + + /** + * @summary Folder to constrain the file picker to + * @function + * @public + * + * @returns {String} - folder to constrain by + * + * @example + * FileSelectorController.getFolderConstraint() + */ + this.getFolderConstraint = () => { + // TODO(Shou): get this dynamically from the mountpoint of a specific port in Etcher Pro + return process.env.ETCHER_FILE_BROWSER_CONSTRAIN_FOLDER + } +} diff --git a/lib/gui/app/components/file-selector/file-selector/file-selector.jsx b/lib/gui/app/components/file-selector/file-selector/file-selector.jsx new file mode 100644 index 00000000..95a26d64 --- /dev/null +++ b/lib/gui/app/components/file-selector/file-selector/file-selector.jsx @@ -0,0 +1,620 @@ +/* + * Copyright 2018 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const _ = require('lodash') +const os = require('os') +const path = require('path') +const React = require('react') +const propTypes = require('prop-types') +const { default: styled } = require('styled-components') +const rendition = require('rendition') +const prettyBytes = require('pretty-bytes') +const Bluebird = require('bluebird') +const fontAwesome = require('@fortawesome/fontawesome') +const { + faFileAlt, + faFolder, + faAngleLeft, + faHdd +} = require('@fortawesome/fontawesome-free-solid') +const Storage = require('../../../models/storage') +const analytics = require('../../../modules/analytics') +const middleEllipsis = require('../../../utils/middle-ellipsis') +const files = require('../../../../../shared/files') +const selectionState = require('../../../../../shared/models/selection-state') +const imageStream = require('../../../../../sdk/image-stream') +const errors = require('../../../../../shared/errors') +const messages = require('../../../../../shared/messages') +const supportedFormats = require('../../../../../shared/supported-formats') + +/** + * @summary Recent files localStorage object key + * @constant + * @private + */ +const RECENT_FILES_KEY = 'file-selector-recent-files' +const recentStorage = new Storage(RECENT_FILES_KEY) + +/** + * @summary How many directories to show with the breadcrumbs + * @type {Number} + * @constant + * @private + */ +const MAX_DIR_CRUMBS = 3 + +/** + * @summary Character limit of a filename before a middle-ellipsis is added + * @constant + * @private + */ +const FILENAME_CHAR_LIMIT = 20 + +/** + * @summary Character limit of a filename before a middle-ellipsis is added + * @constant + * @private + */ +const FILENAME_CHAR_LIMIT_SHORT = 15 + +/** + * @summary Color scheme + * @constant + * @private + */ +const colors = { + primary: { + color: '#3a3c41', + background: '#ffffff', + subColor: '#ababab' + }, + secondary: { + color: '#1c1d1e', + background: '#ebeff4', + title: '#b3b6b9' + }, + highlight: { + color: 'white', + background: '#2297de' + } +} + +/** + * @summary Awesome icons HTML object + * @constant + * @type {Object} + */ +const icons = { + faFileAlt: fontAwesome.icon(faFileAlt).html[0], + faFolder: fontAwesome.icon(faFolder).html[0], + faAngleLeft: fontAwesome.icon(faAngleLeft).html[0], + faHdd: fontAwesome.icon(faHdd).html[0] +} + +/** + * @summary Icon React component + * @function + * @type {ReactElement} + */ +const Icon = styled((props) => { + const { type, ...restProps } = props + + return ( + + ) +})` + color: ${props => props.color}; + font-size: ${props => props.size}; +` + +/** + * @summary Flex styled component + * @function + * @type {ReactElement} + */ +const Flex = styled.div` + display: flex; + flex: ${ props => props.flex }; + flex-direction: ${ props => props.direction }; + justify-content: ${ props => props.justifyContent }; + align-items: ${ props => props.alignItems }; + flex-wrap: ${ props => props.wrap }; + flex-grow: ${ props => props.grow }; +` + +const FileLink = styled((props) => { + const icon = props.isDirectory ? 'faFolder' : 'faFileAlt' + + return ( + + + + { middleEllipsis(props.basename || '', FILENAME_CHAR_LIMIT) } + +
{ prettyBytes(props.size || 0) }
+
+ ) +})` + width: 80px; + max-height: 128px; + margin: 5px 10px; + padding: 5px; + background-color: ${ props => props.highlight ? colors.highlight.background : 'none' }; + transition: 0.15s background-color ease-out; + color: ${ props => props.highlight ? colors.highlight.color : colors.primary.color }; + cursor: pointer; + border-radius: 5px; + + > span:first-child { + align-self: center; + line-height: 1; + margin-bottom: 6px; + } + + > button, + > button:hover, + > button:focus { + color: inherit; + word-break: break-all; + } + + > div:last-child { + background-color: ${ props => props.highlight ? colors.highlight.background : 'none' } + color: ${ props => props.highlight ? colors.highlight.color : colors.primary.subColor } + text-align: center; + font-size: 12px; + } +` + +const Header = Flex.extend` + margin: 10px 15px 0; + + > * { + margin: 5px; + } +` + +const Main = Flex.extend`` + +const Footer = Flex.extend` + margin: 10px 20px; + flex: 0 0 auto; + + > * { + margin: 0 10px; + } +` + +const FileListWrap = Flex.extend` + overflow-x: hidden; + overflow-y: auto; + padding: 0 20px; +` + +class RecentFilesUnstyled extends React.PureComponent { + constructor (props) { + super(props) + + this.state = { + recents: recentStorage.get('recents'), + favorites: recentStorage.get('favorites') + } + } + + render () { + const existing = (fileObjs) => { + return _.filter(fileObjs, (fileObj) => { + return files.exists(fileObj.fullpath) + }) + } + + return ( + +
Recent
+ { + _.map(existing(this.state.recents), (file) => { + return ( + this.props.selectFile(files.getFileMetadataSync(file.dirname)) } + plaintext={ true }> + { middleEllipsis(path.basename(file.dirname), FILENAME_CHAR_LIMIT_SHORT) } + + ) + }) + } +
Favorite
+ { + _.map(existing(this.state.favorites.slice(0, 4)), (file) => { + return ( + this.props.selectFile(files.getFileMetadataSync(file.fullpath)) } + plaintext={ true }> + { middleEllipsis(file.basename, FILENAME_CHAR_LIMIT_SHORT) } + + ) + }) + } +
+ ) + } + + componentWillMount () { + window.addEventListener('storage', this.onStorage) + } + + componentWillUnmount () { + window.removeEventListener('storage', this.onStorage) + } + + onStorage (event) { + if (event.key === RECENT_FILES_KEY) { + this.setState(event.newValue) + } + } +} + +const RecentFiles = styled(RecentFilesUnstyled)` + display: flex; + flex-direction: column; + align-items: flex-start; + width: 130px; + background-color: ${ colors.secondary.background }; + padding: 20px; + color: ${ colors.secondary.color }; + + > h5 { + color: ${ colors.secondary.title }; + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + margin-bottom: 15px; + } + + > button { + margin-bottom: 10px; + text-align: start; + } +` + +const labels = { + '/': 'Root' +} + +const Breadcrumbs = styled((props) => { + const folderConstraint = props.constraint || path.parse(props.path).root + const dirs = files.subpaths(props.path).filter((subpath) => { + // Guard against displaying folders outside the constrained folder + return !path.relative(folderConstraint, subpath.fullpath).startsWith('..') + }) + + return ( +
+ { dirs.length > MAX_DIR_CRUMBS ? '... / ' : null } + { + _.map(dirs.slice(-MAX_DIR_CRUMBS), (dir, index) => { + return ( + props.selectFile(dir) } + plaintext={ true }> + + { middleEllipsis(labels[dir.fullpath] || dir.basename, FILENAME_CHAR_LIMIT_SHORT) } + + + ) + }) + } +
+ ) +})` + font-size: 18px; + + & > button:not(:last-child)::after { + content: '/'; + margin: 9px; + } +` + +class FileSelector extends React.PureComponent { + constructor (props) { + super(props) + + const fullpath = props.path || os.homedir() + + this.state = { + path: fullpath, + files: [], + history: [], + highlighted: null, + error: null, + filters: [] + } + + // Filters schema + this.schema = { + type: 'object', + properties: { + basename: { + type: 'string' + }, + isHidden: { + type: 'boolean' + }, + isDirectory: { + type: 'boolean' + } + } + } + + this.closeModal = this.closeModal.bind(this) + this.browsePath = this.browsePath.bind(this) + this.selectFile = this.selectFile.bind(this) + this.previousDirectory = this.previousDirectory.bind(this) + } + + render () { + const items = rendition.SchemaSieve.filter(this.state.filters, this.state.files) + + const styles = { + display: 'flex', + height: 'calc(100vh - 20px)', + } + + const errorModal = ( + this.setState({ error: null }) } + action="Dismiss" + primaryButtonProps={ { danger: true, primary: false } } + > + { _.get(this.state.error, [ 'message' ]) || this.state.error } + + ) + + return ( + + + +
+ + +  Back + + + +
+
+ + this.setFilters(filters) } + onViewsUpdate={ views => this.setViews(views) } + schema={ this.schema } + renderMode={ [] } /> + + + { + items.map((item, index) => { + return ( + this.setState({ highlighted: item }) } + onDoubleClick={ _.partial(this.selectFile, item) } + /> + ) + }) + } + + +
+
+ Cancel + + Select file + +
+
+ { this.state.error ? errorModal : null } +
+ ) + } + + componentDidMount () { + this.setFilesProgressively(this.state.path) + } + + closeModal () { + this.props.close() + } + + setFilesProgressively (dirname) { + return files.getDirectory(dirname).then((basenames) => { + const fileObjs = basenames.map((basename) => { + return { + dirname: this.state.path, + basename, + fullpath: path.join(dirname, basename) + } + }) + + this.setState({ files: fileObjs }) + + return files.getAllFilesMetadataAsync(dirname, basenames) + }).then((fileObjs) => { + this.setState({ files: fileObjs }) + }) + } + + browsePath (file) { + analytics.logEvent('File browse', _.omit(file, [ + 'fullpath', + 'basename', + 'dirname' + ])) + + const folderConstraint = this.props.constraint || path.parse(this.state.path).root + + // Guard against browsing outside the constrained folder + if (path.relative(folderConstraint, file.fullpath).startsWith('..')) { + const error = `Cannot browse outside constrained folder ${constrainedFolder}` + analytics.logException(new Error(error)) + this.setState({ error }) + return + } + + this.setFilesProgressively(file.fullpath).then(() => { + this.setState({ path: file.fullpath }) + }) + } + + selectFile (file, event) { + if (file === null) { + analytics.logEvent('File dismiss', null) + } else if (file.isDirectory) { + const prevFile = files.getFileMetadataSync(this.state.path) + this.setState({ + history: this.state.history.concat(prevFile) + }) + this.browsePath(file) + } else { + analytics.logEvent('File select', file.basename) + + imageStream.getImageMetadata(file.fullpath) + .then((image) => { + if (!supportedFormats.isSupportedImage(image.path)) { + const invalidImageError = errors.createUserError({ + title: 'Invalid image', + description: messages.error.invalidImage(image) + }) + + this.setState({ error: invalidImageError }) + analytics.logEvent('Invalid image', image) + return + } + + return Bluebird.try(() => { + let message = null + + if (supportedFormats.looksLikeWindowsImage(image.path)) { + analytics.logEvent('Possibly Windows image', image) + message = messages.warning.looksLikeWindowsImage() + } else if (!image.hasMBR) { + analytics.logEvent('Missing partition table', image) + message = messages.warning.missingPartitionTable() + } + + if (message) { + // TODO(Shou): `Continue` should be on a red background (dangerous action) instead of `Change`. + // We want `X` to act as `Continue`, that's why `Continue` is the `rejectionLabel` + this.setState({ error: message }) + return + } + + return image + }).then((image) => { + if (image) { + selectionState.selectImage(image) + } + }) + }) + .catch((error) => { + const imageError = errors.createUserError({ + title: 'Error opening image', + description: messages.error.openImage(file.basename, error.message) + }) + + this.setState({ error: imageError }) + analytics.logException(error) + }) + + // Add folder to recently used + recentStorage.modify('recents', (recents) => { + const newRecents = _.uniqBy([ file ].concat(recents), (recentFile) => { + return recentFile.dirname + }) + + // NOTE(Shou): we want to limit how many recent directories are stored - since we only + // display four and don't rely on past state, we can only store four + return newRecents.slice(0, 4) + }, []) + + // Add file to potential favorites list + recentStorage.modify('favorites', (favorites) => { + const favorite = _.find(favorites, (favoriteFile) => { + return favoriteFile.fullpath === file.fullpath + }) || _.assign({}, file, { frequency: 1 }) + + const newFavorites = _.uniqBy([ favorite ].concat(favorites), (favoriteFile) => { + return favoriteFile.fullpath + }) + + // NOTE(Shou): we want to limit how many favorite files are stored - since we + // *do* rely on past state, we need to store a reasonable amount of favorites so + // they can be sorted by frequency. We only display four. + return newFavorites.slice(0, 256) + }, []) + + this.closeModal() + } + } + + previousDirectory () { + analytics.logEvent('Prev directory', null) + const dir = this.state.history.shift() + this.setState({ history: this.state.history }) + + if (dir) { + this.browsePath(dir) + } + } + + setFilters (filters) { + this.setState({ filters }) + } + + setViews (views) { + this.setState({ views }) + } +} + +FileSelector.propTypes = { + path: propTypes.string, + + close: propTypes.func, + + constraint: propTypes.string +} + +module.exports = FileSelector diff --git a/lib/gui/app/components/file-selector/index.js b/lib/gui/app/components/file-selector/index.js new file mode 100644 index 00000000..36f57f0b --- /dev/null +++ b/lib/gui/app/components/file-selector/index.js @@ -0,0 +1,37 @@ +/* + * Copyright 2018 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +/* eslint-disable jsdoc/require-example */ + +/** + * @module Etcher.Components.SVGIcon + */ + +const angular = require('angular') +const react2angular = require('react2angular').react2angular + +const MODULE_NAME = 'Etcher.Components.FileSelector' +const angularFileSelector = angular.module(MODULE_NAME, [ + require('../modal/modal') +]) + +angularFileSelector.component('fileSelector', react2angular(require('./file-selector/file-selector.jsx'))) +angularFileSelector.controller('FileSelectorController', require('./controllers/file-selector')) +angularFileSelector.service('FileSelectorService', require('./services/file-selector')) + +module.exports = MODULE_NAME diff --git a/lib/gui/app/components/file-selector/services/file-selector.js b/lib/gui/app/components/file-selector/services/file-selector.js new file mode 100644 index 00000000..8bcc5dda --- /dev/null +++ b/lib/gui/app/components/file-selector/services/file-selector.js @@ -0,0 +1,52 @@ +/* + * Copyright 2018 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +module.exports = function (ModalService, $q) { + let modal = null + + /** + * @summary Open the file selector widget + * @function + * @public + * + * @example + * DriveSelectorService.open() + */ + this.open = () => { + modal = ModalService.open({ + name: 'file-selector', + template: require('../templates/file-selector-modal.tpl.html'), + controller: 'FileSelectorController as selector', + size: 'file-selector-modal' + }) + } + + /** + * @summary Close the file selector widget + * @function + * @public + * + * @example + * DriveSelectorService.close() + */ + this.close = () => { + if (modal) { + modal.close() + } + } +} diff --git a/lib/gui/app/components/file-selector/styles/_file-selector.scss b/lib/gui/app/components/file-selector/styles/_file-selector.scss new file mode 100644 index 00000000..d7579c68 --- /dev/null +++ b/lib/gui/app/components/file-selector/styles/_file-selector.scss @@ -0,0 +1,23 @@ +/* + * Copyright 2018 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.modal-file-selector-modal { + width: calc(100vw - 10px); + + > .modal-content { + height: calc(100vh - 10px); + } +} diff --git a/lib/gui/app/components/file-selector/templates/file-selector-modal.tpl.html b/lib/gui/app/components/file-selector/templates/file-selector-modal.tpl.html new file mode 100644 index 00000000..9893fbc0 --- /dev/null +++ b/lib/gui/app/components/file-selector/templates/file-selector-modal.tpl.html @@ -0,0 +1 @@ + diff --git a/lib/gui/app/components/modal/styles/_modal.scss b/lib/gui/app/components/modal/styles/_modal.scss index da5d3b0a..7cb86814 100644 --- a/lib/gui/app/components/modal/styles/_modal.scss +++ b/lib/gui/app/components/modal/styles/_modal.scss @@ -20,6 +20,7 @@ flex-direction: column; margin: 0 auto; height: auto; + overflow: hidden; } .modal-header { @@ -101,5 +102,4 @@ .modal-dialog { margin: 0; position: initial; - max-width: 50%; } diff --git a/lib/gui/app/pages/main/controllers/image-selection.js b/lib/gui/app/pages/main/controllers/image-selection.js index 5992f844..8f35cd53 100644 --- a/lib/gui/app/pages/main/controllers/image-selection.js +++ b/lib/gui/app/pages/main/controllers/image-selection.js @@ -30,6 +30,7 @@ const exceptionReporter = require('../../../modules/exception-reporter') module.exports = function ( $timeout, + FileSelectorService, WarningModalService ) { /** @@ -155,16 +156,20 @@ module.exports = function ( this.openImageSelector = () => { analytics.logEvent('Open image selector') - osDialog.selectImage().then((imagePath) => { - // Avoid analytics and selection state changes - // if no file was resolved from the dialog. - if (!imagePath) { - analytics.logEvent('Image selector closed') - return - } + if (process.env.ETCHER_EXPERIMENTAL_FILE_PICKER) { + FileSelectorService.open() + } else { + osDialog.selectImage().then((imagePath) => { + // Avoid analytics and selection state changes + // if no file was resolved from the dialog. + if (!imagePath) { + analytics.logEvent('Image selector closed') + return + } - this.selectImageByPath(imagePath) - }).catch(exceptionReporter.report) + this.selectImageByPath(imagePath) + }).catch(exceptionReporter.report) + } } /** diff --git a/lib/gui/app/pages/main/main.js b/lib/gui/app/pages/main/main.js index 609399be..407ac59c 100644 --- a/lib/gui/app/pages/main/main.js +++ b/lib/gui/app/pages/main/main.js @@ -38,6 +38,7 @@ const MainPage = angular.module(MODULE_NAME, [ require('../../components/flash-error-modal/flash-error-modal'), require('../../components/progress-button/progress-button'), require('../../components/warning-modal/warning-modal'), + require('../../components/file-selector'), require('../../os/open-external/open-external'), require('../../os/dropzone/dropzone'), diff --git a/lib/gui/app/scss/main.scss b/lib/gui/app/scss/main.scss index dd573f52..f8890630 100644 --- a/lib/gui/app/scss/main.scss +++ b/lib/gui/app/scss/main.scss @@ -37,6 +37,7 @@ $disabled-opacity: 0.2; @import "../components/svg-icon/styles/svg-icon"; @import "../components/tooltip-modal/styles/tooltip-modal"; @import "../components/warning-modal/styles/warning-modal"; +@import "../components/file-selector/styles/file-selector"; @import "../pages/main/styles/main"; @import "../pages/settings/styles/settings"; @import "../pages/finish/styles/finish"; diff --git a/lib/gui/css/main.css b/lib/gui/css/main.css index f4df55c8..02caa55e 100644 --- a/lib/gui/css/main.css +++ b/lib/gui/css/main.css @@ -6225,7 +6225,8 @@ body { display: flex; flex-direction: column; margin: 0 auto; - height: auto; } + height: auto; + overflow: hidden; } .modal-header { display: flex; @@ -6277,8 +6278,7 @@ body { .modal-dialog { margin: 0; - position: initial; - max-width: 50%; } + position: initial; } /* * Copyright 2016 resin.io @@ -6487,6 +6487,26 @@ svg-icon { max-height: 200px; overflow-y: auto; } +/* + * Copyright 2018 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.modal-file-selector-modal { + width: calc(100vw - 10px); } + .modal-file-selector-modal > .modal-content { + height: calc(100vh - 10px); } + /* * Copyright 2016 resin.io * diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e1713218..0edeaacd 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -18,10 +18,34 @@ "version": "1.6.17", "resolved": "https://registry.npmjs.org/@types/angular/-/angular-1.6.17.tgz" }, + "@types/chart.js": { + "version": "2.7.18", + "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.18.tgz" + }, + "@types/color": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/color/-/color-2.0.1.tgz" + }, + "@types/color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz" + }, + "@types/color-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.0.tgz" + }, + "@types/grid-styled": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/grid-styled/-/grid-styled-3.2.2.tgz" + }, "@types/jquery": { "version": "2.0.43", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-2.0.43.tgz" }, + "@types/json-schema": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-6.0.1.tgz" + }, "@types/lodash": { "version": "4.14.64", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.64.tgz" @@ -38,14 +62,21 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/@types/lodash.mapvalues/-/lodash.mapvalues-4.6.2.tgz" }, + "@types/lodash.memoize": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.3.tgz" + }, "@types/lodash.some": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/@types/lodash.some/-/lodash.some-4.6.2.tgz" }, "@types/node": { "version": "7.0.58", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.58.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.58.tgz" + }, + "@types/prop-types": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.2.tgz" }, "@types/react": { "version": "15.0.24", @@ -55,6 +86,30 @@ "version": "15.5.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-15.5.0.tgz" }, + "@types/react-icon-base": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/react-icon-base/-/react-icon-base-2.1.1.tgz" + }, + "@types/react-icons": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@types/react-icons/-/react-icons-2.2.5.tgz" + }, + "@types/react-jsonschema-form": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/react-jsonschema-form/-/react-jsonschema-form-1.0.4.tgz" + }, + "@types/recompose": { + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/@types/recompose/-/recompose-0.24.7.tgz" + }, + "@types/semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz" + }, + "@types/uuid": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.3.tgz" + }, "7zip-bin": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-2.3.4.tgz", @@ -599,81 +654,66 @@ "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "dev": true, "dependencies": { "js-tokens": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" } } }, "babel-core": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "dev": true, "dependencies": { "babel-runtime": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" }, "lodash": { "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz" }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" }, "regenerator-runtime": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" } } }, "babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "dev": true, "dependencies": { "babel-runtime": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" }, "lodash": { "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz" }, "regenerator-runtime": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" }, "source-map": { "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" } } }, @@ -795,8 +835,7 @@ }, "babel-helpers": { "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz" }, "babel-loader": { "version": "7.1.2", @@ -805,8 +844,7 @@ }, "babel-messages": { "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz" }, "babel-plugin-check-es2015-constants": { "version": "6.22.0", @@ -1119,6 +1157,16 @@ "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", "dev": true }, + "babel-polyfill": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz" + } + } + }, "babel-preset-env": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", @@ -1164,127 +1212,104 @@ "babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "dev": true, "dependencies": { "babel-runtime": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" }, "core-js": { "version": "2.5.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz" }, "lodash": { "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz" }, "regenerator-runtime": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" }, "source-map-support": { "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz" } } }, "babel-runtime": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz", - "dev": true, "dependencies": { "regenerator-runtime": { "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz" } } }, "babel-template": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "dev": true, "dependencies": { "babel-runtime": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" }, "lodash": { "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz" }, "regenerator-runtime": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" } } }, "babel-traverse": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "dev": true, "dependencies": { "babel-runtime": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" }, "debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" }, "globals": { "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz" }, "lodash": { "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz" }, "regenerator-runtime": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" } } }, "babel-types": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "dev": true, "dependencies": { "babel-runtime": { "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" }, "lodash": { "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz" }, "regenerator-runtime": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz" } } }, "babylon": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz" }, "balanced-match": { "version": "0.4.2", @@ -1735,10 +1760,31 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" }, + "change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz" + }, "chardet": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz" + }, + "chart.js": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.2.tgz" + }, + "chartjs-color": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz" + } + } + }, + "chartjs-color-string": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz" }, "check-error": { "version": "1.0.2", @@ -1886,6 +1932,42 @@ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz", "dev": true }, + "clean-tag": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clean-tag/-/clean-tag-1.0.4.tgz", + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz" + }, + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "prop-types": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", + "dependencies": { + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz" + } + } + }, + "react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/react/-/react-16.3.2.tgz" + } + } + }, "cli-boxes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", @@ -1935,14 +2017,26 @@ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "dev": true }, + "color": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color/-/color-2.0.1.tgz" + }, "color-convert": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz" }, + "color-hash": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/color-hash/-/color-hash-1.0.3.tgz" + }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" }, + "color-string": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.2.tgz" + }, "columnify": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz" @@ -2032,8 +2126,7 @@ }, "convert-source-map": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz" }, "cookie": { "version": "0.3.1", @@ -2049,10 +2142,13 @@ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", "dev": true }, + "copy-to-clipboard": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz" + }, "core-js": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz" }, "core-util-is": { "version": "1.0.2", @@ -2130,16 +2226,28 @@ } } }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz" + }, "css-parse": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", "dev": true }, + "css-to-react-native": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.2.0.tgz" + }, "css-value": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/css-value/-/css-value-0.0.1.tgz", "dev": true }, + "csstype": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.0.tgz" + }, "cuint": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", @@ -2307,8 +2415,7 @@ }, "detect-indent": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz" }, "detect-libc": { "version": "1.0.3", @@ -3404,8 +3511,7 @@ }, "esutils": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" }, "event-emitter": { "version": "0.3.5", @@ -3484,17 +3590,14 @@ "external-editor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", - "dev": true, "dependencies": { "iconv-lite": { "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz" }, "tmp": { "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" } } }, @@ -3551,13 +3654,11 @@ }, "fast-deep-equal": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz" }, "fast-json-stable-stringify": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz" }, "fast-levenshtein": { "version": "2.0.6", @@ -4031,6 +4132,40 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" }, + "grid-styled": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/grid-styled/-/grid-styled-3.2.1.tgz", + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz" + }, + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "prop-types": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz" + }, + "styled-system": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/styled-system/-/styled-system-2.2.5.tgz" + } + } + }, "growl": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", @@ -4242,10 +4377,13 @@ "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" }, + "hoist-non-react-statics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz" + }, "home-or-tmp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz" }, "home-path": { "version": "1.0.5", @@ -4305,6 +4443,10 @@ "resolved": "https://registry.npmjs.org/html-tag/-/html-tag-0.2.1.tgz", "dev": true }, + "html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz" + }, "htmlparser2": { "version": "3.9.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz" @@ -4336,8 +4478,7 @@ }, "ieee754": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz" }, "ignore": { "version": "3.3.7", @@ -4435,8 +4576,7 @@ }, "invariant": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz" }, "invert-kv": { "version": "1.0.0", @@ -4615,12 +4755,10 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "dev": true, "dependencies": { "isobject": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" } } }, @@ -4751,8 +4889,7 @@ }, "jsesc": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz" }, "json-loader": { "version": "0.5.7", @@ -4765,8 +4902,7 @@ }, "json-schema-traverse": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz" }, "json-stable-stringify": { "version": "1.0.1", @@ -4784,8 +4920,7 @@ }, "json5": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz" }, "jsonfile": { "version": "2.4.0", @@ -4930,6 +5065,10 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz" }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + }, "lodash.mergewith": { "version": "4.6.1", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", @@ -4944,6 +5083,10 @@ "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", "dev": true }, + "lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz" + }, "log-update": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", @@ -5821,8 +5964,7 @@ }, "ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" }, "multistream": { "version": "2.1.0", @@ -6225,6 +6367,66 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz" }, + "opencollective": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/opencollective/-/opencollective-1.0.3.tgz", + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz" + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz" + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz" + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz" + }, + "inquirer": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.0.6.tgz" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz" + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz" + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz" + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" + } + } + } + } + }, + "opn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz" + }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -6566,6 +6768,10 @@ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "dev": true }, + "postcss-value-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz" + }, "prebuild-install": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.3.0.tgz", @@ -6597,8 +6803,7 @@ }, "private": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz" }, "process": { "version": "0.11.10", @@ -6807,6 +7012,24 @@ "version": "15.5.4", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.5.4.tgz" }, + "react-icon-base": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-icon-base/-/react-icon-base-2.1.0.tgz" + }, + "react-icons": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-2.2.7.tgz" + }, + "react-jsonschema-form": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.0.3.tgz", + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz" + } + } + }, "react2angular": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/react2angular/-/react2angular-1.1.3.tgz" @@ -6877,6 +7100,20 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz" }, + "recompose": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz", + "dependencies": { + "hoist-non-react-statics": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz" + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz" + } + } + }, "redent": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz" @@ -6927,6 +7164,10 @@ } } }, + "regex-parser": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.9.tgz" + }, "regexpu-core": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", @@ -7008,6 +7249,48 @@ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "dev": true }, + "rendition": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/rendition/-/rendition-4.6.0.tgz", + "dependencies": { + "@types/lodash": { + "version": "4.14.108", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.108.tgz" + }, + "@types/react": { + "version": "16.3.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.3.14.tgz" + }, + "@types/react-dom": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.5.tgz" + }, + "ajv": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz" + }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz" + }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz" + }, + "moment": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz" + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz" + } + } + }, "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", @@ -7152,6 +7435,20 @@ } } }, + "resin-device-status": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resin-device-status/-/resin-device-status-1.1.1.tgz" + }, + "resin-semver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/resin-semver/-/resin-semver-1.2.2.tgz", + "dependencies": { + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz" + } + } + }, "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", @@ -7208,6 +7505,10 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz" }, + "rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz" + }, "rx-lite": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz" @@ -7402,6 +7703,56 @@ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.6.1.tgz", "dev": true }, + "showdown": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.8.6.tgz", + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz" + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz" + }, + "yargs": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz" + }, + "yargs-parser": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.1.0.tgz" + } + } + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz" @@ -7461,6 +7812,16 @@ } } }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "dependencies": { + "is-arrayish": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.1.tgz" + } + } + }, "single-line-log": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", @@ -7483,8 +7844,7 @@ }, "slash": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" }, "slice-ansi": { "version": "0.0.4", @@ -7593,8 +7953,7 @@ }, "source-map": { "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz" }, "source-map-resolve": { "version": "0.3.1", @@ -7772,6 +8131,62 @@ "resolved": "https://registry.npmjs.org/striptags/-/striptags-2.2.1.tgz", "dev": true }, + "styled-components": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-3.2.3.tgz", + "dependencies": { + "buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.1.0.tgz" + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz" + } + } + }, + "styled-system": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/styled-system/-/styled-system-1.1.7.tgz", + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz" + }, + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz" + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + }, + "prop-types": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz" + } + } + }, + "stylis": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.0.tgz" + }, + "stylis-rule-sheet": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz" + }, "sudo-prompt": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-8.0.0.tgz" @@ -7972,6 +8387,10 @@ } } }, + "tag-hoc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tag-hoc/-/tag-hoc-1.0.0.tgz" + }, "tapable": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", @@ -8080,8 +8499,7 @@ }, "to-fast-properties": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" }, "to-file": { "version": "0.2.0", @@ -8174,6 +8592,10 @@ } } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz" + }, "touch": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz", @@ -8206,8 +8628,7 @@ }, "trim-right": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" }, "true-case-path": { "version": "1.0.2", @@ -8466,6 +8887,16 @@ "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", "dev": true }, + "uri-js": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.1.tgz", + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz" + } + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -9103,6 +9534,10 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", "dev": true }, + "xterm": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-3.3.0.tgz" + }, "xxhash": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/xxhash/-/xxhash-0.2.4.tgz" diff --git a/package.json b/package.json index 381f3aef..9b121490 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "react2angular": "1.1.3", "readable-stream": "2.3.3", "redux": "3.5.2", + "rendition": "4.6.0", "request": "2.81.0", "resin-cli-form": "1.4.1", "resin-cli-visuals": "1.4.1", @@ -85,6 +86,8 @@ "roboto-fontface": "0.9.0", "semver": "5.1.1", "speedometer": "1.0.0", + "styled-components": "3.2.3", + "styled-system": "1.1.7", "sudo-prompt": "8.0.0", "udif": "0.13.0", "unbzip2-stream": "github:resin-io-modules/unbzip2-stream#core-streams", diff --git a/tests/gui/pages/main.spec.js b/tests/gui/pages/main.spec.js index 80e6f1f0..e20c0c44 100644 --- a/tests/gui/pages/main.spec.js +++ b/tests/gui/pages/main.spec.js @@ -35,6 +35,10 @@ require.extensions['.html'] = (module, filename) => { }) } +// NOTE(Shou): since we don't test React yet we just ignore JSX files +// eslint-disable-next-line node/no-deprecated-api +require.extensions['.jsx'] = _.constant(null) + describe('Browser: MainPage', function () { beforeEach(angular.mock.module( require('../../../lib/gui/app/pages/main/main')