diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cdfdee7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +node_js: + - node +cache: + directories: + - node_modules +before_install: + - sudo apt-get -qq update + - sudo apt-get install -y build-essential libavahi-compat-libdnssd-dev +before_script: + - npm install -g gulp-cli +script: gulp +script: npm test \ No newline at end of file diff --git a/README.md b/README.md index a77e4b2..9bb608e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ -MultiCast v1.0 +MultiCast v2.0 ========= [![npm version](https://badge.fury.io/js/multicast.svg)](https://badge.fury.io/js/multicast) +[![Build Status](https://travis-ci.org/superhawk610/multicast.svg?branch=wip)](https://travis-ci.org/superhawk610/multicast) :green_heart: A persistent solution to presenting content across multiple Chromecast devices, inspired by [Greenscreen](http://greenscreen.io/). diff --git a/app/config.js b/app/config.js index a685d1e..ba66013 100644 --- a/app/config.js +++ b/app/config.js @@ -64,7 +64,8 @@ var startConfig = () => { console.log('') console.log('Configuration updated! To get started, just run\n' + '\n' + - ' multicast start') + ' multicast start' + '\n') }) rl.close() } else { diff --git a/app/lib/channels.js b/app/lib/channels.js new file mode 100644 index 0000000..b9e83cf --- /dev/null +++ b/app/lib/channels.js @@ -0,0 +1,44 @@ +'use strict' + +const Channel = require('../models/Channel') + +const devices = require('./devices') + +let channels = [] + +const func = { + init: () => func.refresh(), + refresh: () => { + Channel.find() + .sort('name') + .exec((err, _channels) => { + if (err) console.log(err) + channels = _channels + }) + }, + list: () => channels, + withId: id => channels.find(c => c._id == id), + create: (opts, callback) => { + var c = new Channel(opts) + c.save((err, channel) => { + if (err) console.log(err) + channels.push(c) + callback(channel._id) + }) + }, + update: (id, opts, callback) => { + Channel.update({ _id: id }, opts, err => { + Object.assign(func.withId(id), opts) + opts._id = id + devices.updateChannel(opts, () => callback(id)) + }) + }, + remove: (id, callback) => { + Channel.remove({ _id: id }, () => { + channels.splice(channels.findIndex(c => c._id == id), 1) // remove from local listing + devices.removeChannel(id, () => callback()) + }) + } +} + +module.exports = func \ No newline at end of file diff --git a/app/lib/cleanObject.js b/app/lib/cleanObject.js new file mode 100644 index 0000000..8a1ed8e --- /dev/null +++ b/app/lib/cleanObject.js @@ -0,0 +1,7 @@ +module.exports = obj => { + let keys = Object.keys(obj) + keys.forEach(k => { + if (!obj[k]) delete obj[k] + }) + return obj +} diff --git a/app/lib/config.js b/app/lib/config.js index 436b51d..d525ef4 100644 --- a/app/lib/config.js +++ b/app/lib/config.js @@ -1,3 +1,5 @@ +'use strict' + const fs = require('fs') const path = require('path') @@ -11,20 +13,25 @@ const configVariables = [ 'mongoPort' ] -try { +const port = 3944 +try { // Attempts to pull entire config from environment variables. If any key is // not found, the configuration will instead be drawn from the .config file // which should be placed in the project root. var envUsed = true configVariables.forEach(envVar => { - if (process.env[envVar] != undefined) module.exports[envVar] = process.env[envVar] + if (process.env[envVar] != undefined) + module.exports[envVar] = process.env[envVar] else envUsed = false // If any are empty, the .config file will be used }) // Skips if the full config is already in the environment variables - if (!envUsed) module.exports = JSON.parse(fs.readFileSync(configPath)) - + if (!envUsed) { + let opts = JSON.parse(fs.readFileSync(configPath)) + opts.port = port + module.exports = opts + } } catch (e) { console.log(`No config file found in ${configPath} Please run diff --git a/app/lib/connection.js b/app/lib/connection.js new file mode 100644 index 0000000..6edf358 --- /dev/null +++ b/app/lib/connection.js @@ -0,0 +1,126 @@ +'use strict' + +const Client = require('castv2').Client +const config = require('./config') + +let func = { + establish: (d, disconnectCallback) => { + let host = d.address + d.connectionFailCount = 0 + if (d.status == 'offline' || d.status == 'waiting') { + const client = new Client() + client.connect(host, () => { + // reset number of failed connection attempts + d.connectionFailCount = 0 + + // create various namespace handlers + d.connection = client.createChannel( + 'sender-0', + 'receiver-0', + 'urn:x-cast:com.google.cast.tp.connection', + 'JSON' + ) + d.heartbeat = client.createChannel( + 'sender-0', + 'receiver-0', + 'urn:x-cast:com.google.cast.tp.heartbeat', + 'JSON' + ) + d.receiver = client.createChannel( + 'sender-0', + 'receiver-0', + 'urn:x-cast:com.google.cast.receiver', + 'JSON' + ) + + // establish virtual connection to the receiver + d.connection.send({ type: 'CONNECT' }) + + // start heartbeating + d.missedHeartbeats = 0 + d.pulse = setInterval(() => { + d.missedHeartbeats++ + if (d.missedHeartbeats > 6) { + // receiver has been offline for more than 30 seconds + d.status = 'offline' // mark receiver as offline + clearInterval(d.pulse) // stop checking for pulse :( + func.addError( + d, + 'Receiver missed too many heartbeats, it is likely offline.' + ) + } else d.heartbeat.send({ type: 'PING' }) + }, 5 * 1000) + d.heartbeat.on('message', (data, broadcast) => { + if (data.type == 'PONG') { + d.missedHeartbeats = 0 + d.status = 'online' + func.clearErrors(d) + } + }) + + // launch hub app + d.receiver.send({ type: 'LAUNCH', appId: config.appId, requestId: 1 }) + + // monitor receiver status updates to insure hub is open + d.receiver.on('message', (data, broadcast) => { + if (data.type != 'RECEIVER_STATUS') + func.addError(d, `Message from receiver: ${data.type}`) + if ((data.type = 'RECEIVER_STATUS')) { + // data.status contains relevant information about current app, volume, etc + if (data.status && data.status.applications) { + var apps = data.status.applications + + /* Backdrop means that our hub applications has stopped running, so we need to restart it */ + if (apps.find(a => a.displayName == 'Backdrop')) { + func.addError(d, 'Receiver closed hub, relaunching...') + d.receiver.send({ + type: 'LAUNCH', + appId: config.appId, + requestId: 1 + }) + } + } + } + }) + }) + client.on('error', () => { + d.connectionFailCount++ + if (d.connectionFailCount > 6) { + // receiver hasn't responded after 60 seconds + clearTimeout(d.connectionFail) + func.addError( + d, + 'Receiver is unresponsive, attempting to reconnect...' + ) + } else d.connectionFail = setTimeout(disconnectCallback, 10 * 1000) + }) + } + }, + hasErrors: () => Object.keys(errors).length > 0, + getErrors: () => errors, + addError: (device, message) => { + // you can pass a tag directly instead of a device + let tag = typeof device === 'object' ? device.deviceId : device + if (!errors[tag]) + errors[tag] = { + name: typeof device === 'object' ? device.location : device, + messages: [] + } + if (errors[tag].messages.indexOf(message) == -1) + errors[tag].messages.push(message) + }, + clearErrors: device => + delete errors[typeof device === 'object' ? device.deviceId : device], + hasStatus: () => status.length > 0, + getStatus: () => status.map(s => s.message), + addStatus: (tag, message) => + status.push({ + tag: tag, + message: message + }), + clearStatus: tag => (status = status.filter(s => s.tag != tag)) + }, + errors = {}, + status = [] + +module.exports = func diff --git a/app/lib/dbConnect.js b/app/lib/dbConnect.js index 91832fa..5cda28f 100644 --- a/app/lib/dbConnect.js +++ b/app/lib/dbConnect.js @@ -1,9 +1,19 @@ +'use strict' + const mongoose = require('mongoose') module.exports = config => { - const userPassword = config.mongoUser ? `${config.mongoUser}:${config.mongoPass}@` : '' - mongoose.connect( - `mongodb://${userPassword}${config.mongoHost}:${config.mongoPort}/multicast?authSource=${config.mongoAuthSource}`, { - useMongoClient: true - }).on('error', () => console.log('Could not connect to Mongo.')) -} \ No newline at end of file + const userPassword = config.mongoUser + ? `${config.mongoUser}:${config.mongoPass}@` + : '' + mongoose + .connect( + `mongodb://${userPassword}${config.mongoHost}:${ + config.mongoPort + }/multicast?authSource=${config.mongoAuthSource}`, + { + useMongoClient: true + } + ) + .on('error', () => console.log('Could not connect to Mongo.')) +} diff --git a/app/lib/devices.js b/app/lib/devices.js new file mode 100644 index 0000000..fa914d1 --- /dev/null +++ b/app/lib/devices.js @@ -0,0 +1,149 @@ +'use strict' + +const mdns = require('mdns') + +const Chromecast = require('../models/Chromecast') +const Channel = require('../models/Channel') + +const sockets = require('./sockets') +const connection = require('./connection') + +let devices = [] + +const func = { + init: () => { + Chromecast.find() + .populate('channel') + .exec((err, chromecasts) => { + devices = chromecasts + devices.forEach(d => (d.status = 'offline')) + findDevices() + /* start interval to continue polling for device status */ + setInterval(() => { + func.refresh() + }, 30 * 1000) + }) + }, + refresh: () => findDevices(), + list: () => devices, + isRegistered: id => devices.findIndex(d => d.deviceId == id) > -1, + isOffline: id => func.withId(id).status == 'offline', + isOnline: id => func.withId(id).status == 'online', + setStatus: (id, status) => (func.withId(id).status = status), + withId: id => devices.find(d => d.deviceId == id), + withHost: host => devices.find(d => d.address == host), + update: (id, opts) => { + let i = devices.findIndex(d => d.deviceId == id) + Object.assign(devices[i], opts) + }, + register: (id, opts) => { + let d = func.withId(id) + + delete d.unregistered // mark local info as registered + func.update(id, opts) // update local info with any new details + + /* Launch hub on newly registered device */ + let c = sockets.withHost(d.address) + if (c) + c.emit('register', d.deviceId) // emit 'register' to have setup page redirected + else launchHub(d.address) // establish connection if client hasn't already connected + }, + reconnect: host => launchHub(host), + updateChannel: (channel, callback) => { + for (let i in devices) { + let d = devices[i] + if (d.channel && d.channel._id == channel._id) { + Object.assign(d.channel, channel) + let c = sockets.withHost(d.address) + if (c) c.emit('refresh') + } + } + callback() + }, + removeChannel: (id, callback) => { + for (let i in devices) { + let d = devices[i] + if (d.channel && d.channel._id == id) delete d.channel + let c = sockets.withHost(d.address) + if (c) c.emit('change_channel', null) + } + + Chromecast.update({ channel: id }, { $unset: { channel: 1 } }, err => { + if (err) console.log(err) + callback() + }) + }, + refreshChannelPath: _path => { + for (let i in devices) { + let d = devices[i] + if (d.channel && d.channel.URLs[0].match(new RegExp(`/${_path}`))) { + let c = sockets.withHost(d.address) + if (c) c.emit('refresh') + } + } + }, + refreshAll: callback => { + for (let i in devices) { + let d = devices[i], + c = sockets.withHost(d.address) + if (c) c.emit('refresh') + } + callback() + } +} + +const findDevices = () => { + /* Look for mDNS Cast devices on local network */ + let browser = mdns.createBrowser(mdns.tcp('googlecast')) + + /* Only scan IPv4 addresses */ + mdns.Browser.defaultResolverSequence[1] = + 'DNSServiceGetAddrInfo' in mdns.dns_sd + ? mdns.rst.DNSServiceGetAddrInfo() + : mdns.rst.getaddrinfo({ families: [4] }) + + browser.on('serviceUp', service => { + // service.name: Chromecast-hexadecimalid + let _id = service.name.split('-'), + id = _id.pop() + + // fix for groups with IDs that resemble Google-Cast-Group-{deviceId}-1 + while (_id.length > 0 && id.length < 3) id = _id.pop() + + /* If device is registered, append local statistics */ + if (func.isRegistered(id)) { + func.update(id, { + name: service.txtRecord.fn, + address: service.addresses[0], + port: service.port + }) + if (func.isOffline(id)) { + func.setStatus(id, 'waiting') + launchHub(service.addresses[0]) + } + + /* Otherwise, add info for unregistered device */ + } else { + devices.push({ + unregistered: true, + deviceId: id, + name: service.txtRecord.fn, + address: service.addresses[0], + port: service.port, + rotation: 'rot0' + }) + } + }) + + /* Begin searching */ + browser.start() + + /* Stop searching after 15 seconds */ + setTimeout(() => browser.stop(), 15 * 1000) + }, + /* Establish connection with Chromecast */ + launchHub = host => { + if (host) connection.establish(func.withHost(host), () => launchHub(host)) + } + +module.exports = func diff --git a/app/lib/sockets.js b/app/lib/sockets.js new file mode 100644 index 0000000..0c95223 --- /dev/null +++ b/app/lib/sockets.js @@ -0,0 +1,30 @@ +'use strict' + +const stripIPv6 = require('./stripIPv6') +let io + +let clients = [] + +const func = { + init: server => { + io = require('socket.io')(server) + io.on('connection', client => { + console.log( + 'connected client with host:', + stripIPv6(client.handshake.address) + ) + clients.push(client) + client.on('disconnect', () => { + console.log( + 'disconnected client with host:', + stripIPv6(client.handshake.address) + ) + clients.splice(clients.indexOf(client), 1) + }) + }) + }, + list: () => clients, + withHost: host => clients.find(c => stripIPv6(c.handshake.address) == host) +} + +module.exports = func diff --git a/app/lib/squashKeyedArrays.js b/app/lib/squashKeyedArrays.js new file mode 100644 index 0000000..5c3ea22 --- /dev/null +++ b/app/lib/squashKeyedArrays.js @@ -0,0 +1,30 @@ +module.exports = (obj, upgradeElementsToArrays) => { + let keys = Object.keys(obj), + arrays = [] + keys.forEach(k => { + let key = /(.+)\[\d+\]$/.exec(k) + if (key && arrays.indexOf(key[1]) == -1) arrays.push(key[1]) + }) + + arrays.forEach(a => { + let _arr = {}, + arr = [], + highestIndex = 0 + keys.forEach(k => { + let index = new RegExp(`${a}\\[(\\d+)\\]`).exec(k) + if (index) { + highestIndex = Math.max(highestIndex, index) + let item = obj[k] + if (upgradeElementsToArrays && !Array.isArray(item)) item = [item] + arr[index[1]] = item + delete obj[k] + } + }) + for (let i = 0; i < highestIndex; i++) { + if (_arr.hasOwnProperty(i)) arr.push(_arr[i]) + else arr.push(upgradeElementsToArrays ? [] : null) + } + obj[a] = arr + }) + return obj +} diff --git a/app/lib/stripIPv6.js b/app/lib/stripIPv6.js new file mode 100644 index 0000000..715caaa --- /dev/null +++ b/app/lib/stripIPv6.js @@ -0,0 +1 @@ +module.exports = ip => ip.replace(/^.*:/, '') diff --git a/app/lib/takeover.js b/app/lib/takeover.js new file mode 100644 index 0000000..06d4519 --- /dev/null +++ b/app/lib/takeover.js @@ -0,0 +1,30 @@ +'use strict' + +const Channel = require('../models/Channel') +const devices = require('../lib/devices') +const sockets = require('../lib/sockets') +const connection = require('../lib/connection') + +var takeover = null + +const func = { + isActive: () => takeover != null, + channel: () => takeover, + activate: (channelId, callback) => { + Channel.findOne({ _id: channelId }, (err, channel) => { + if (err) console.log(err) + + takeover = channel + connection.addStatus('takeover', 'Takeover is currently active!') + devices.refreshAll(() => callback()) + }) + }, + deactivate: callback => { + takeover = null + connection.clearStatus('takeover') + sockets.list().forEach(c => c.emit('refresh')) + callback() + } +} + +module.exports = func diff --git a/app/lib/ux.js b/app/lib/ux.js new file mode 100644 index 0000000..35a89b0 --- /dev/null +++ b/app/lib/ux.js @@ -0,0 +1,19 @@ +'use strict' + +const errors = [ + 'Houston, we have a problem.', + 'Yikes!', + "Something doesn't look right...", + "Something's fishy." + ], + atRandom = arr => { + let min = 0, + max = arr.length - 1 + return arr[Math.floor(Math.random() * (max - min + 1)) + min] + } + +let func = { + error: () => atRandom(errors) +} + +module.exports = func diff --git a/app/main.js b/app/main.js index 24f5dce..53aa9ef 100644 --- a/app/main.js +++ b/app/main.js @@ -4,9 +4,7 @@ const express = require('express') const app = express() const server = require('http').createServer(app) -const io = require('socket.io')(server) -const Client = require('castv2').Client -const mdns = require('mdns') +const cors = require('cors') const mongoose = require('mongoose') mongoose.Promise = require('bluebird') const bodyParser = require('body-parser') @@ -14,223 +12,59 @@ const path = require('path') const fs = require('fs') const Chromecast = require('./models/Chromecast') -const Channel = require('./models/Channel') const config = require('./lib/config') const dbConnect = require('./lib/dbConnect') +const stripIPv6 = require('./lib/stripIPv6') -const port = 3944 +const devices = require('./lib/devices') +const channels = require('./lib/channels') +const sockets = require('./lib/sockets') +const takeover = require('./lib/takeover') +const connection = require('./lib/connection') +const ux = require('./lib/ux') + +const port = config.port const serveOnly = process.argv.find(arg => arg == '--serve-only') // prettier-ignore-block -var takeover = null - +/* Establish database connection */ dbConnect(config) -/* Establish connection with Chromecast devices on local network */ - -var devices = [], - findDevices = () => { - /* Look for mDNS Cast devices on local network */ - var browser = mdns.createBrowser(mdns.tcp('googlecast')) - - /* Only scan IPv4 addresses */ - mdns.Browser.defaultResolverSequence[1] = - 'DNSServiceGetAddrInfo' in mdns.dns_sd - ? mdns.rst.DNSServiceGetAddrInfo() - : mdns.rst.getaddrinfo({ families: [4] }) - - browser.on('serviceUp', service => { - // service.name: Chromecast-hexadecimalid - var id = service.name.split('-').pop(), - i = devices.findIndex(d => d.deviceId == id) - - /* If device is registered, append local statistics */ - if (i > -1) { - devices[i].name = service.txtRecord.fn - devices[i].address = service.addresses[0] - devices[i].port = service.port - if (devices[i].status == 'offline') { - devices[i].status = 'waiting' - launchHub(service.addresses[0]) - } - - /* Otherwise, add info for unregistered device */ - } else { - devices.push({ - unregistered: true, - deviceId: id, - name: service.txtRecord.fn, - address: service.addresses[0], - port: service.port - }) - } - }) - - /* Begin searching */ - browser.start() - - /* Stop searching after 15 seconds */ - setTimeout(() => browser.stop(), 15 * 1000) - }, - /* Establish connection with Chromecast */ - launchHub = host => { - if (host) { - var d = devices.find(d => d.address == host) - - d.connectionFailCount = 0 - if (d.status == 'offline' || d.status == 'waiting') { - const client = new Client() - client.connect(host, () => { - // reset number of failed connection attempts - d.connectionFailCount = 0 - - // create various namespace handlers - d.connection = client.createChannel( - 'sender-0', - 'receiver-0', - 'urn:x-cast:com.google.cast.tp.connection', - 'JSON' - ) - d.heartbeat = client.createChannel( - 'sender-0', - 'receiver-0', - 'urn:x-cast:com.google.cast.tp.heartbeat', - 'JSON' - ) - d.receiver = client.createChannel( - 'sender-0', - 'receiver-0', - 'urn:x-cast:com.google.cast.receiver', - 'JSON' - ) - - // establish virtual connection to the receiver - d.connection.send({ type: 'CONNECT' }) - - // start heartbeating - d.missedHeartbeats = 0 - d.pulse = setInterval(() => { - d.missedHeartbeats++ - if (d.missedHeartbeats > 6) { - // receiver has been offline for more than 30 seconds - d.status = 'offline' // mark receiver as offline - clearInterval(d.pulse) // stop checking for pulse :( - } else d.heartbeat.send({ type: 'PING' }) - }, 5 * 1000) - d.heartbeat.on('message', (data, broadcast) => { - if (data.type == 'PONG') { - d.missedHeartbeats = 0 - d.status = 'online' - } - }) - - // launch hub app - d.receiver.send({ type: 'LAUNCH', appId: config.appId, requestId: 1 }) - - // monitor receiver status updates to insure hub is open - d.receiver.on('message', (data, broadcast) => { - if (data.type != 'RECEIVER_STATUS') console.log(data.type) - if ((data.type = 'RECEIVER_STATUS')) { - // data.status contains relevant information about current app, volume, etc - if (data.status && data.status.applications) { - var apps = data.status.applications - - /* Backdrop means that our hub applications has stopped running, so we need to restart it */ - if (apps.find(a => a.displayName == 'Backdrop')) { - console.log('relaunching hub...') - d.receiver.send({ - type: 'LAUNCH', - appId: config.appId, - requestId: 1 - }) - } - } - } - }) - }) - client.on('error', () => { - d.connectionFailCount++ - if (d.connectionFailCount > 6) { - // receiver hasn't responded after 60 seconds - clearTimeout(d.connectionFail) - } else d.connectionFail = setTimeout(() => launchHub(host), 10 * 1000) - }) - } - } - } +/* Load initial channel listing */ +channels.init() /* Establish socket.io service */ - -var clients = [] -io.on('connection', client => { - clients.push(client) - client.on('disconnect', () => { - var i = clients.indexOf(client) - clients.splice(i, 1) - }) -}) +sockets.init(server) /* Express Setup */ - -app.set('views', path.join(__dirname, 'views')) +app.use(cors()) app.set('view engine', 'pug') - -app.use(express.static(path.resolve(__dirname, '..', 'public'))) +app.set('views', path.join(__dirname, 'views')) app.use(bodyParser.urlencoded({ extended: false })) +app.use(express.static(path.resolve(__dirname, '..', 'public'))) +app.locals.connection = connection +app.locals.ux = ux /* Home Page */ - app.get('/', (req, res) => { res.render('index', { render: 'home', takeover: takeover }) }) /* Nyan!! */ - app.get('/nyan', (req, res) => { res.render('nyan', {}) }) /* Basic Message Channel */ - -app.get('/message', (req, res) => { - fs.readFile(path.resolve(__dirname, '..', 'message.txt'), (err, data) => { - if (err || data == '') - res.render('message', { message: 'No message configured.' }) - else res.render('message', { message: data }) - }) -}) - -app.get('/message/edit', (req, res) => { - fs.readFile(path.resolve(__dirname, '..', 'message.txt'), (err, data) => { - if (err) res.render('message-edit', { message: '' }) - else res.render('message-edit', { message: data }) - }) -}) - -app.post('/message/edit', (req, res) => { - fs.writeFile( - path.resolve(__dirname, '..', 'message.txt'), - req.body.message, - err => console.log(err) - ) - for (var i in devices) { - var d = devices[i] - if (d.channel && d.channel.URLs[0].match(new RegExp(`${port}/message`))) { - var c = clients.find(c => stripIPv6(c.handshake.address) == d.address) - if (c) c.emit('refresh') - } - } - res.sendStatus(200) -}) +app.use('/message', require('./routes/message')) /* Landing Page */ - app.get('/landing', (req, res) => { var ip = stripIPv6(req.connection.remoteAddress), // Get IPv4 address of device - d = devices.find(d => d.address == ip) // Find local info for device + d = devices.withHost(ip) // Find local info for device if (d) - res.redirect(`/device/${d.deviceId}`) // Redirect to device display // If not recognized, display information + res.redirect(`/devices/${d.deviceId}`) // Redirect to device display // Display device info else res.render('setup-chromecast', { device: { @@ -244,352 +78,24 @@ app.get('/landing', (req, res) => { }) /* Devices */ - -app.get('/devices', (req, res) => { - res.render('index', { render: 'devices', devices: devices }) -}) - -app.get('/device/new', (req, res) => { - Channel.find() - .sort('name') - .exec((err, channels) => { - if (err) console.log(err) - res.render('index', { - render: 'device', - devices: devices.filter(d => d.unregistered), - channels: channels - }) - }) -}) - -app.post('/device/new', (req, res) => { - var c = new Chromecast(req.body) - c.save((err, device) => { - if (err) console.log(err) - - /* Mark local info as registered */ - var i = devices.findIndex(d => d.deviceId == req.body.deviceId) - delete devices[i].unregistered - - /* Update local info with any new details */ - devices[i] = Object.assign(devices[i], req.body) - - /* Launch hub on newly registered device */ - var c = clients.find( - c => stripIPv6(c.handshake.address) == devices[i].address - ) - if (c) - c.emit('register', devices[i].deviceId) // emit 'register' to have setup page redirected - else launchHub(devices[i].address) // establish connection if client hasn't already connected - - res.send(device.deviceId) - }) -}) - -app.get('/device/:device_id', (req, res) => { - Chromecast.findOne({ deviceId: req.params.device_id }) - .populate('channel') - .exec((err, device) => { - if (err) console.log(err) - if (device && device.channel) { - if (takeover) - res.render(`layouts/${takeover.layout}`, { - deviceId: req.params.device_id, - channel: takeover, - casting: true - }) - else { - /* device registered and channel set - display device page */ - res.render(`layouts/${device.channel.layout}`, { - deviceId: req.params.device_id, - channel: device.channel, - casting: true - }) - } - } else { - var localDevice = devices.find(d => d.deviceId == req.params.device_id) - if (device) { - if (takeover) - res.render(`layouts/${takeover.layout}`, { - deviceId: req.params.device_id, - channel: takeover, - casting: true - }) - else { - /* device registered but no channel set - display setup page */ - res.render('setup-chromecast', { - device: Object.assign(localDevice, device), - registered: true, - setupUrl: `${req.protocol}://${req.hostname}:${port}/` - }) - } - } else { - /* device is not registered - display setup page */ - res.render('setup-chromecast', { - device: Object.assign(localDevice, device), - registered: false, - setupUrl: `${req.protocol}://${req.hostname}:${port}/` - }) - } - } - }) -}) - -app.get('/device/:device_id/connect', (req, res) => { - var d = devices.find(d => d.deviceId == req.params.device_id) - if (d.status == 'offline' || d.status == 'waiting') launchHub(d.address) - else { - // launch hub if not already open - // hard reload page if already open - var c = clients.find(c => stripIPv6(c.handshake.address) == d.address) - if (c) c.emit('refresh') - } - res.sendStatus(200) -}) - -app.post('/device/:device_id/edit', (req, res) => { - Chromecast.update( - { deviceId: req.params.device_id }, - req.body, - (err, numAffected, response) => { - if (err) console.log(err) - var i = devices.findIndex(d => d.deviceId == req.params.device_id) - devices[i].location = req.body.location // update local info with location - if (req.body.channel) { - Channel.findOne({ _id: req.body.channel }).exec((err, channel) => { - var c = clients.find( - c => stripIPv6(c.handshake.address) == devices[i].address - ) - if (c) c.emit('change_channel', channel) - devices[i].channel = channel // update local info with new channel info for any applicable devices - thing({ obj: { obj: 'foo' } }, bar) - res.send(req.params.device_id) - }) - } else { - var c = clients.find( - c => stripIPv6(c.handshake.address) == devices[i].address - ) - if (c) c.emit('change_channel', null) - res.send(req.params.device_id) - } - } - ) -}) - -app.delete('/device/:device_id/edit', (req, res) => { - Chromecast.remove({ deviceId: req.params.device_id }, () => { - /* Mark local info for device as unregistered */ - var i = devices.findIndex(d => d.deviceId == req.params.device_id) - devices[i].unregistered = true - delete devices[i].channel - delete devices[i].location - res.sendStatus(200) - }) -}) - -app.get('/device/:device_id/edit', (req, res) => { - Chromecast.findOne({ deviceId: req.params.device_id }) - .populate('channel') - .exec((err, device) => { - if (err) console.log(err) - Channel.find() - .sort('name') - .exec((err, channels) => { - if (err) console.log(err) - if (device) { - var localDevice = devices.find( - d => d.deviceId == req.params.device_id - ) - res.render('index', { - render: 'device', - device: Object.assign(localDevice, device), - channels: channels - }) - } else res.render('index', {}) - }) - }) -}) +app.use('/devices', require('./routes/devices')) /* Channels */ +app.use('/channels', require('./routes/channels')) -app.get('/channels', (req, res) => { - Channel.find() - .sort('name') - .exec((err, channels) => { - res.render('index', { render: 'channels', channels: channels }) - }) -}) - -app.get('/channel/new', (req, res) => { - res.render('index', { - render: 'channel', - host: `${req.protocol}://${req.hostname}:${port}/` - }) -}) - -app.post('/channel/new', (req, res) => { - if (req.body.URLs) req.body.URLs = req.body.URLs.filter(u => u.trim() != '') - var c = new Channel(req.body) - c.save((err, channel) => { - if (err) console.log(err) - res.send(channel._id) - }) -}) - -app.get('/channel/:channel_id', (req, res) => { - Channel.findOne({ _id: req.params.channel_id }).exec((err, channel) => { - if (err) console.log(err) - if (channel) - res.render(`layouts/${channel.layout}`, { - channel: channel, - casting: false - }) - else res.render('layouts/empty', { casting: false }) - }) -}) - -app.post('/channel/:channel_id/edit', (req, res) => { - if (req.body.URLs) req.body.URLs = req.body.URLs.filter(u => u.trim() != '') - for (var i in devices) { - var d = devices[i] - - /* update channel info on local info for any devices displaying this channel */ - if ( - d.channel && - d.channel._id.toString() == req.params.channel_id.toString() - ) - Object.assign(devices[i].channel, req.body) - } - Channel.update( - { _id: req.params.channel_id }, - req.body, - (err, numAffected, response) => { - if (err) console.log(err) - res.send(req.params.channel_id) - } - ) -}) - -app.delete('/channel/:channel_id/edit', (req, res) => { - Channel.remove({ _id: req.params.channel_id }, () => { - for (var i in devices) { - var d = devices[i] - if ( - d.channel && - d.channel._id.toString() == req.params.channel_id.toString() - ) { - /* remove channel listing from local info of relevant devices */ - delete devices[i].channel - - /* send devices on this channel back to setup page */ - var c = clients.find( - c => stripIPv6(c.handshake.address) == devices[i].address - ) - if (c) c.emit('change_channel', null) - } - } - - /* remove channel listing from Mongo */ - Chromecast.update( - { channel: new mongoose.Types.ObjectId(req.params.channel_id) }, - { $unset: { channel: 1 } }, - (err, numAffected, result) => { - res.sendStatus(200) - } - ) - }) -}) - -app.get('/channel/:channel_id/edit', (req, res) => { - Channel.findOne({ _id: req.params.channel_id }).exec((err, channel) => { - if (err) console.log(err) - if (channel) - res.render('index', { - render: 'channel', - channel: channel, - host: `${req.protocol}://${req.hostname}:${port}/` - }) - else res.render('index', {}) - }) -}) - -/* Push Alerts & Takeovers */ - -app.get('/new/push', (req, res) => { - res.render('index', { render: 'push' }) -}) - -app.post('/new/push', (req, res) => { - clients.forEach(c => { - c.emit('push', { - message: req.body.message, - style: req.body.style, - duration: req.body.duration - }) - }) - res.sendStatus(200) -}) - -app.get('/new/takeover', (req, res) => { - Channel.find() - .sort('name') - .exec((err, channels) => { - res.render('index', { render: 'takeover', channels: channels }) - }) -}) - -app.post('/new/takeover', (req, res) => { - Channel.findOne({ - _id: new mongoose.Types.ObjectId(req.body.channel_id) - }).exec((err, channel) => { - if (err) console.log(err) - takeover = channel - clients.forEach(c => { - c.emit('change_channel', channel) - }) - res.sendStatus(200) - }) -}) +/* Push Alerts */ +app.use('/push', require('./routes/push')) -app.post('/takeover/end', (req, res) => { - takeover = null - clients.forEach(c => { - c.emit('refresh') - }) - res.sendStatus(200) -}) +/* Takeover */ +app.use('/takeover', require('./routes/takeover')) /* Server */ - server.listen(port, () => { console.log('MultiCast is live!') - console.log(`listening at port ${port}...`) + console.log(`Listening at port ${port}...`) if (!serveOnly) { - /* load saved devices */ - Chromecast.find() - .populate('channel') - .exec((err, _devices) => { - for (var i in _devices) { - var d = _devices[i].toObject() - d.status = 'offline' - devices.push(d) - } - - /* poll for active devices */ - findDevices() - - /* start interval to continue polling for device status */ - setInterval(() => { - findDevices() - }, 30 * 1000) - }) + /* poll for active devices */ + devices.init() } }) - -/* Utility */ - -var stripIPv6 = ip => ip.replace(/^.*:/, '') diff --git a/app/models/Channel.js b/app/models/Channel.js index 995371a..9a0c649 100644 --- a/app/models/Channel.js +++ b/app/models/Channel.js @@ -1,11 +1,12 @@ -'use strict'; -const mongoose = require('mongoose') -const Schema = mongoose.Schema +'use strict' +const mongoose = require('mongoose') +const Schema = mongoose.Schema const ChannelSchema = new Schema({ name: String, layout: String, - URLs: [String] + URLs: [[String]], + duration: Number }) -module.exports = mongoose.model('Channel', ChannelSchema) \ No newline at end of file +module.exports = mongoose.model('Channel', ChannelSchema) diff --git a/app/models/Chromecast.js b/app/models/Chromecast.js index 24f4e2a..53c695e 100644 --- a/app/models/Chromecast.js +++ b/app/models/Chromecast.js @@ -1,12 +1,12 @@ -'use strict'; +'use strict' const mongoose = require('mongoose') const Schema = mongoose.Schema const ChromecastSchema = new Schema({ deviceId: String, // hexadecimal identifier provided by Chromecast - // don't confuse this with _id provided by Mongo location: String, - channel: { type: Schema.Types.ObjectId, ref: 'Channel' }, + rotation: String, + channel: { type: Schema.Types.ObjectId, ref: 'Channel' } }) -module.exports = mongoose.model('Chromecast', ChromecastSchema) \ No newline at end of file +module.exports = mongoose.model('Chromecast', ChromecastSchema) diff --git a/app/routes/channels.js b/app/routes/channels.js new file mode 100644 index 0000000..bdd2e52 --- /dev/null +++ b/app/routes/channels.js @@ -0,0 +1,63 @@ +const express = require('express') +const router = express.Router() + +const channels = require('../lib/channels') + +const Chromecast = require('../models/Chromecast') +const Channel = require('../models/Channel') + +const port = require('../lib/config').port +const squashKeyedArrays = require('../lib/squashKeyedArrays') +const cleanObject = require('../lib/cleanObject') + +router.use((req, res, next) => { + if (req.body.URLs) req.body.URLs = req.body.URLs.filter(u => u.trim() != '') + next() +}) + +router.get('/', (req, res) => { + res.render('index', { render: 'channels', channels: channels.list() }) +}) + +router + .route('/new') + .get((req, res) => { + res.render('index', { + render: 'channel', + host: `${req.protocol}://${req.hostname}:${port}/` + }) + }) + .post((req, res) => channels.create(req.body, id => res.send(id))) + +router.get('/:channel_id', (req, res) => { + let channel = channels.withId(req.params.channel_id) + if (channel) + res.render(`layouts/${channel.layout}`, { + channel: channel, + casting: false + }) + else res.render('layouts/empty', { casting: false }) +}) + +router + .route('/:channel_id/edit') + .get((req, res) => { + let channel = channels.withId(req.params.channel_id) + if (channel) + res.render('index', { + render: 'channel', + channel: channel, + host: `${req.protocol}://${req.hostname}:${port}/` + }) + else res.render('index', {}) + }) + .post((req, res) => { + channels.update(req.params.channel_id, squashKeyedArrays(req.body), id => + res.send(id) + ) + }) + .delete((req, res) => + channels.remove(req.params.channel_id, () => res.sendStatus(200)) + ) + +module.exports = router diff --git a/app/routes/devices.js b/app/routes/devices.js new file mode 100644 index 0000000..3794828 --- /dev/null +++ b/app/routes/devices.js @@ -0,0 +1,155 @@ +'use strict' + +const express = require('express') +const router = express.Router() + +const Chromecast = require('../models/Chromecast') + +const devices = require('../lib/devices') +const channels = require('../lib/channels') +const sockets = require('../lib/sockets') +const takeover = require('../lib/takeover') + +const port = require('../lib/config').port + +/* List of device rotations */ +const rotations = [ + { + value: 'rot0', + description: 'Landscape' + }, + { + value: 'rot90', + description: 'Portrait' + }, + { + value: 'rot180', + description: 'Landscape Reversed' + }, + { + value: 'rot270', + description: 'Portrait Reversed' + } +] + +router.get('/', (req, res) => { + res.render('index', { render: 'devices', devices: devices.list() }) +}) + +router + .route('/new') + .get((req, res) => { + res.render('index', { + render: 'device', + devices: devices.list().filter(d => d.unregistered), + channels: channels.list(), + rotations: rotations, + rotation: 0 + }) + }) + .post((req, res) => { + let c = new Chromecast(req.body) + c.save((err, device) => { + if (err) console.log(err) + devices.register(device.deviceId, req.body) + res.send(device.deviceId) + }) + }) + +router.get('/:device_id/connect', (req, res) => { + let d = devices.withId(req.params.device_id) + if (!devices.isOnline(req.params.device_id)) devices.reconnect(d.address) + else { + // launch hub if not already open + // hard reload page if already open + var c = sockets.withHost(d.address) + if (c) c.emit('refresh') + } + res.sendStatus(200) +}) + +router + .route('/:device_id/edit') + .get((req, res) => { + let d = devices.withId(req.params.device_id) + if (d) { + res.render('index', { + render: 'device', + device: d, + channels: channels.list(), + rotations: rotations, + rotation: d.rotation + }) + } else render('index', {}) + }) + .post((req, res) => { + Chromecast.update({ deviceId: req.params.device_id }, req.body, err => { + if (err) console.log(err) + + let d = devices.withId(req.params.device_id) + d.location = req.body.location // update local info with location + d.rotation = req.body.rotation + let c = sockets.withHost(d.address), + channel = null + if (req.body.channel) { + channel = channels.withId(req.body.channel) + d.channel = channel // update local info with channel + } + if (c) c.emit('change_channel', channel) + res.send(req.params.device_id) + }) + }) + .delete((req, res) => { + Chromecast.remove({ deviceId: req.params.device_id }, () => { + /* Mark local info for device as unregistered */ + let d = devices.withId(req.params.device_id) + d.unregistered = true + delete d.channel + delete d.location + delete d.rotation + res.sendStatus(200) + }) + }) + +router.get('/:device_id/:preview*?', (req, res) => { + let d = devices.withId(req.params.device_id) + if (d && takeover.isActive()) { + res.render(`layouts/${takeover.channel().layout}`, { + deviceId: req.params.device_id, + channel: takeover.channel(), + rotation: d.rotation, + casting: !req.params.preview + }) + } else { + if (d) { + if (d.channel) { + /* device registered and channel set + display device page */ + res.render(`layouts/${d.channel.layout}`, { + deviceId: req.params.device_id, + channel: d.channel, + rotation: d.rotation, + casting: !req.params.preview + }) + } else { + /* device registered but no channel set + display setup page */ + res.render('setup-chromecast', { + device: d, + registered: true, + setupUrl: `${req.protocol}://${req.hostname}:${port}/` + }) + } + } else { + /* device is not registered + display setup page */ + res.render('setup-chromecast', { + device: d, + registered: false, + setupUrl: `${req.protocol}://${req.hostname}:${port}/` + }) + } + } +}) + +module.exports = router diff --git a/app/routes/message.js b/app/routes/message.js new file mode 100644 index 0000000..495ac74 --- /dev/null +++ b/app/routes/message.js @@ -0,0 +1,32 @@ +'use strict' + +const express = require('express') +const router = express.Router() +const path = require('path') +const fs = require('fs') +const devices = require('../lib/devices') +const messageFile = path.resolve(__dirname, '..', '..', 'message.txt') + +router.get('/', (req, res) => { + fs.readFile(messageFile, (err, data) => { + if (err || data == '') + res.render('message', { message: 'No message configured.' }) + else res.render('message', { message: data }) + }) +}) + +router + .route('/edit') + .get((req, res) => { + fs.readFile(messageFile, (err, data) => { + if (err) res.render('message-edit', { message: '' }) + else res.render('message-edit', { message: data }) + }) + }) + .post((req, res) => { + fs.writeFile(messageFile, req.body.message, err => console.log(err)) + devices.refreshChannelPath('message') + res.sendStatus(200) + }) + +module.exports = router \ No newline at end of file diff --git a/app/routes/push.js b/app/routes/push.js new file mode 100644 index 0000000..8a060fd --- /dev/null +++ b/app/routes/push.js @@ -0,0 +1,23 @@ +'use strict' + +const express = require('express') +const router = express.Router() + +const sockets = require('../lib/sockets') + +router.route('/') +.get((req, res) => { + res.render('index', { render: 'push' }) +}) +.post((req, res) => { + sockets.list().forEach(c => { + c.emit('push', { + message: req.body.message, + style: req.body.style, + duration: req.body.duration + }) + }) + res.sendStatus(200) +}) + +module.exports = router \ No newline at end of file diff --git a/app/routes/takeover.js b/app/routes/takeover.js new file mode 100644 index 0000000..5cadb72 --- /dev/null +++ b/app/routes/takeover.js @@ -0,0 +1,24 @@ +'use strict' + +const express = require('express') +const router = express.Router() +'use strict' + +const channels = require('../lib/channels') +const takeover = require('../lib/takeover') + +router + .route('/') + .get((req, res) => { + res.render('index', { render: 'takeover', channels: channels.list() }) + }) + .post((req, res) => { + console.log('activating takeover!') + takeover.activate(req.body.channel_id, () => res.sendStatus(200)) + }) + +router.post('/end', (req, res) => { + takeover.deactivate(() => res.sendStatus(200)) +}) + +module.exports = router \ No newline at end of file diff --git a/app/views/channel.pug b/app/views/channel.pug index 949da09..66af0c6 100644 --- a/app/views/channel.pug +++ b/app/views/channel.pug @@ -23,18 +23,48 @@ form#edit-channel .layout.right-panel(class=(channel && channel.layout == 'right-panel' ? 'active' : '')) .contents 1 label Contents with Sidebar - if channel - each url, i in channel.URLs - .form-group.display-section.visible - label.form-label.label-lg Section #{i+1} - input.form-input.input-lg(type='text', name='URLs', placeholder='http://192.168.1.100/page.html', value=url) - - var maxSectionCount = 4 - - var length = 0 - - if (channel && channel.URLs) length = channel.URLs.length - each url, i in [...Array(maxSectionCount-length)] - .form-group.display-section(class=((!channel && i == 0) ? 'visible' : '' )) - label.form-label.label-lg Section #{length+i+1} - input.form-input.input-lg(type='text', name='URLs', placeholder='http://192.168.1.100/page.html') + + #channel-list + if channel + each URLs, i in channel.URLs + .display-section.visible + div + label.form-label.label-lg Section #{i+1} + each url, j in URLs + .form-group + label.form-label URL #{j+1} + input.form-input.input-lg(class=(j > 0 ? 'pad-right' : ''), type='text', name='URLs[' + i + ']', placeholder='http://192.168.1.100/page.html', value=url) + if j > 0 + button.btn.btn-lg.btn-link.remove-url + i.fa.fa-trash + button.btn.btn-link.mt-1.add-url + i.fa.fa-plus.mr-2 + | Add Another URL + - var maxSectionCount = 4 + - var length = 0 + - if (channel && channel.URLs) length = channel.URLs.length + each url, i in [...Array(maxSectionCount - length)] + .display-section + div + label.form-label.label-lg Section #{length+i+1} + .form-group + label.form-label URL 1 + input.form-input.input-lg(type='text', name='URLs[' + (length+i) + ']', placeholder='http://192.168.1.100/page.html') + button.btn.btn-link.mt-1.add-url + i.fa.fa-plus.mr-2 + | Add Another URL + + + .form-group#rotation-duration + label.form-label.label-lg Rotation Duration + button.btn.btn-lg.btn-primary(data-duration=15 * 1000) 15s + button.btn.btn-lg(data-duration= 30 * 1000) 30s + button.btn.btn-lg(data-duration= 60 * 1000) 1m + button.btn.btn-lg(data-duration= 2 * 60 * 1000) 2m + button.btn.btn-lg(data-duration= 5 * 60 * 1000) 5m + button.btn.btn-lg(data-duration=30 * 60 * 1000) 30m + button.btn.btn-lg(data-duration=60 * 60 * 1000) 1h + .columns(style='margin-top: 15px') .column button.btn.btn-primary.btn-lg.btn-block(type='submit') Save Channel diff --git a/app/views/channels.pug b/app/views/channels.pug index 458ead1..139caa7 100644 --- a/app/views/channels.pug +++ b/app/views/channels.pug @@ -1,5 +1,5 @@ h1 Channels - a.btn.btn-primary.float-right(href='/channel/new') + a.btn.btn-primary.float-right(href='/channels/new') i.fa.fa-plus |   Add Channel table.table @@ -12,9 +12,9 @@ table.table each c in channels tr td - a(href=`/channel/${c._id}/edit`)= c.name + a(href=`/channels/${c._id}/edit`)= c.name td.text-right - a(href=`/channel/${c._id}`) Preview + a(href=`/channels/${c._id}`) Preview else tr td.text-gray(colspan=42) No channels available. \ No newline at end of file diff --git a/app/views/device.pug b/app/views/device.pug index 9b50ec8..9a70062 100644 --- a/app/views/device.pug +++ b/app/views/device.pug @@ -38,6 +38,17 @@ form#edit-device option(value=c._id)= c.name else option(selected, disabled, value='') No channels available. + .form-group + label.form-label.label-lg(for='device-rotation') Rotation + select.form-select.input-lg#device-rotation(name='rotation') + if rotations.length + each rot in rotations + if device && device.rotation && (rot.value == device.rotation) + option(value=rot.value, selected)= rot.description + else + option(value=rot.value)= rot.description + else + option(value='0', selected) Something went wrong... .columns(style='margin-top: 15px') if device .column @@ -46,4 +57,4 @@ form#edit-device a.btn.btn-lg.btn-block(href='/devices') Cancel else .column - button.btn.btn-primary.btn-lg.btn-block(type='submit') Add Chromecast \ No newline at end of file + button.btn.btn-primary.btn-lg.btn-block(type='submit') Add Chromecast diff --git a/app/views/devices.pug b/app/views/devices.pug index f62f3b6..e536262 100644 --- a/app/views/devices.pug +++ b/app/views/devices.pug @@ -1,5 +1,5 @@ h1 Chromecasts - a.btn.btn-primary.float-right(href='/device/new') + a.btn.btn-primary.float-right(href='/devices/new') i.fa.fa-plus |   Register Chromecast table.table @@ -14,21 +14,21 @@ table.table tr td if d.unregistered - a.badge(href='/device/new', class=d.status)= d.name + a.badge(href='/devices/new', class=d.status)= d.name div Unregistered else - a.badge(href=`/device/${d.deviceId}/edit`, class=d.status)= d.name + a.badge(href=`/devices/${d.deviceId}/edit`, class=d.status)= d.name div= d.location td if d.unregistered - | unregistered + span(style='color: #ccc') unregistered else if d.channel - a(href=`/channel/${d.channel._id}/edit`)= d.channel.name + a(href=`/channels/${d.channel._id}/edit`)= d.channel.name else .text-gray no channel td.text-right - a(href=`/device/${d.deviceId}`) Preview + a(href=`/devices/${d.deviceId}/preview`) Preview else tr td.text-gray(colspan=42) No devices available. \ No newline at end of file diff --git a/app/views/home.pug b/app/views/home.pug index ef299da..cf5baf2 100644 --- a/app/views/home.pug +++ b/app/views/home.pug @@ -1,8 +1,8 @@ a.btn.btn-lg.btn-primary.btn-block(href='/devices') Chromecasts a.btn.btn-lg.btn-primary.btn-block(href='/channels') Channels -a.btn.btn-lg.btn-primary.btn-block(href='/new/push') Push Alert -unless takeover - a.btn.btn-lg.btn-primary.btn-block(href='/new/takeover') Channel Takeover +a.btn.btn-lg.btn-primary.btn-block(href='/push') Push Alert +unless takeover.isActive() + a.btn.btn-lg.btn-primary.btn-block(href='/takeover') Channel Takeover else a.btn.btn-lg.btn-error.btn-block#stop-takeover Stop Channel Takeover hr diff --git a/app/views/include/error.pug b/app/views/include/error.pug new file mode 100644 index 0000000..7b65e96 --- /dev/null +++ b/app/views/include/error.pug @@ -0,0 +1 @@ +#error(style='display: none') \ No newline at end of file diff --git a/app/views/include/nav.pug b/app/views/include/nav.pug index cdcaf64..9359044 100644 --- a/app/views/include/nav.pug +++ b/app/views/include/nav.pug @@ -7,4 +7,24 @@ header.navbar.bg-success(style='padding: 0.3em') a.btn.btn-link(style='color: #fff', href='/devices') Devices a.btn.btn-link(style='color: #fff', href='/channels') Channels +if connection.hasErrors() + .toast.toast-error.connection-errors + strong= ux.error() + ul + each e in connection.getErrors() + each m in e.messages + li + strong= e.name + |   + i.fa.fa-angle-right + |   + = m + +if connection.hasStatus() + .toast.status-messages + strong Active Status Conditions + ul + each s in connection.getStatus() + li= s + include toast.pug \ No newline at end of file diff --git a/app/views/include/rotation.pug b/app/views/include/rotation.pug new file mode 100644 index 0000000..1b08aea --- /dev/null +++ b/app/views/include/rotation.pug @@ -0,0 +1,5 @@ +if (channel.URLs.find(panel => panel.length > 1)) + - console.log(channel) + script(type='text/javascript'). + var rotationDuration = #{channel.duration}, + rotationChannels = !{JSON.stringify(channel.URLs)} \ No newline at end of file diff --git a/app/views/layouts/empty.pug b/app/views/layouts/empty.pug index 514f62c..07c855a 100644 --- a/app/views/layouts/empty.pug +++ b/app/views/layouts/empty.pug @@ -5,7 +5,7 @@ html(lang='en') include ../include/head.pug link(rel='stylesheet', href='/css/setup.css') - body + body(class=rotation) #center h1 #[span.emphasis This channel] hasn't been configured. @@ -15,4 +15,4 @@ html(lang='en') script(type='text/javascript', src='//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js') script(type='text/javascript', src='/js/app.js') script(type='text/javascript', src='/js/client.js') - \ No newline at end of file + diff --git a/app/views/layouts/fullscreen.pug b/app/views/layouts/fullscreen.pug index 6c0b87c..5576366 100644 --- a/app/views/layouts/fullscreen.pug +++ b/app/views/layouts/fullscreen.pug @@ -5,14 +5,16 @@ html(lang='en') include ../include/head.pug link(rel='stylesheet', href='/css/channel.css') - body.fullscreen + body.fullscreen(class=rotation) + include ../include/error.pug include ../include/toast.pug if channel.URLs != null && channel.URLs.length - iframe(src=channel.URLs[0], frameborder=0, framespacing=0) + iframe(src=channel.URLs[0][0], frameborder=0, framespacing=0) + include ../include/rotation.pug script(type='text/javascript', src='/js/jquery.min.js') script(type='text/javascript', src='/socket.io/socket.io.js') if casting script(type='text/javascript', src='//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js') script(type='text/javascript', src='/js/app.js') - script(type='text/javascript', src='/js/client.js') \ No newline at end of file + script(type='text/javascript', src='/js/client.js') diff --git a/app/views/layouts/right-panel.pug b/app/views/layouts/right-panel.pug index af4f658..00affd8 100644 --- a/app/views/layouts/right-panel.pug +++ b/app/views/layouts/right-panel.pug @@ -5,16 +5,18 @@ html(lang='en') include ../include/head.pug link(rel='stylesheet', href='/css/channel.css') - body.right-panel + body.right-panel(class=rotation) + include ../include/error.pug include ../include/toast.pug if channel.URLS != null && channel.URLs.length - iframe(src=channel.URLs[0], frameborder=0, framespacing=0) + iframe(src=channel.URLs[0][0], frameborder=0, framespacing=0) if channel.URLs.length > 1 - iframe(src=channel.URLS[1], frameborder=0, framespacing=0) + iframe(src=channel.URLS[1][0], frameborder=0, framespacing=0) + include ../include/rotation.pug script(type='text/javascript', src='/js/jquery.min.js') script(type='text/javascript', src='/socket.io/socket.io.js') if casting script(type='text/javascript', src='//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js') script(type='text/javascript', src='/js/app.js') - script(type='text/javascript', src='/js/client.js') \ No newline at end of file + script(type='text/javascript', src='/js/client.js') diff --git a/app/views/layouts/template.pug b/app/views/layouts/template.pug index 5a44efd..abe8f4c 100644 --- a/app/views/layouts/template.pug +++ b/app/views/layouts/template.pug @@ -5,16 +5,17 @@ html(lang='en') include ../include/head.pug link(rel='stylesheet', href='/css/channel.css') - body.layout-name + body.layout-name(class=rotation) + include ../include/error.pug include ../include/toast.pug if channel.URLS != null && channel.URLs.length each u in channel.URLS - iframe(src=u, frameborder=0, framespacing=0) + iframe(src=u[0], frameborder=0, framespacing=0) - + include ../include/rotation.pug script(type='text/javascript', src='/js/jquery.min.js') script(type='text/javascript', src='/socket.io/socket.io.js') if casting script(type='text/javascript', src='//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js') script(type='text/javascript', src='/js/app.js') - script(type='text/javascript', src='/js/client.js') \ No newline at end of file + script(type='text/javascript', src='/js/client.js') diff --git a/build/css/channel-edit.css b/build/css/channel-edit.css index b25ce97..8f3a05a 100644 --- a/build/css/channel-edit.css +++ b/build/css/channel-edit.css @@ -5,7 +5,7 @@ #channel-layout .layout:not(:last-child) { margin-right: 15px; -} +} #channel-layout .layout label { margin-top: 8px; @@ -46,20 +46,47 @@ } #channel-layout .layout.right-panel .contents::after { - content: "2"; + content: '2'; position: absolute; - top: -1px; right: -50px; - height: 150px; width: 50px; + top: -1px; + right: -50px; + height: 150px; + width: 50px; display: block; line-height: 146px; border: 1px solid rgb(202, 206, 215); border-left: none; } -#channel-layout ~ .display-section:not(.visible) { +#channel-list .display-section:not(.visible) { display: none; } -#channel-layout + .display-section { +#channel-list .display-section { display: block; -} \ No newline at end of file +} + +#channel-list .form-group > label { + display: inline-block; + width: 65px; +} + +#channel-list .form-group > input { + display: inline-block; + width: calc(100% - 65px); +} + +#channel-list .form-group > input.pad-right { + display: inline-block; + width: calc(100% - 110px); +} + +#channel-list .form-group > button { + display: inline-block; + vertical-align: top; + width: 26px; +} + +#rotation-duration button:not(:last-of-type) { + margin-right: 10px; +} diff --git a/build/css/channel.css b/build/css/channel.css index 46d598f..c683949 100644 --- a/build/css/channel.css +++ b/build/css/channel.css @@ -1,31 +1,93 @@ -html, body { - margin: 0; +html, +body { + margin: auto; padding: 0; width: 100%; height: 100%; } +body iframe { + width: 100%; + height: 100%; +} +/* body.fullscreen iframe { - width: 100vw; height: 100vh; + width: 100vw; + height: 100vh; } - +*/ body.right-panel iframe:first-of-type { position: absolute; - top: 0; left: 0; - width: 80vw; height: 100vh; + top: 0; + left: 0; + width: 80vw; + height: 100vh; } body.right-panel iframe:nth-of-type(2) { position: absolute; - top: 0; left: 80vw; - width: 20vw; height: 100vh; + top: 0; + left: 80vw; + width: 20vw; + height: 100vh; +} + +#error { + position: absolute; + top: 0; + left: 0; + width: 100vw; + background: #000; + color: #fff; + padding: 20px; +} + +#error span { + font-weight: 700; + color: #f00; +} + +.rot0, +.fullscreen { + width: 100vw; + height: 100vh; +} + +.rot90 { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + transform: rotate(90deg); + width: 100vh; + height: 100vw; +} + +.rot180 { + transform: rotate(180deg); + width: 100vw; + height: 100vh; +} + +.rot270 { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + transform: rotate(-90deg); + width: 100vh; + height: 100vw; } #toast.full-width { width: calc(100% - 20px); - left: 10px; bottom: 15px; right: 10px; + left: 10px; + bottom: 15px; + right: 10px; } #toast.full-width .title { font-size: 7.5vh; -} \ No newline at end of file +} diff --git a/build/css/main.css b/build/css/main.css index a4fb127..173903f 100644 --- a/build/css/main.css +++ b/build/css/main.css @@ -1,4 +1,5 @@ -html, body { +html, +body { background: #f8f9fa; } @@ -6,7 +7,8 @@ html, body { width: 400px; max-width: 100%; position: fixed; - bottom: 20px; right: 20px; + bottom: 20px; + right: 20px; } .toast .title, @@ -14,14 +16,21 @@ html, body { margin: 0; } -.toast .title + .text { - margin-top: .25em; +.toast .title + .text { + margin-top: 0.25em; } -.toast :empty { +.toast:empty { display: none; } +.connection-errors > ul, +.status-messages > ul { + list-style: none; + margin: 0; + padding: 0; +} + #wrapper { background: #fff; padding: 3em; @@ -44,7 +53,7 @@ html, body { .btn-error:hover, .btn-error:focus, .btn-error:active { - border-color: #e85600; + border-color: #e85600; background: #df5200; color: #fff; } @@ -74,4 +83,4 @@ footer { width: 100%; min-height: 100px; padding: 3em; -} \ No newline at end of file +} diff --git a/build/js/app.js b/build/js/app.js index eda9053..e006757 100644 --- a/build/js/app.js +++ b/build/js/app.js @@ -3,17 +3,78 @@ function notify(opts) { if (notifyIsActive) return notifyIsActive = true var title = opts.title || '', - msg = opts.message || '', - style = opts.style != '' ? `toast-${opts.style}` : '', - full = opts.fullWidth || false, - dur = opts.duration || 3000 - $('#toast').find('.title').text(title).end() - .find('.text').text(msg).end() - .addClass(`${style}${full ? ' full-width' : ''}`).fadeIn() + msg = opts.message || '', + style = opts.style != '' ? `toast-${opts.style}` : '', + full = opts.fullWidth || false, + dur = opts.duration || 3000 + $('#toast') + .find('.title') + .text(title) + .end() + .find('.text') + .text(msg) + .end() + .addClass(`${style}${full ? ' full-width' : ''}`) + .fadeIn() setTimeout(function() { $('#toast').fadeOut(500, function() { $('#toast').removeClass(`${style} full-width`) notifyIsActive = false }) }, dur) -} \ No newline at end of file +} + +function displayError(message) { + $('#error') + .append( + `
+ Error! + ${message} +
` + ) + .show() +} + +function checkFrame(frame) { + var html, + failed = false + try { + var doc = frame.contentDocument || frame.contentWindow.document + html = doc.body.innerHTML + } catch (e) { + failed = true + } + + if (!html) failed = true + if (failed) + displayError('Frame loading blocked due to cross-origin prevention') +} + +function checkCORS() { + $('#error') + .empty() + .hide() + if (window.rotationDuration) setTimeout(rotateChannels, rotationDuration) +} + +function rotateChannels() { + if (window.rotationChannels) { + $('iframe').each(function(i) { + var rotationIndex = $(this).attr('data-rotation-index') || 0 + if (rotationIndex < rotationChannels[i].length - 1) rotationIndex++ + else rotationIndex = 0 + $(this).attr('data-rotation-index', rotationIndex) + $(this).attr('src', rotationChannels[i][rotationIndex]) + }) + checkCORS() + } +} + +$('iframe').each(function(i) { + $(this) + .off() + .on('load error', function() { + checkFrame(this) + }) +}) +checkCORS() diff --git a/build/js/channel.js b/build/js/channel.js index 86f578c..6af7d24 100644 --- a/build/js/channel.js +++ b/build/js/channel.js @@ -1,13 +1,31 @@ $('#channel-layout .layout').on('click', function() { - $(this).addClass('active').siblings().removeClass('active') - if ($(this).is('.fullscreen')) $('.display-section').find('input').slice(1).val('').end().end().hide().eq(0).show() - if ($(this).is('.right-panel')) $('.display-section').hide().slice(0, 2).show() + $(this) + .addClass('active') + .siblings() + .removeClass('active') + if ($(this).is('.fullscreen')) + $('.display-section') + .find('input') + .slice(1) + .val('') + .end() + .end() + .hide() + .eq(0) + .show() + if ($(this).is('.right-panel')) + $('.display-section') + .hide() + .slice(0, 2) + .show() }) -$('#edit-channel [type="submit"]').on('click', function (e) { +$('#edit-channel [type="submit"]').on('click', function(e) { e.preventDefault() $('input.is-error').removeClass('is-error') - $empty = $('#edit-channel input:visible').filter(function() { return !this.value }) + $empty = $('#edit-channel input:visible').filter(function() { + return !this.value + }) $empty.addClass('is-error') if ($empty.length || !$('.layout.active').length) { notify({ @@ -16,13 +34,16 @@ $('#edit-channel [type="submit"]').on('click', function (e) { }) } else { var $layout = $('#channel-layout .layout.active'), - layout + layout, + duration = $('#rotation-duration .btn-primary').attr('data-duration') if ($layout.is('.fullscreen')) layout = 'fullscreen' if ($layout.is('.right-panel')) layout = 'right-panel' $.ajax({ method: 'post', - data: `${$('#edit-channel').serialize()}&layout=${layout}`, - success: function (response) { + data: `${$( + '#edit-channel' + ).serialize()}&layout=${layout}&duration=${duration}`, + success: function(response) { window.location = '/channels' } }) @@ -40,3 +61,47 @@ $('#delete-channel').on('click', function() { } return false }) + +$('#channel-list').on('click', '.add-url', function() { + var $section = $(this).prev(), + $prev = $section.find('.form-group').last(), + id = + parseInt( + $prev + .find('label') + .text() + .split(' ') + .pop() + ) + 1, + groupId = + $section + .find('label') + .first() + .text() + .split(' ') + .pop() - 1 + $section.append(` +
+ + +
+ `) + return false +}) + +$('#channel-list').on('click', '.remove-url', function() { + $(this) + .parent() + .remove() + return false +}) + +$('#rotation-duration .btn').on('click', function() { + $(this) + .addClass('btn-primary') + .siblings() + .removeClass('btn-primary') + return false +}) diff --git a/build/js/client.js b/build/js/client.js index 34ca92e..8d61d96 100644 --- a/build/js/client.js +++ b/build/js/client.js @@ -1,9 +1,15 @@ /* socket.io */ var socket = io() -socket.on('connect', function () { }) -socket.on('disconnect', function() { }) -socket.on('refresh', function() { location.reload(true) }) +//console.log('establishing socket.io connection') + +socket.on('connect', function() { + //console.log('connection established!') +}) +socket.on('disconnect', function() {}) +socket.on('refresh', function() { + location.reload(true) +}) socket.on('register', function(device_id) { window.location = `/device/${device_id}` @@ -23,47 +29,60 @@ socket.on('push', function(opts) { }) }) +// Function passed to $('body').removeClass() +function removeBodyRotation(index, css) { + return (css.match(/(^|\s)rot\S+/g) || []).join(' ') +} + /* Google Cast */ -if (cast) { - cast.receiver.logger.setLevelValue(0); - window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance(); - console.log('Starting Receiver Manager'); +if (window.cast !== undefined) { + cast.receiver.logger.setLevelValue(0) + window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance() + console.log('Starting Receiver Manager') // handler for the 'ready' event - castReceiverManager.onReady = function (event) { - console.log('Received Ready event: ' + JSON.stringify(event.data)); - window.castReceiverManager.setApplicationState($(document).find('title').text()); - }; + castReceiverManager.onReady = function(event) { + console.log('Received Ready event: ' + JSON.stringify(event.data)) + window.castReceiverManager.setApplicationState( + $(document) + .find('title') + .text() + ) + } // handler for 'senderconnected' event - castReceiverManager.onSenderConnected = function (event) { - console.log('Received Sender Connected event: ' + event.data); - console.log(window.castReceiverManager.getSender(event.data).userAgent); - }; + castReceiverManager.onSenderConnected = function(event) { + console.log('Received Sender Connected event: ' + event.data) + console.log(window.castReceiverManager.getSender(event.data).userAgent) + } // handler for 'senderdisconnected' event - castReceiverManager.onSenderDisconnected = function (event) { - console.log('Received Sender Disconnected event: ' + event.data); + castReceiverManager.onSenderDisconnected = function(event) { + console.log('Received Sender Disconnected event: ' + event.data) if (window.castReceiverManager.getSenders().length == 0) { - window.close(); + window.close() } - }; + } // handler for 'systemvolumechanged' event - castReceiverManager.onSystemVolumeChanged = function (event) { - console.log('Received System Volume Changed event: ' + event.data['level'] + ' ' + - event.data['muted']); - }; + castReceiverManager.onSystemVolumeChanged = function(event) { + console.log( + 'Received System Volume Changed event: ' + + event.data['level'] + + ' ' + + event.data['muted'] + ) + } // create a CastMessageBus to handle messages for a custom namespace - window.messageBus = - window.castReceiverManager.getCastMessageBus( - 'urn:x-cast:com.unitedcatalystcorporation.ucc-utility'); + window.messageBus = window.castReceiverManager.getCastMessageBus( + 'urn:x-cast:com.unitedcatalystcorporation.ucc-utility' + ) // handler for the CastMessageBus message event - window.messageBus.onMessage = function (event) { - console.log('Message [' + event.senderId + ']: ' + event.data); + window.messageBus.onMessage = function(event) { + console.log('Message [' + event.senderId + ']: ' + event.data) // display the message from the sender - displayText(event.data); + displayText(event.data) // inform all senders on the CastMessageBus of the incoming message event // sender message listener will be invoked - window.messageBus.send(event.senderId, event.data); + window.messageBus.send(event.senderId, event.data) } // initialize the CastReceiverManager with an application status message - window.castReceiverManager.start({ statusText: 'Loading Hub...' }); - console.log('Receiver Manager started'); -} \ No newline at end of file + window.castReceiverManager.start({ statusText: 'Loading Hub...' }) + console.log('Receiver Manager started') +} diff --git a/gulpfile.js b/gulpfile.js index 03cd2fe..0704871 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,31 +1,32 @@ -const gulp = require('gulp') -const uglify = require('gulp-uglify-es').default -const cleanCSS = require('gulp-clean-css') +const gulp = require('gulp') +const uglify = require('gulp-uglify-es').default +const cleanCSS = require('gulp-clean-css') gulp.task('js', () => { - return gulp.src('build/js/*') + return gulp + .src('build/js/*') .pipe(uglify().on('error', err => console.log(err))) .pipe(gulp.dest('public/js')) }) gulp.task('css', () => { - return gulp.src('build/css/*') + return gulp + .src('build/css/*') .pipe(cleanCSS()) .pipe(gulp.dest('public/css')) }) gulp.task('vendor', () => { - gulp.src('node_modules/jquery/dist/jquery.min.js') - .pipe(gulp.dest('public/js')) - gulp.src('node_modules/spectre.css/dist/spectre.min.css') - .pipe(gulp.dest('public/css')) - gulp.src('node_modules/font-awesome/css/*') - .pipe(gulp.dest('public/css')) - gulp.src('node_modules/font-awesome/fonts/*') - .pipe(gulp.dest('public/fonts')) - gulp.src('node_modules/bowser/bowser.min.js') - .pipe(gulp.dest('public/js')) + gulp + .src('node_modules/jquery/dist/jquery.min.js') + .pipe(gulp.dest('public/js')) + gulp + .src('node_modules/spectre.css/dist/spectre.min.css') + .pipe(gulp.dest('public/css')) + gulp.src('node_modules/font-awesome/css/*').pipe(gulp.dest('public/css')) + gulp.src('node_modules/font-awesome/fonts/*').pipe(gulp.dest('public/fonts')) + gulp.src('node_modules/bowser/bowser.min.js').pipe(gulp.dest('public/js')) }) -gulp.task('default', [ 'js', 'css', 'vendor' ]) -gulp.task('minify', [ 'js', 'css' ]) \ No newline at end of file +gulp.task('default', ['js', 'css', 'vendor']) +gulp.task('minify', ['js', 'css']) diff --git a/index.js b/index.js index ef2a609..6c442cb 100644 --- a/index.js +++ b/index.js @@ -10,8 +10,14 @@ if (process.argv.length == 2) { // r console.log(' start start Multicast as a foreground process') console.log('') console.log('Flags:') + console.log(' -v, --version print application version') console.log(' --serve-only do not run the mDNS server (won\'t interrupt existing receivers)') } else { + if (process.argv.find(arg => arg == '-v' || arg == '--version')) { + console.log(`Multicast v${require('./package.json').version}`) + console.log('Author: Aaron Ross (@superhawk610)') + process.exit(0) + } if (process.argv.find(arg => arg == 'config')) require('./app/config.js') // run configuration else require('./app/main.js') // start application } diff --git a/package-lock.json b/package-lock.json index 2248a38..742e468 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,16 @@ { "name": "multicast", - "version": "1.1.0", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/node": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.5.2.tgz", + "integrity": + "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ==", + "dev": true + }, "accepts": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", @@ -20,7 +27,8 @@ }, "acorn-globals": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", + "resolved": + "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", "requires": { "acorn": "4.0.13" @@ -40,7 +48,8 @@ }, "align-text": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "resolved": + "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "requires": { "kind-of": "3.2.2", @@ -55,13 +64,15 @@ }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "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", + "resolved": + "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, @@ -71,6 +82,15 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", @@ -82,48 +102,67 @@ }, "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==", + "resolved": + "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": + "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "array-differ": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", "dev": true }, "array-each": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "resolved": + "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", "dev": true }, "array-flatten": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": + "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-slice": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/array-slice/-/array-slice-1.0.0.tgz", "integrity": "sha1-5zA08A3MH0CHYAj9IP6ud71LfC8=", "dev": true }, + "array-union": { + "version": "1.0.2", + "resolved": + "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, "array-uniq": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "resolved": + "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, "array-unique": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "resolved": + "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, "arraybuffer.slice": { "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", + "resolved": + "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=" }, "asap": { @@ -150,23 +189,54 @@ }, "async-limiter": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + "resolved": + "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": + "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + }, + "babel-code-frame": { + "version": "7.0.0-alpha.12", + "resolved": + "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-7.0.0-alpha.12.tgz", + "integrity": + "sha512-5BmA2es52XNA9PN96HRfJg0co5CkmrKxWdvyW507LNqJmicnHM4we4HI16bIzgAuVeL7RKc4GJmTsyhkFco4Tw==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "babylon": { + "version": "7.0.0-beta.28", + "resolved": + "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.28.tgz", + "integrity": + "sha512-DBCCAejmP2ub9aCxA+ZN+Yv67Z/9yQqs4crzo9IuJxqbCNpR0KnvyzQxoB1S9G/R0vnAonPT0osaKjikuCAWqQ==", + "dev": true }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, + "bail": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.2.tgz", + "integrity": "sha1-99bBcxYwqfnw1NNe0fli4gdKF2Q=", + "dev": true + }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "base64-arraybuffer": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "resolved": + "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" }, "base64id": { @@ -182,7 +252,8 @@ }, "better-assert": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", "requires": { "callsite": "1.0.0" @@ -201,11 +272,13 @@ "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + "integrity": + "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, "body-parser": { "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "resolved": + "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { "bytes": "3.0.0", @@ -222,7 +295,8 @@ }, "brace-expansion": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "resolved": + "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "requires": { @@ -248,17 +322,20 @@ }, "buffer-shims": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" }, "bufferview": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bufferview/-/bufferview-1.0.1.tgz", + "resolved": + "https://registry.npmjs.org/bufferview/-/bufferview-1.0.1.tgz", "integrity": "sha1-ev10pF+Tf6QiodM4wIu/3HbNcl0=" }, "bytebuffer": { "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-3.5.5.tgz", + "resolved": + "https://registry.npmjs.org/bytebuffer/-/bytebuffer-3.5.5.tgz", "integrity": "sha1-em+vGhNRSwg/H8+VQcTJv75+f9M=", "requires": { "bufferview": "1.0.1", @@ -291,7 +368,8 @@ }, "center-align": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "resolved": + "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "requires": { "align-text": "0.1.4", @@ -311,14 +389,43 @@ "supports-color": "2.0.0" } }, + "character-entities": { + "version": "1.2.1", + "resolved": + "https://registry.npmjs.org/character-entities/-/character-entities-1.2.1.tgz", + "integrity": "sha1-92hxvl72bdt/j440eOzDdMJ9bco=", + "dev": true + }, + "character-entities-legacy": { + "version": "1.1.1", + "resolved": + "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.1.tgz", + "integrity": "sha1-9Ad53xoQGHK7UQo9KV4fzPFHIC8=", + "dev": true + }, "character-parser": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "resolved": + "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", "requires": { "is-regex": "1.0.4" } }, + "character-reference-invalid": { + "version": "1.1.1", + "resolved": + "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.1.tgz", + "integrity": "sha1-lCg191Dk7GGjCOYMLvjMEBEgLvw=", + "dev": true + }, + "cjk-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cjk-regex/-/cjk-regex-1.0.2.tgz", + "integrity": + "sha512-NwSMtwULPLk8Ka9DEUcoFXhMRnV/bpyKDnoyDiVw/Qy5przhvHTvXLcsKaOmx13o8J4XEsPVT1baoCUj5zQs3w==", + "dev": true + }, "clean-css": { "version": "3.4.28", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", @@ -346,10 +453,36 @@ }, "clone-stats": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "resolved": + "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", "dev": true }, + "collapse-white-space": { + "version": "1.0.3", + "resolved": + "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.3.tgz", + "integrity": "sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": + "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": + "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": + "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "colour": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", @@ -365,28 +498,33 @@ }, "component-bind": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" }, "component-emitter": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "resolved": + "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "component-inherit": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "resolved": + "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": + "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "constantinople": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz", + "resolved": + "https://registry.npmjs.org/constantinople/-/constantinople-3.1.0.tgz", "integrity": "sha1-dWnKqKo/jVk11i4fqW+fcCzYHHk=", "requires": { "acorn": "3.3.0", @@ -395,13 +533,16 @@ }, "content-disposition": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "resolved": + "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" }, "content-type": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + "resolved": + "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": + "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { "version": "0.3.1", @@ -410,31 +551,65 @@ }, "cookie-signature": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "resolved": + "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", + "requires": { + "object-assign": "4.1.1", + "vary": "1.1.2" + } + }, + "cosmiconfig": { + "version": "3.1.0", + "resolved": + "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-3.1.0.tgz", + "integrity": + "sha512-zedsBhLSbPBms+kE7AH4vHg6JsKDz6epSv2/+5XHs8ILHlgDciSJfSWf8sX9aQ52Jb7KI7VswUTsLpR/G0cr2Q==", + "dev": true, + "requires": { + "is-directory": "0.3.1", + "js-yaml": "3.10.0", + "parse-json": "3.0.0", + "require-from-string": "2.0.1" + } + }, + "dashify": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dashify/-/dashify-0.2.2.tgz", + "integrity": "sha1-agdBWgHJH69KMuONnfunH2HLIP4=", + "dev": true + }, "dateformat": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "resolved": + "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", "dev": true }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "integrity": + "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" } }, "decamelize": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "resolved": + "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "defaults": { @@ -453,7 +628,8 @@ }, "deprecated": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", + "resolved": + "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=", "dev": true }, @@ -464,13 +640,27 @@ }, "detect-file": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", + "resolved": + "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", "dev": true, "requires": { "fs-exists-sync": "0.1.0" } }, + "detect-newline": { + "version": "2.1.0", + "resolved": + "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", + "dev": true + }, "doctypes": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", @@ -493,7 +683,8 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": + "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -505,7 +696,8 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": + "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true } @@ -516,6 +708,14 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "6.5.1", + "resolved": + "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz", + "integrity": + "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==", + "dev": true + }, "encodeurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", @@ -523,7 +723,8 @@ }, "end-of-stream": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "resolved": + "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", "dev": true, "requires": { @@ -557,7 +758,8 @@ }, "engine.io-client": { "version": "3.1.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.4.tgz", + "resolved": + "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.4.tgz", "integrity": "sha1-T88TcLRxY70s6b4nM5ckMDUNTqE=", "requires": { "component-emitter": "1.2.1", @@ -575,7 +777,8 @@ }, "engine.io-parser": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.1.tgz", + "resolved": + "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.1.tgz", "integrity": "sha1-4Ps/DgRi9/WLt3waUun1p+JuRmg=", "requires": { "after": "0.8.2", @@ -585,22 +788,47 @@ "has-binary2": "1.0.2" } }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, "es6-promise": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "resolved": + "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" }, "escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "resolved": + "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "resolved": + "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": + "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -608,7 +836,8 @@ }, "expand-brackets": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "resolved": + "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { @@ -617,7 +846,8 @@ }, "expand-range": { "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "resolved": + "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { @@ -626,7 +856,8 @@ }, "expand-tilde": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", + "resolved": + "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", "dev": true, "requires": { @@ -672,12 +903,15 @@ "dependencies": { "setprototypeof": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "resolved": + "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": + "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, "statuses": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "resolved": + "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" } } @@ -707,15 +941,26 @@ "time-stamp": "1.1.0" } }, + "fault": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.1.tgz", + "integrity": "sha1-3o01Df1IviS13BsChn4IcbkTUJI=", + "dev": true, + "requires": { + "format": "0.2.2" + } + }, "filename-regex": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "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", + "resolved": + "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", "dev": true, "requires": { @@ -728,7 +973,8 @@ }, "finalhandler": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "resolved": + "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", "requires": { "debug": "2.6.9", @@ -742,20 +988,23 @@ "dependencies": { "statuses": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "resolved": + "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" } } }, "find-index": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", + "resolved": + "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", "dev": true }, "findup-sync": { "version": "0.4.3", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", + "resolved": + "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", "dev": true, "requires": { @@ -780,7 +1029,8 @@ "dependencies": { "expand-tilde": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "resolved": + "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { @@ -791,19 +1041,35 @@ }, "first-chunk-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", "dev": true }, "flagged-respawn": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz", + "resolved": + "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-0.3.2.tgz", "integrity": "sha1-/xke3c1wiKZ1smEP/8l2vpuAdLU=", "dev": true }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", + "dev": true + }, + "flow-parser": { + "version": "0.59.0", + "resolved": + "https://registry.npmjs.org/flow-parser/-/flow-parser-0.59.0.tgz", + "integrity": "sha1-9uvK5h/6GH5CCZnUDOCoAfObJjU=", + "dev": true + }, "font-awesome": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "resolved": + "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" }, "for-in": { @@ -821,6 +1087,12 @@ "for-in": "1.0.2" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=", + "dev": true + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -833,14 +1105,24 @@ }, "fs-exists-sync": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "resolved": + "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", "dev": true }, + "fs.realpath": { + "version": "1.0.0", + "resolved": + "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, "function-bind": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "resolved": + "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": + "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "gaze": { "version": "0.5.2", @@ -851,6 +1133,13 @@ "globule": "0.1.0" } }, + "get-stream": { + "version": "3.0.0", + "resolved": + "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, "glob": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", @@ -875,7 +1164,8 @@ }, "glob-parent": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "resolved": + "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { @@ -884,7 +1174,8 @@ }, "glob-stream": { "version": "3.1.18", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", + "resolved": + "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", "dev": true, "requires": { @@ -904,7 +1195,8 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": + "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -916,13 +1208,15 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": + "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, "through2": { "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "resolved": + "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { @@ -934,7 +1228,8 @@ }, "glob-watcher": { "version": "0.0.6", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", + "resolved": + "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", "dev": true, "requires": { @@ -952,7 +1247,8 @@ }, "global-modules": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", + "resolved": + "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", "dev": true, "requires": { @@ -962,7 +1258,8 @@ }, "global-prefix": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", + "resolved": + "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", "dev": true, "requires": { @@ -972,6 +1269,47 @@ "which": "1.3.0" } }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "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.3.3", + "path-is-absolute": "1.0.1" + } + }, + "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.8" + } + } + } + }, "globule": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", @@ -996,13 +1334,15 @@ }, "graceful-fs": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "resolved": + "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", "dev": true }, "inherits": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", "dev": true }, @@ -1014,7 +1354,8 @@ }, "minimatch": { "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "resolved": + "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", "dev": true, "requires": { @@ -1035,7 +1376,8 @@ }, "graceful-fs": { "version": "3.0.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "resolved": + "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "dev": true, "requires": { @@ -1044,9 +1386,20 @@ }, "graceful-readlink": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "resolved": + "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, + "graphql": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.10.5.tgz", + "integrity": + "sha512-Q7cx22DiLhwHsEfUnUip1Ww/Vfx7FS0w6+iHItNuN61+XpegHSa3k5U0+6M5BcpavQImBwFiy0z3uYwY7cXMLQ==", + "dev": true, + "requires": { + "iterall": "1.1.3" + } + }, "gulp": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", @@ -1078,8 +1431,10 @@ }, "gulp-clean-css": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.9.0.tgz", - "integrity": "sha512-CsqaSO2ZTMQI/WwbWloZWBudhsRMKgxBthzxt4bbcbWrjOY4pRFziyK9IH6YbTpaWAPKEwWpopPkpiAEoDofxw==", + "resolved": + "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.9.0.tgz", + "integrity": + "sha512-CsqaSO2ZTMQI/WwbWloZWBudhsRMKgxBthzxt4bbcbWrjOY4pRFziyK9IH6YbTpaWAPKEwWpopPkpiAEoDofxw==", "dev": true, "requires": { "clean-css": "4.1.9", @@ -1090,7 +1445,8 @@ "dependencies": { "clean-css": { "version": "4.1.9", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz", + "resolved": + "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz", "integrity": "sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE=", "dev": true, "requires": { @@ -1099,7 +1455,8 @@ }, "source-map": { "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "resolved": + "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } @@ -1118,8 +1475,10 @@ }, "gulp-uglify-es": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/gulp-uglify-es/-/gulp-uglify-es-0.1.4.tgz", - "integrity": "sha512-gVAzB1ubmPScn49lU78fA+CZzW8jK6GGAY7p9M11WKO/yIs9DY+4WB+GUnZ53sD6Wq9GpaOPUPH7WRgmKxBsqw==", + "resolved": + "https://registry.npmjs.org/gulp-uglify-es/-/gulp-uglify-es-0.1.4.tgz", + "integrity": + "sha512-gVAzB1ubmPScn49lU78fA+CZzW8jK6GGAY7p9M11WKO/yIs9DY+4WB+GUnZ53sD6Wq9GpaOPUPH7WRgmKxBsqw==", "dev": true, "requires": { "gulp-exec": "2.1.3", @@ -1129,20 +1488,26 @@ "dependencies": { "commander": { "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "resolved": + "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": + "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", "dev": true }, "source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "resolved": + "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": + "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "uglify-es": { "version": "3.1.10", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.1.10.tgz", - "integrity": "sha512-RwBX0aOeHvO8MKKUeLCArQGb9OZ6xA+EqfVxsE9wqK0saFYFVLIFvHeeCOg61C6NO6KCuSiG9OjNjCA+OB4nzg==", + "resolved": + "https://registry.npmjs.org/uglify-es/-/uglify-es-3.1.10.tgz", + "integrity": + "sha512-RwBX0aOeHvO8MKKUeLCArQGb9OZ6xA+EqfVxsE9wqK0saFYFVLIFvHeeCOg61C6NO6KCuSiG9OjNjCA+OB4nzg==", "dev": true, "requires": { "commander": "2.11.0", @@ -1179,7 +1544,8 @@ "dependencies": { "object-assign": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "resolved": + "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", "dev": true } @@ -1213,7 +1579,8 @@ }, "has-binary2": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz", "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=", "requires": { "isarray": "2.0.1" @@ -1231,9 +1598,16 @@ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, "has-gulplog": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "resolved": + "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", "dev": true, "requires": { @@ -1242,7 +1616,8 @@ }, "homedir-polyfill": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "resolved": + "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "dev": true, "requires": { @@ -1251,12 +1626,15 @@ }, "hooks-fixed": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.2.tgz", - "integrity": "sha512-YurCM4gQSetcrhwEtpQHhQ4M7Zo7poNGqY4kQGeBS6eZtOcT3tnNs01ThFa0jYBByAiYt1MjMjP/YApG0EnAvQ==" + "resolved": + "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.2.tgz", + "integrity": + "sha512-YurCM4gQSetcrhwEtpQHhQ4M7Zo7poNGqY4kQGeBS6eZtOcT3tnNs01ThFa0jYBByAiYt1MjMjP/YApG0EnAvQ==" }, "http-errors": { "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "resolved": + "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", "requires": { "depd": "1.1.1", @@ -1267,8 +1645,24 @@ }, "iconv-lite": { "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + "resolved": + "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": + "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": + "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true }, "indexof": { "version": "0.0.1", @@ -1293,7 +1687,8 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "integrity": + "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, "interpret": { @@ -1309,7 +1704,8 @@ }, "is-absolute": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", + "resolved": + "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=", "dev": true, "requires": { @@ -1317,20 +1713,62 @@ "is-windows": "0.2.0" } }, + "is-alphabetical": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.1.tgz", + "integrity": "sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg=", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz", + "integrity": "sha1-37SqTRCF4zvbYcLe6cgOnGwZ9Ts=", + "dev": true, + "requires": { + "is-alphabetical": "1.0.1", + "is-decimal": "1.0.1" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": + "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": + "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-decimal": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.1.tgz", + "integrity": "sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI=", + "dev": true + }, + "is-directory": { + "version": "0.3.1", + "resolved": + "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true }, "is-dotfile": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "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", + "resolved": + "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { @@ -1339,7 +1777,8 @@ }, "is-expression": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-2.1.0.tgz", + "resolved": + "https://registry.npmjs.org/is-expression/-/is-expression-2.1.0.tgz", "integrity": "sha1-kb6dR968/vB3l36XIr5tz7RGXvA=", "requires": { "acorn": "3.3.0", @@ -1348,16 +1787,25 @@ }, "is-extendable": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "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", + "resolved": + "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", "dev": true }, + "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", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, "is-glob": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", @@ -1367,6 +1815,13 @@ "is-extglob": "1.0.0" } }, + "is-hexadecimal": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz", + "integrity": "sha1-bghLvJIGH7sJcexYts5tQE4k2mk=", + "dev": true + }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", @@ -1376,10 +1831,19 @@ "kind-of": "3.2.2" } }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": + "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, "is-plain-object": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "resolved": + "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": + "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "3.0.1" @@ -1387,7 +1851,8 @@ "dependencies": { "isobject": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "resolved": + "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } @@ -1395,19 +1860,22 @@ }, "is-posix-bracket": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "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", + "resolved": + "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, "is-promise": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "resolved": + "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" }, "is-regex": { @@ -1420,7 +1888,8 @@ }, "is-relative": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", + "resolved": + "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", "dev": true, "requires": { @@ -1429,7 +1898,8 @@ }, "is-unc-path": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", + "resolved": + "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", "dev": true, "requires": { @@ -1442,12 +1912,27 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, + "is-whitespace-character": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz", + "integrity": "sha1-muAXbzKCtlRXoZks2whPil+DPjs=", + "dev": true + }, "is-windows": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "resolved": + "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", "dev": true }, + "is-word-character": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.1.tgz", + "integrity": "sha1-WgP6HqkazopusMfNdw64bWXIvvs=", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1468,19 +1953,120 @@ "isarray": "1.0.0" } }, + "iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": + "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true + }, + "jest-docblock": { + "version": "21.3.0-beta.7", + "resolved": + "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.3.0-beta.7.tgz", + "integrity": + "sha512-Qk4si4BSb79ZNUsxYHGBXHf+nIUD9fbgfkipow+WGL8HSwp1cDOU1i127MC5kIqjoU7h9HOZcJlzBKcip4uizQ==", + "dev": true, + "requires": { + "detect-newline": "2.1.0" + } + }, + "jest-get-type": { + "version": "21.2.0", + "resolved": + "https://registry.npmjs.org/jest-get-type/-/jest-get-type-21.2.0.tgz", + "integrity": + "sha512-y2fFw3C+D0yjNSDp7ab1kcd6NUYfy3waPTlD8yWkAtiocJdBRQqNoRqVfMNxgj+IjT0V5cBIHJO0z9vuSSZ43Q==", + "dev": true + }, + "jest-validate": { + "version": "21.1.0", + "resolved": + "https://registry.npmjs.org/jest-validate/-/jest-validate-21.1.0.tgz", + "integrity": + "sha512-xS0cyErNWpsLFlGkn/b87pk/Mv7J+mCTs8hQ4KmtOIIoM1sHYobXII8AtkoN8FC7E3+Ptxjo+/3xWk6LK1dKcw==", + "dev": true, + "requires": { + "chalk": "2.3.0", + "jest-get-type": "21.2.0", + "leven": "2.1.0", + "pretty-format": "21.2.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": + "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": + "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": + "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": + "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, "jquery": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=" }, + "js-base64": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.0.tgz", + "integrity": + "sha512-Wehd+7Pf9tFvGb+ydPm9TjYjV8X1YHOVyG8QyELZxEMqOhemVwGRmoG8iQ/soqI3n8v4xn59zaLxiCJiaaRzKA==", + "dev": true + }, "js-stringify": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": + "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, "jstransformer": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", "requires": { "is-promise": "2.1.0", @@ -1502,9 +2088,16 @@ }, "lazy-cache": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "resolved": + "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" }, + "leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "dev": true + }, "liftoff": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.3.0.tgz", @@ -1529,61 +2122,71 @@ }, "lodash._basecopy": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "resolved": + "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", "dev": true }, "lodash._basetostring": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "resolved": + "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", "dev": true }, "lodash._basevalues": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "resolved": + "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", "dev": true }, "lodash._getnative": { "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "resolved": + "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", "dev": true }, "lodash._isiterateecall": { "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "resolved": + "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", "dev": true }, "lodash._reescape": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "resolved": + "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", "dev": true }, "lodash._reevaluate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "resolved": + "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", "dev": true }, "lodash._reinterpolate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "resolved": + "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, "lodash._root": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "resolved": + "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", "dev": true }, "lodash.escape": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "resolved": + "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", "dev": true, "requires": { @@ -1592,36 +2195,42 @@ }, "lodash.get": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "resolved": + "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" }, "lodash.isarguments": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "resolved": + "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", "dev": true }, "lodash.isarray": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "resolved": + "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, "lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "resolved": + "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", "dev": true }, "lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "resolved": + "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", "dev": true }, "lodash.keys": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "resolved": + "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { @@ -1632,19 +2241,22 @@ }, "lodash.mapvalues": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "resolved": + "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=", "dev": true }, "lodash.restparam": { "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "resolved": + "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", "dev": true }, "lodash.template": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "resolved": + "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { @@ -1661,7 +2273,8 @@ }, "lodash.templatesettings": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "resolved": + "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { @@ -1669,6 +2282,13 @@ "lodash.escape": "3.2.0" } }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": + "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, "long": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz", @@ -1691,10 +2311,18 @@ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, + "markdown-escapes": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.1.tgz", + "integrity": "sha1-GZTfLTr0gR3lmmcUk0wrIpJzRRg=", + "dev": true + }, "mdns": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/mdns/-/mdns-2.3.4.tgz", - "integrity": "sha512-Z4WTKeTukCtJG53SS3BGNnsGkHdIXNZa9nwGMYeoohU1AjEBPS3c/1vIx95SEfeQKYduuOMTo1E4RfXDUt2ZYg==", + "integrity": + "sha512-Z4WTKeTukCtJG53SS3BGNnsGkHdIXNZa9nwGMYeoohU1AjEBPS3c/1vIx95SEfeQKYduuOMTo1E4RfXDUt2ZYg==", "requires": { "bindings": "1.2.1", "nan": "2.3.5" @@ -1702,12 +2330,23 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": + "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } + }, "merge-descriptors": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "resolved": + "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, "methods": { @@ -1717,7 +2356,8 @@ }, "micromatch": { "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "resolved": + "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { @@ -1739,7 +2379,8 @@ "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + "integrity": + "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" }, "mime-db": { "version": "1.30.0", @@ -1748,12 +2389,19 @@ }, "mime-types": { "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "resolved": + "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "requires": { "mime-db": "1.30.0" } }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, "minimatch": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", @@ -1780,7 +2428,8 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": + "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -1798,7 +2447,8 @@ }, "mongodb-core": { "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.17.tgz", + "resolved": + "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.17.tgz", "integrity": "sha1-pBizN6FKFJkPtRC5I97mqBMXPfg=", "requires": { "bson": "1.0.4", @@ -1808,7 +2458,8 @@ "mongoose": { "version": "4.13.4", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.13.4.tgz", - "integrity": "sha512-MYS9qWVmWz0PJ1tewtGisKT3/61ctcgX05w9epcy2WQ+1IH4HIPFO5hToR4u+ro7fyBWpSsTlUPMBJblzRLCzQ==", + "integrity": + "sha512-MYS9qWVmWz0PJ1tewtGisKT3/61ctcgX05w9epcy2WQ+1IH4HIPFO5hToR4u+ro7fyBWpSsTlUPMBJblzRLCzQ==", "requires": { "async": "2.1.4", "bson": "1.0.4", @@ -1838,7 +2489,8 @@ "mquery": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.2.tgz", - "integrity": "sha512-KXWMypZSvhCuqRtza+HMQZdYw7PfFBjBTFvP31NNAq0OX0/NTIgpcDpkWQ2uTxk6vGQtwQ2elhwhs+ZvCA8OaA==", + "integrity": + "sha512-KXWMypZSvhCuqRtza+HMQZdYw7PfFBjBTFvP31NNAq0OX0/NTIgpcDpkWQ2uTxk6vGQtwQ2elhwhs+ZvCA8OaA==", "requires": { "bluebird": "3.5.1", "debug": "2.6.9", @@ -1870,7 +2522,8 @@ "muri": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/muri/-/muri-1.3.0.tgz", - "integrity": "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg==" + "integrity": + "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg==" }, "nan": { "version": "2.3.5", @@ -1885,12 +2538,14 @@ }, "negotiator": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "resolved": + "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, "normalize-path": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "resolved": + "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { @@ -1900,22 +2555,26 @@ "o-stream": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/o-stream/-/o-stream-0.2.2.tgz", - "integrity": "sha512-V3j76KU3g/Gyl8rpdi2z72rn5zguMvTCQgAXfBe3pxEefKqXmOUOD7mvx/mNjykdxGqDVfpSoo8r+WdrkWg/1Q==", + "integrity": + "sha512-V3j76KU3g/Gyl8rpdi2z72rn5zguMvTCQgAXfBe3pxEefKqXmOUOD7mvx/mNjykdxGqDVfpSoo8r+WdrkWg/1Q==", "dev": true }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": + "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-component": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "resolved": + "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" }, "object.defaults": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "resolved": + "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, "requires": { @@ -1936,7 +2595,8 @@ }, "isobject": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "resolved": + "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } @@ -1944,7 +2604,8 @@ }, "object.omit": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "resolved": + "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { @@ -1954,7 +2615,8 @@ }, "object.pick": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "resolved": + "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { @@ -1963,7 +2625,8 @@ "dependencies": { "isobject": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "resolved": + "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true } @@ -1971,7 +2634,8 @@ }, "on-finished": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "resolved": + "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", "requires": { "ee-first": "1.1.1" @@ -1993,7 +2657,8 @@ }, "orchestrator": { "version": "0.3.8", - "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", + "resolved": + "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", "dev": true, "requires": { @@ -2004,19 +2669,37 @@ }, "ordered-read-streams": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", + "resolved": + "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=", "dev": true }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "parse-entities": { + "version": "1.1.1", + "resolved": + "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.1.tgz", + "integrity": "sha1-gRLYhHExnyerrk1klksSL+ThuJA=", + "dev": true, + "requires": { + "character-entities": "1.2.1", + "character-entities-legacy": "1.1.1", + "character-reference-invalid": "1.1.1", + "is-alphanumerical": "1.0.1", + "is-decimal": "1.0.1", + "is-hexadecimal": "1.0.1" + } + }, "parse-filepath": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz", + "resolved": + "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.1.tgz", "integrity": "sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M=", "dev": true, "requires": { @@ -2027,7 +2710,8 @@ }, "parse-glob": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "resolved": + "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { @@ -2037,12 +2721,33 @@ "is-glob": "2.0.1" } }, + "parse-json": { + "version": "3.0.0", + "resolved": + "https://registry.npmjs.org/parse-json/-/parse-json-3.0.0.tgz", + "integrity": "sha1-+m9HsY4jgm6tMvJj50TQ4ehH+xM=", + "dev": true, + "requires": { + "error-ex": "1.3.1" + } + }, "parse-passwd": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": + "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "dev": true, + "requires": { + "@types/node": "8.5.2" + } + }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", @@ -2064,9 +2769,17 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, + "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 + }, "path-parse": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "resolved": + "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" }, "path-root": { @@ -2080,43 +2793,347 @@ }, "path-root-regex": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "resolved": + "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, "path-to-regexp": { "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "resolved": + "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": + "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": + "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "1.1.3", + "js-base64": "2.4.0", + "source-map": "0.5.7", + "supports-color": "3.2.3" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": + "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "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 + }, + "supports-color": { + "version": "3.2.3", + "resolved": + "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "1.0.0" + } + } + } + }, + "postcss-less": { + "version": "1.1.1", + "resolved": + "https://registry.npmjs.org/postcss-less/-/postcss-less-1.1.1.tgz", + "integrity": + "sha512-zl0EEqq8Urh37Ppdv9zzhpZpLHrgkxmt6e3O4ftRa7/b8Uq2LV+/KBVM8/KuzmHNu+mthhOArg1lxbfqQ3NUdg==", + "dev": true, + "requires": { + "postcss": "5.2.18" + } + }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": + "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, + "postcss-scss": { + "version": "1.0.2", + "resolved": + "https://registry.npmjs.org/postcss-scss/-/postcss-scss-1.0.2.tgz", + "integrity": "sha1-/0XPM1S4ee6JpOtoaA9GrJuxT5Q=", + "dev": true, + "requires": { + "postcss": "6.0.14" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": + "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": + "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": + "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "postcss": { + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz", + "integrity": + "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==", + "dev": true, + "requires": { + "chalk": "2.3.0", + "source-map": "0.6.1", + "supports-color": "4.5.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": + "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": + "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "4.5.0", + "resolved": + "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": + "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true, + "requires": { + "flatten": "1.0.2", + "indexes-of": "1.0.1", + "uniq": "1.0.1" + } + }, + "postcss-values-parser": { + "version": "1.3.1", + "resolved": + "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-1.3.1.tgz", + "integrity": + "sha512-chFn9CnFAAUpQ3cwrxvVjKB8c0y6BfONv6eapndJoTXJ3h8fr1uAiue8lGP3rUIpBI2KgJGdgCVk9KNvXh0n6A==", + "dev": true, + "requires": { + "flatten": "1.0.2", + "indexes-of": "1.0.1", + "uniq": "1.0.1" + } + }, "preserve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, + "prettier": { + "version": + "git+https://github.com/superhawk610/prettier.git#be4bf4467857ad7f3ca45901dac48a9fcc61c521", + "dev": true, + "requires": { + "babel-code-frame": "7.0.0-alpha.12", + "babylon": "7.0.0-beta.28", + "camelcase": "4.1.0", + "chalk": "2.1.0", + "cjk-regex": "1.0.2", + "cosmiconfig": "3.1.0", + "dashify": "0.2.2", + "diff": "3.2.0", + "emoji-regex": "6.5.1", + "escape-string-regexp": "1.0.5", + "esutils": "2.0.2", + "flow-parser": "0.59.0", + "get-stream": "3.0.0", + "globby": "6.1.0", + "graphql": "0.10.5", + "ignore": "3.3.7", + "jest-docblock": "21.3.0-beta.7", + "jest-validate": "21.1.0", + "leven": "2.1.0", + "mem": "1.1.0", + "minimatch": "3.0.4", + "minimist": "1.2.0", + "parse5": "3.0.3", + "postcss-less": "1.1.1", + "postcss-media-query-parser": "0.2.3", + "postcss-scss": "1.0.2", + "postcss-selector-parser": "2.2.3", + "postcss-values-parser": "1.3.1", + "remark-frontmatter": "1.1.0", + "remark-parse": "4.0.0", + "semver": "5.4.1", + "string-width": "2.1.1", + "typescript": "2.5.3", + "typescript-eslint-parser": + "git://github.com/eslint/typescript-eslint-parser.git#9c71a627da36e97da52ed2731d58509c952b67ae", + "unicode-regex": "1.0.1", + "unified": "6.1.5" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": + "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": + "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": + "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", + "integrity": + "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.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.8" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": + "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "pretty-format": { + "version": "21.2.1", + "resolved": + "https://registry.npmjs.org/pretty-format/-/pretty-format-21.2.1.tgz", + "integrity": + "sha512-ZdWPGYAnYfcVP8yKA3zFjCn8s4/17TeYH28MXuC8vTp0o21eXjbFGcOAXZEaDaOFJjc3h2qa7HQNHNshhvoh2A==", + "dev": true, + "requires": { + "ansi-regex": "3.0.0", + "ansi-styles": "3.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": + "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": + "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": + "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + } + } + }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": + "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, "process-nextick-args": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "resolved": + "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "integrity": + "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { "asap": "2.0.6" } }, "protobufjs": { "version": "3.8.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-3.8.2.tgz", + "resolved": + "https://registry.npmjs.org/protobufjs/-/protobufjs-3.8.2.tgz", "integrity": "sha1-vIJuNMOvRpfo0K96Zp5NYSrtzRc=", "requires": { "ascli": "0.3.0", @@ -2125,7 +3142,8 @@ }, "proxy-addr": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "resolved": + "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", "requires": { "forwarded": "0.1.2", @@ -2135,7 +3153,8 @@ "pug": { "version": "2.0.0-rc.4", "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.0-rc.4.tgz", - "integrity": "sha512-SL7xovj6E2Loq9N0UgV6ynjMLW4urTFY/L/Fprhvz13Xc5vjzkjZjI1QHKq31200+6PSD8PyU6MqrtCTJj6/XA==", + "integrity": + "sha512-SL7xovj6E2Loq9N0UgV6ynjMLW4urTFY/L/Fprhvz13Xc5vjzkjZjI1QHKq31200+6PSD8PyU6MqrtCTJj6/XA==", "requires": { "pug-code-gen": "2.0.0", "pug-filters": "2.1.5", @@ -2159,8 +3178,10 @@ }, "pug-code-gen": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.0.tgz", - "integrity": "sha512-E4oiJT+Jn5tyEIloj8dIJQognbiNNp0i0cAJmYtQTFS0soJ917nlIuFtqVss3IXMEyQKUew3F4gIkBpn18KbVg==", + "resolved": + "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.0.tgz", + "integrity": + "sha512-E4oiJT+Jn5tyEIloj8dIJQognbiNNp0i0cAJmYtQTFS0soJ917nlIuFtqVss3IXMEyQKUew3F4gIkBpn18KbVg==", "requires": { "constantinople": "3.1.0", "doctypes": "1.1.0", @@ -2179,8 +3200,10 @@ }, "pug-filters": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.5.tgz", - "integrity": "sha512-xkw71KtrC4sxleKiq+cUlQzsiLn8pM5+vCgkChW2E6oNOzaqTSIBKIQ5cl4oheuDzvJYCTSYzRaVinMUrV4YLQ==", + "resolved": + "https://registry.npmjs.org/pug-filters/-/pug-filters-2.1.5.tgz", + "integrity": + "sha512-xkw71KtrC4sxleKiq+cUlQzsiLn8pM5+vCgkChW2E6oNOzaqTSIBKIQ5cl4oheuDzvJYCTSYzRaVinMUrV4YLQ==", "requires": { "clean-css": "3.4.28", "constantinople": "3.1.0", @@ -2208,7 +3231,8 @@ }, "is-expression": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", + "resolved": + "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", "requires": { "acorn": "4.0.13", @@ -2219,8 +3243,10 @@ }, "pug-linker": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.3.tgz", - "integrity": "sha512-DCKczglCXOzJ1lr4xUj/lVHYvS+lGmR2+KTCjZjtIpdwaN7lNOoX2SW6KFX5X4ElvW+6ThwB+acSUg08UJFN5A==", + "resolved": + "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.3.tgz", + "integrity": + "sha512-DCKczglCXOzJ1lr4xUj/lVHYvS+lGmR2+KTCjZjtIpdwaN7lNOoX2SW6KFX5X4ElvW+6ThwB+acSUg08UJFN5A==", "requires": { "pug-error": "1.3.2", "pug-walk": "1.1.5" @@ -2229,7 +3255,8 @@ "pug-load": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.9.tgz", - "integrity": "sha512-BDdZOCru4mg+1MiZwRQZh25+NTRo/R6/qArrdWIf308rHtWA5N9kpoUskRe4H6FslaQujC+DigH9LqlBA4gf6Q==", + "integrity": + "sha512-BDdZOCru4mg+1MiZwRQZh25+NTRo/R6/qArrdWIf308rHtWA5N9kpoUskRe4H6FslaQujC+DigH9LqlBA4gf6Q==", "requires": { "object-assign": "4.1.1", "pug-walk": "1.1.5" @@ -2237,8 +3264,10 @@ }, "pug-parser": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-4.0.0.tgz", - "integrity": "sha512-ocEUFPdLG9awwFj0sqi1uiZLNvfoodCMULZzkRqILryIWc/UUlDlxqrKhKjAIIGPX/1SNsvxy63+ayEGocGhQg==", + "resolved": + "https://registry.npmjs.org/pug-parser/-/pug-parser-4.0.0.tgz", + "integrity": + "sha512-ocEUFPdLG9awwFj0sqi1uiZLNvfoodCMULZzkRqILryIWc/UUlDlxqrKhKjAIIGPX/1SNsvxy63+ayEGocGhQg==", "requires": { "pug-error": "1.3.2", "token-stream": "0.0.1" @@ -2246,12 +3275,14 @@ }, "pug-runtime": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.3.tgz", + "resolved": + "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.3.tgz", "integrity": "sha1-mBYmB7D86eJU1CfzOYelrucWi9o=" }, "pug-strip-comments": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.2.tgz", "integrity": "sha1-0xOvoBvMN0mA4TmeI+vy65vchRM=", "requires": { "pug-error": "1.3.2" @@ -2260,17 +3291,21 @@ "pug-walk": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.5.tgz", - "integrity": "sha512-rJlH1lXerCIAtImXBze3dtKq/ykZMA4rpO9FnPcIgsWcxZLOvd8zltaoeOVFyBSSqCkhhJWbEbTMga8UxWUUSA==" + "integrity": + "sha512-rJlH1lXerCIAtImXBze3dtKq/ykZMA4rpO9FnPcIgsWcxZLOvd8zltaoeOVFyBSSqCkhhJWbEbTMga8UxWUUSA==" }, "qs": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + "integrity": + "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" }, "randomatic": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "resolved": + "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": + "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", "dev": true, "requires": { "is-number": "3.0.0", @@ -2279,7 +3314,8 @@ "dependencies": { "is-number": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "resolved": + "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { @@ -2288,7 +3324,8 @@ "dependencies": { "kind-of": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "resolved": + "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { @@ -2310,7 +3347,8 @@ }, "range-parser": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "resolved": + "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" }, "raw-body": { @@ -2326,7 +3364,8 @@ }, "readable-stream": { "version": "2.2.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", + "resolved": + "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", "requires": { "buffer-shims": "1.0.0", @@ -2349,8 +3388,10 @@ }, "regex-cache": { "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "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" @@ -2358,36 +3399,87 @@ }, "regexp-clone": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", + "resolved": + "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" }, + "remark-frontmatter": { + "version": "1.1.0", + "resolved": + "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-1.1.0.tgz", + "integrity": + "sha512-mLbYtwP9w1L9TA8dX+I/HyDF5lCpa0dmYvvW9Io+zUPpqEZ49QMKWb0hSpunpLVA+Squy0SowzSzjHVPbxWq1g==", + "dev": true, + "requires": { + "fault": "1.0.1", + "xtend": "4.0.1" + } + }, + "remark-parse": { + "version": "4.0.0", + "resolved": + "https://registry.npmjs.org/remark-parse/-/remark-parse-4.0.0.tgz", + "integrity": + "sha512-XZgICP2gJ1MHU7+vQaRM+VA9HEL3X253uwUM/BGgx3iv6TH2B3bF3B8q00DKcyP9YrJV+/7WOWEWBFF/u8cIsw==", + "dev": true, + "requires": { + "collapse-white-space": "1.0.3", + "is-alphabetical": "1.0.1", + "is-decimal": "1.0.1", + "is-whitespace-character": "1.0.1", + "is-word-character": "1.0.1", + "markdown-escapes": "1.0.1", + "parse-entities": "1.1.1", + "repeat-string": "1.6.1", + "state-toggle": "1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "1.1.0", + "unherit": "1.1.0", + "unist-util-remove-position": "1.1.1", + "vfile-location": "2.0.2", + "xtend": "4.0.1" + } + }, "remove-trailing-separator": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "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", + "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", + "resolved": + "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "replace-ext": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "resolved": + "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", "dev": true }, + "require-from-string": { + "version": "2.0.1", + "resolved": + "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.1.tgz", + "integrity": "sha1-xUUjPp19pmFunVmt+zn8n1iGdv8=", + "dev": true + }, "require_optional": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", - "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "resolved": + "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": + "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", "requires": { "resolve-from": "2.0.0", "semver": "5.4.1" @@ -2396,14 +3488,16 @@ "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", - "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "integrity": + "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", "requires": { "path-parse": "1.0.5" } }, "resolve-dir": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", + "resolved": + "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", "dev": true, "requires": { @@ -2413,12 +3507,14 @@ }, "resolve-from": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "resolved": + "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" }, "right-align": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "resolved": + "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "requires": { "align-text": "0.1.4" @@ -2426,18 +3522,22 @@ }, "safe-buffer": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "resolved": + "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": + "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "semver": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + "integrity": + "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, "send": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", + "integrity": + "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", "requires": { "debug": "2.6.9", "depd": "1.1.1", @@ -2456,21 +3556,25 @@ "dependencies": { "statuses": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "resolved": + "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" } } }, "sequencify": { "version": "0.0.7", - "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", + "resolved": + "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", "dev": true }, "serve-static": { "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", + "resolved": + "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", + "integrity": + "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", "requires": { "encodeurl": "1.0.1", "escape-html": "1.0.3", @@ -2480,7 +3584,8 @@ }, "setprototypeof": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "resolved": + "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" }, "sigmund": { @@ -2508,12 +3613,14 @@ }, "socket.io-adapter": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "resolved": + "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" }, "socket.io-client": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", + "resolved": + "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", "requires": { "backo2": "1.0.2", @@ -2533,7 +3640,8 @@ }, "socket.io-parser": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz", + "resolved": + "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz", "integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=", "requires": { "component-emitter": "1.2.1", @@ -2551,7 +3659,8 @@ }, "source-map": { "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": + "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "requires": { "amdefine": "1.0.1" @@ -2565,31 +3674,82 @@ }, "spectre.css": { "version": "0.4.6", - "resolved": "https://registry.npmjs.org/spectre.css/-/spectre.css-0.4.6.tgz", + "resolved": + "https://registry.npmjs.org/spectre.css/-/spectre.css-0.4.6.tgz", "integrity": "sha1-HfmK0Qk/rpDebHSHlmDVnNAphU8=" }, + "sprintf-js": { + "version": "1.0.3", + "resolved": + "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "state-toggle": { + "version": "1.0.0", + "resolved": + "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.0.tgz", + "integrity": "sha1-0g+aYWu08MO5i5GSLSW2QKorxCU=", + "dev": true + }, "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + "integrity": + "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, "stream-consume": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", + "resolved": + "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.0.tgz", "integrity": "sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8=", "dev": true }, + "string-width": { + "version": "2.1.1", + "resolved": + "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": + "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": + "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": + "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, "string_decoder": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "resolved": + "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": + "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "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", + "resolved": + "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -2608,7 +3768,8 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": + "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, @@ -2633,7 +3794,8 @@ }, "time-stamp": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "resolved": + "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, @@ -2644,9 +3806,29 @@ }, "token-stream": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", + "resolved": + "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", + "dev": true + }, + "trim-trailing-lines": { + "version": "1.1.0", + "resolved": + "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz", + "integrity": "sha1-eu+7eAjfnWafbaLkOMrIxGradoQ=", + "dev": true + }, + "trough": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.1.tgz", + "integrity": "sha1-qf2LA5Swro//guBjOgo2zK1bX4Y=", + "dev": true + }, "type-is": { "version": "1.6.15", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", @@ -2656,6 +3838,23 @@ "mime-types": "2.1.17" } }, + "typescript": { + "version": "2.5.3", + "resolved": + "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz", + "integrity": + "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==", + "dev": true + }, + "typescript-eslint-parser": { + "version": + "git://github.com/eslint/typescript-eslint-parser.git#9c71a627da36e97da52ed2731d58509c952b67ae", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.4.1" + } + }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", @@ -2668,34 +3867,112 @@ "dependencies": { "source-map": { "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "resolved": + "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, "uglify-to-browserify": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + "integrity": + "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" }, "unc-path-regex": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "resolved": + "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, + "unherit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.0.tgz", + "integrity": "sha1-a5qu379z3xdWrZ4xbdmBiFhAzX0=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "xtend": "4.0.1" + } + }, + "unicode-regex": { + "version": "1.0.1", + "resolved": + "https://registry.npmjs.org/unicode-regex/-/unicode-regex-1.0.1.tgz", + "integrity": "sha1-+BngUBkdW5VhozmljdO5CV7ZSzU=", + "dev": true + }, + "unified": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-6.1.5.tgz", + "integrity": "sha1-cWk3hyYhpjE15iztLzrGoGPG+4c=", + "dev": true, + "requires": { + "bail": "1.0.2", + "extend": "3.0.1", + "is-plain-obj": "1.1.0", + "trough": "1.0.1", + "vfile": "2.3.0", + "x-is-function": "1.0.4", + "x-is-string": "0.1.0" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, "unique-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", + "resolved": + "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", "dev": true }, + "unist-util-is": { + "version": "2.1.1", + "resolved": + "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.1.tgz", + "integrity": "sha1-DDEmKeP5YMZukx6BLT2A53AQlHs=", + "dev": true + }, + "unist-util-remove-position": { + "version": "1.1.1", + "resolved": + "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz", + "integrity": "sha1-WoXBVV/BugwQG4ZwfRXlD6TIcbs=", + "dev": true, + "requires": { + "unist-util-visit": "1.3.0" + } + }, + "unist-util-stringify-position": { + "version": "1.1.1", + "resolved": + "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.1.tgz", + "integrity": "sha1-PMvcU2ee7W7PN3fdf14yKcG2qjw=", + "dev": true + }, + "unist-util-visit": { + "version": "1.3.0", + "resolved": + "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.3.0.tgz", + "integrity": + "sha512-9ntYcxPFtl44gnwXrQKZ5bMqXMY0ZHzUpqMFiU4zcc8mmf/jzYm8GhYgezuUlX4cJIM1zIDYaO6fG/fI+L6iiQ==", + "dev": true, + "requires": { + "unist-util-is": "2.1.1" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -2709,12 +3986,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": + "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "resolved": + "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uws": { @@ -2737,6 +4016,46 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "vfile": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", + "integrity": + "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "dev": true, + "requires": { + "is-buffer": "1.1.6", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "1.1.1", + "vfile-message": "1.0.0" + }, + "dependencies": { + "replace-ext": { + "version": "1.0.0", + "resolved": + "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + } + } + }, + "vfile-location": { + "version": "2.0.2", + "resolved": + "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.2.tgz", + "integrity": "sha1-02dcWch3SY5JK0dW/2Xkrxp1IlU=", + "dev": true + }, + "vfile-message": { + "version": "1.0.0", + "resolved": + "https://registry.npmjs.org/vfile-message/-/vfile-message-1.0.0.tgz", + "integrity": + "sha512-HPREhzTOB/sNDc9/Mxf8w0FmHnThg5CRSJdR9VRFkD2riqYWs+fuXlj5z8mIpv2LrD7uU41+oPWFOL4Mjlf+dw==", + "dev": true, + "requires": { + "unist-util-stringify-position": "1.1.1" + } + }, "vinyl": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", @@ -2778,7 +4097,8 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": + "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -2790,13 +4110,15 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": + "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, "through2": { "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "resolved": + "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { @@ -2818,7 +4140,8 @@ }, "vinyl-sourcemaps-apply": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "resolved": + "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "dev": true, "requires": { @@ -2827,7 +4150,8 @@ "dependencies": { "source-map": { "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "resolved": + "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } @@ -2835,13 +4159,15 @@ }, "void-elements": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "resolved": + "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "integrity": + "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { "isexe": "2.0.0" @@ -2849,7 +4175,8 @@ }, "window-size": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "resolved": + "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" }, "with": { @@ -2875,16 +4202,32 @@ "ws": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.2.tgz", - "integrity": "sha512-t+WGpsNxhMR4v6EClXS8r8km5ZljKJzyGhJf7goJz9k5Ye3+b5Bvno5rjqPuIBn5mnn5GBb7o8IrIWHxX1qOLQ==", + "integrity": + "sha512-t+WGpsNxhMR4v6EClXS8r8km5ZljKJzyGhJf7goJz9k5Ye3+b5Bvno5rjqPuIBn5mnn5GBb7o8IrIWHxX1qOLQ==", "requires": { "async-limiter": "1.0.0", "safe-buffer": "5.1.1", "ultron": "1.1.1" } }, + "x-is-function": { + "version": "1.0.4", + "resolved": + "https://registry.npmjs.org/x-is-function/-/x-is-function-1.0.4.tgz", + "integrity": "sha1-XSlNw9Joy90GJYDgxd93o5HR+h4=", + "dev": true + }, + "x-is-string": { + "version": "0.1.0", + "resolved": + "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", + "dev": true + }, "xmlhttprequest-ssl": { "version": "1.5.4", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.4.tgz", + "resolved": + "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.4.tgz", "integrity": "sha1-BPVgkVcks4kIhxXMDteBPpZ3v1c=" }, "xtend": { diff --git a/package.json b/package.json index 11404cb..a3992ba 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "multicast", - "version": "1.1.0", + "version": "2.0.0", "description": "Service for persisting a session indefinitely across multiple Chromecast devices.", "main": "index.js", "scripts": { "prepublishOnly": "gulp", "start": "node .", - "test": "echo \"No tests configured\"" + "test": "echo \"No tests configured\" && exit 0" }, "bin": { "multicast": "index.js" @@ -32,6 +32,7 @@ "bluebird": "^3.5.0", "body-parser": "^1.18.2", "castv2": "^0.1.9", + "cors": "^2.8.4", "express": "^4.16.1", "font-awesome": "^4.7.0", "jquery": "^3.2.1",